Add help page

This commit is contained in:
2024-11-03 10:11:25 +11:00
parent 9f61ff1dab
commit e8a9bd1aa5
11 changed files with 264 additions and 182 deletions
BIN
View File
Binary file not shown.
+4
View File
@@ -0,0 +1,4 @@
import sys
from django import urls
sys.modules['django.core.urlresolvers'] = urls
+141
View File
@@ -0,0 +1,141 @@
{% extends 'base.html' %}
{% load i18n %}
{% load markdown_extras %}
{% block extra_css %}
<link href="https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/2.2.19/tailwind.min.css" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/tailwindcss-typography/0.4.0/typography.min.css" rel="stylesheet">
<style>
.prose {
color: #374151;
max-width: none !important;
}
.prose p {
margin-top: 1.25em;
margin-bottom: 1.25em;
}
.prose a {
color: #2563eb;
text-decoration: underline;
}
.prose strong {
font-weight: 600;
}
.prose ul {
margin-top: 1.25em;
margin-bottom: 1.25em;
list-style-type: disc;
padding-left: 1.625em;
}
.prose ol {
margin-top: 1.25em;
margin-bottom: 1.25em;
list-style-type: decimal;
padding-left: 1.625em;
}
.prose h1 {
font-size: 2.25em;
margin-top: 0;
margin-bottom: 0.8888889em;
line-height: 1.1111111;
}
.prose h2 {
font-size: 1.5em;
margin-top: 2em;
margin-bottom: 1em;
line-height: 1.3333333;
}
.prose h3 {
font-size: 1.25em;
margin-top: 1.6em;
margin-bottom: 0.6em;
line-height: 1.6;
}
.prose img {
margin-top: 2em;
margin-bottom: 2em;
}
.prose code {
color: #111827;
font-weight: 600;
background-color: #f3f4f6;
padding: 0.2em 0.4em;
border-radius: 0.375rem;
}
.prose pre {
color: #e5e7eb;
background-color: #1f2937;
overflow-x: auto;
font-size: 0.875em;
line-height: 1.7142857;
margin-top: 1.7142857em;
margin-bottom: 1.7142857em;
border-radius: 0.375rem;
padding-top: 0.8571429em;
padding-right: 1.1428571em;
padding-bottom: 0.8571429em;
padding-left: 1.1428571em;
}
.prose pre code {
background-color: transparent;
padding: 0;
color: inherit;
font-weight: inherit;
}
</style>
{% endblock %}
{% block content %}
<div class="max-w-7xl mx-auto mt-10 p-4 sm:px-6 bg-white shadow-md rounded-lg">
<div class="prose prose-sm sm:prose lg:prose-lg xl:prose-xl w-full">
{% filter markdown %}
# Frequently Asked Questions (FAQ)
## Link Types
GoLinks supports three types of links: Normal Links, Template Links, and Custom Pages. Each type serves different purposes and has its own use cases.
### 1. Normal Links
Normal links are the simplest type - they provide direct redirection to a target URL.
**Example:**
- Original URL: `https://github.com/microsoft/vscode`
- Alias: `vscode`
- Access: `http://go/vscode` → Redirects to VS Code GitHub repository
**Use Cases:**
- Quick access to frequently visited websites
- Shortening long URLs
- Creating memorable aliases for complex URLs
### 2. Template Links
Template links allow you to create dynamic URLs that can generate the final URL based on parameters. This is particularly useful for search links, API calls, or any URL that needs dynamic parameters.
**Basic Syntax:**
Use the syntax `{parameter_name, default=default_value}` to define variables in your URL: `https://google.com?q={query, default=hello}`, and set the alias to `google`.
**Examples:**
- Use the syntax `{query, default=default_value}` to define variables in your URL: `https://google.com?q={query, default=hello}`, and set the alias to `google`.
Then you can access the link by `http://go/google/<query>`, and the URL will be `https://google.com?q=<query>`.
When you access the url with `http://go/google/`, the URL will be `https://google.com?q=hello`, which will leverage the `default` value.
### 3. Custom Pages
Custom pages allow you to create markdown-based web pages that can serve as documentation, link collections, or any other content you need.
**Key Features:**
- Write content in Markdown format
- Support for headings, lists, code blocks, and links
- Can include multiple sections and navigation
- Perfect for creating documentation or resource collections
**Example Use Cases:**
Check http://go.junv.cc/custom/today/
{% endfilter %}
</div>
</div>
{% endblock %}
+1 -1
View File
@@ -23,7 +23,7 @@
{% endif %}
</h1>
<div class="flex flex-col sm:flex-row space-y-2 sm:space-y-0 sm:space-x-2">
<a href="{{ link.original_url }}" target="_blank" class="bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded text-sm sm:text-base inline-flex items-center justify-center">
<a href="{% url 'redirect_to_original' link.alias %}" target="_blank" class="bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded text-sm sm:text-base inline-flex items-center justify-center">
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"></path>
</svg>
+5 -1
View File
@@ -119,7 +119,11 @@
</td>
<td class="px-3 py-2 sm:px-6 sm:py-4 whitespace-nowrap hidden md:table-cell">
<div class="truncate max-w-xs" title="{{ link.original_url }}">
<a href="{{ link.original_url }}" target="_blank" class="text-blue-600 hover:text-blue-900">{{ link.original_url }}</a>
<a href="{% url 'redirect_to_original' link.alias %}"
class="text-blue-600 hover:text-blue-800 break-all"
target="_blank" rel="noopener noreferrer">
{{ link.original_url }}
</a>
</div>
</td>
<td class="px-3 py-2 sm:px-6 sm:py-4 whitespace-nowrap hidden sm:table-cell">{{ link.click_count }}</td>
+8
View File
@@ -0,0 +1,8 @@
from django import template
import markdown as md
register = template.Library()
@register.filter
def markdown(value):
return md.markdown(value, extensions=['fenced_code', 'tables'])
+6 -3
View File
@@ -8,14 +8,17 @@ urlpatterns = [
path('delete/<int:pk>/', views.LinkDeleteView.as_view(), name='link_delete'),
path('delete-selected/', views.delete_selected, name='delete_selected'),
path('detail/<int:pk>/', views.LinkDetailView.as_view(), name='link_detail'),
path('search/', views.SearchView.as_view(), name='search'),
path('ui/search/', views.SearchView.as_view(), name='search'),
path('link/<int:pk>/', views.LinkDetailView.as_view(), name='link_detail'),
path('link/<int:pk>/edit/', views.LinkUpdateView.as_view(), name='link_update'),
path('tools/', views.ToolsView.as_view(), name='tools'),
path('ui/tools/', views.ToolsView.as_view(), name='tools'),
path('tools/export-database/', views.export_database, name='export_database'),
path('ui/help/', views.HelpView.as_view(), name='help'),
path('export/', views.export_links, name='export_links'),
# Aliases
path('<str:alias>/', views.redirect_to_original, name='redirect_to_original'),
path('<str:alias>/<str:param>/', views.redirect_to_original, name='redirect_to_original_with_param'),
path('export/', views.export_links, name='export_links'),
path('alias/<str:alias>/', views.LinkDetailView.as_view(), name='link_detail_by_alias'),
]
+3
View File
@@ -429,3 +429,6 @@ def export_database(request):
logger.error(f"Database export failed: {str(e)}")
messages.error(request, _("Failed to export database: {}").format(str(e)))
return redirect('tools')
class HelpView(TemplateView):
template_name = 'links/help.html'
+93 -177
View File
@@ -670,6 +670,14 @@ video {
margin-right: auto;
}
.-ml-px {
margin-left: -1px;
}
.mb-1 {
margin-bottom: 0.25rem;
}
.mb-2 {
margin-bottom: 0.5rem;
}
@@ -694,10 +702,6 @@ video {
margin-left: 0.5rem;
}
.ml-3 {
margin-left: 0.75rem;
}
.ml-4 {
margin-left: 1rem;
}
@@ -746,14 +750,6 @@ video {
margin-top: 2rem;
}
.mb-1 {
margin-bottom: 0.25rem;
}
.-ml-px {
margin-left: -1px;
}
.block {
display: block;
}
@@ -782,6 +778,14 @@ video {
display: none;
}
.h-12 {
height: 3rem;
}
.h-16 {
height: 4rem;
}
.h-4 {
height: 1rem;
}
@@ -794,12 +798,12 @@ video {
height: 100vh;
}
.h-12 {
height: 3rem;
.w-12 {
width: 3rem;
}
.h-16 {
height: 4rem;
.w-16 {
width: 4rem;
}
.w-4 {
@@ -818,22 +822,14 @@ video {
width: 100%;
}
.w-12 {
width: 3rem;
}
.w-16 {
width: 4rem;
.min-w-0 {
min-width: 0px;
}
.min-w-full {
min-width: 100%;
}
.min-w-0 {
min-width: 0px;
}
.max-w-4xl {
max-width: 56rem;
}
@@ -850,6 +846,10 @@ video {
max-width: 32rem;
}
.max-w-md {
max-width: 28rem;
}
.max-w-none {
max-width: none;
}
@@ -862,14 +862,6 @@ video {
max-width: 20rem;
}
.max-w-3xl {
max-width: 48rem;
}
.max-w-md {
max-width: 28rem;
}
.flex-1 {
flex: 1 1 0%;
}
@@ -920,10 +912,6 @@ video {
align-items: flex-start;
}
.items-end {
align-items: flex-end;
}
.items-center {
align-items: center;
}
@@ -953,6 +941,12 @@ video {
row-gap: 2rem;
}
.space-x-2 > :not([hidden]) ~ :not([hidden]) {
--tw-space-x-reverse: 0;
margin-right: calc(0.5rem * var(--tw-space-x-reverse));
margin-left: calc(0.5rem * calc(1 - var(--tw-space-x-reverse)));
}
.space-x-3 > :not([hidden]) ~ :not([hidden]) {
--tw-space-x-reverse: 0;
margin-right: calc(0.75rem * var(--tw-space-x-reverse));
@@ -995,12 +989,6 @@ video {
margin-bottom: calc(2rem * var(--tw-space-y-reverse));
}
.space-x-2 > :not([hidden]) ~ :not([hidden]) {
--tw-space-x-reverse: 0;
margin-right: calc(0.5rem * var(--tw-space-x-reverse));
margin-left: calc(0.5rem * calc(1 - var(--tw-space-x-reverse)));
}
.divide-y > :not([hidden]) ~ :not([hidden]) {
--tw-divide-y-reverse: 0;
border-top-width: calc(1px * calc(1 - var(--tw-divide-y-reverse)));
@@ -1030,6 +1018,10 @@ video {
white-space: nowrap;
}
.break-all {
word-break: break-all;
}
.rounded {
border-radius: 0.25rem;
}
@@ -1165,11 +1157,21 @@ video {
background-color: rgb(34 197 94 / var(--tw-bg-opacity));
}
.bg-purple-100 {
--tw-bg-opacity: 1;
background-color: rgb(243 232 255 / var(--tw-bg-opacity));
}
.bg-purple-50 {
--tw-bg-opacity: 1;
background-color: rgb(250 245 255 / var(--tw-bg-opacity));
}
.bg-purple-500 {
--tw-bg-opacity: 1;
background-color: rgb(168 85 247 / var(--tw-bg-opacity));
}
.bg-red-500 {
--tw-bg-opacity: 1;
background-color: rgb(239 68 68 / var(--tw-bg-opacity));
@@ -1194,30 +1196,14 @@ video {
background-color: rgb(254 249 195 / var(--tw-bg-opacity));
}
.bg-purple-500 {
--tw-bg-opacity: 1;
background-color: rgb(168 85 247 / var(--tw-bg-opacity));
}
.bg-green-600 {
--tw-bg-opacity: 1;
background-color: rgb(22 163 74 / var(--tw-bg-opacity));
}
.bg-purple-600 {
--tw-bg-opacity: 1;
background-color: rgb(147 51 234 / var(--tw-bg-opacity));
}
.bg-purple-100 {
--tw-bg-opacity: 1;
background-color: rgb(243 232 255 / var(--tw-bg-opacity));
}
.p-2 {
padding: 0.5rem;
}
.p-3 {
padding: 0.75rem;
}
.p-4 {
padding: 1rem;
}
@@ -1226,10 +1212,6 @@ video {
padding: 1.5rem;
}
.p-3 {
padding: 0.75rem;
}
.px-2 {
padding-left: 0.5rem;
padding-right: 0.5rem;
@@ -1265,6 +1247,11 @@ video {
padding-bottom: 0.25rem;
}
.py-12 {
padding-top: 3rem;
padding-bottom: 3rem;
}
.py-2 {
padding-top: 0.5rem;
padding-bottom: 0.5rem;
@@ -1290,23 +1277,10 @@ video {
padding-bottom: 2rem;
}
.py-12 {
padding-top: 3rem;
padding-bottom: 3rem;
}
.pr-3 {
padding-right: 0.75rem;
}
.pl-5 {
padding-left: 1.25rem;
}
.pr-10 {
padding-right: 2.5rem;
}
.text-left {
text-align: left;
}
@@ -1334,6 +1308,11 @@ video {
line-height: 1;
}
.text-base {
font-size: 1rem;
line-height: 1.5rem;
}
.text-lg {
font-size: 1.125rem;
line-height: 1.75rem;
@@ -1354,11 +1333,6 @@ video {
line-height: 1rem;
}
.text-base {
font-size: 1rem;
line-height: 1.5rem;
}
.font-bold {
font-weight: 700;
}
@@ -1411,6 +1385,11 @@ video {
color: rgb(30 64 175 / var(--tw-text-opacity));
}
.text-gray-300 {
--tw-text-opacity: 1;
color: rgb(209 213 219 / var(--tw-text-opacity));
}
.text-gray-400 {
--tw-text-opacity: 1;
color: rgb(156 163 175 / var(--tw-text-opacity));
@@ -1466,11 +1445,26 @@ video {
color: rgb(79 70 229 / var(--tw-text-opacity));
}
.text-purple-500 {
--tw-text-opacity: 1;
color: rgb(168 85 247 / var(--tw-text-opacity));
}
.text-purple-600 {
--tw-text-opacity: 1;
color: rgb(147 51 234 / var(--tw-text-opacity));
}
.text-purple-700 {
--tw-text-opacity: 1;
color: rgb(126 34 206 / var(--tw-text-opacity));
}
.text-purple-800 {
--tw-text-opacity: 1;
color: rgb(107 33 168 / var(--tw-text-opacity));
}
.text-red-600 {
--tw-text-opacity: 1;
color: rgb(220 38 38 / var(--tw-text-opacity));
@@ -1491,26 +1485,6 @@ video {
color: rgb(161 98 7 / var(--tw-text-opacity));
}
.text-gray-300 {
--tw-text-opacity: 1;
color: rgb(209 213 219 / var(--tw-text-opacity));
}
.text-purple-500 {
--tw-text-opacity: 1;
color: rgb(168 85 247 / var(--tw-text-opacity));
}
.text-purple-600 {
--tw-text-opacity: 1;
color: rgb(147 51 234 / var(--tw-text-opacity));
}
.text-purple-800 {
--tw-text-opacity: 1;
color: rgb(107 33 168 / var(--tw-text-opacity));
}
.underline {
text-decoration-line: underline;
}
@@ -1519,12 +1493,6 @@ video {
text-decoration-line: line-through;
}
.shadow {
--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
}
.shadow-md {
--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);
@@ -1543,20 +1511,6 @@ video {
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
}
.ring-1 {
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
}
.ring-inset {
--tw-ring-inset: inset;
}
.ring-blue-700\/10 {
--tw-ring-color: rgb(29 78 216 / 0.1);
}
.blur {
--tw-blur: blur(8px);
filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
@@ -1678,6 +1632,11 @@ video {
background-color: rgb(21 128 61 / var(--tw-bg-opacity));
}
.hover\:bg-purple-600:hover {
--tw-bg-opacity: 1;
background-color: rgb(147 51 234 / var(--tw-bg-opacity));
}
.hover\:bg-red-600:hover {
--tw-bg-opacity: 1;
background-color: rgb(220 38 38 / var(--tw-bg-opacity));
@@ -1688,16 +1647,6 @@ video {
background-color: rgb(185 28 28 / var(--tw-bg-opacity));
}
.hover\:bg-purple-600:hover {
--tw-bg-opacity: 1;
background-color: rgb(147 51 234 / var(--tw-bg-opacity));
}
.hover\:bg-purple-700:hover {
--tw-bg-opacity: 1;
background-color: rgb(126 34 206 / var(--tw-bg-opacity));
}
.hover\:text-blue-700:hover {
--tw-text-opacity: 1;
color: rgb(29 78 216 / var(--tw-text-opacity));
@@ -1782,18 +1731,18 @@ video {
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
}
.focus\:ring-2:focus {
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
}
.focus\:ring-1:focus {
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
}
.focus\:ring-2:focus {
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
}
.focus\:ring-blue-200:focus {
--tw-ring-opacity: 1;
--tw-ring-color: rgb(191 219 254 / var(--tw-ring-opacity));
@@ -1819,16 +1768,6 @@ video {
--tw-ring-color: rgb(239 68 68 / var(--tw-ring-opacity));
}
.focus\:ring-green-500:focus {
--tw-ring-opacity: 1;
--tw-ring-color: rgb(34 197 94 / var(--tw-ring-opacity));
}
.focus\:ring-purple-500:focus {
--tw-ring-opacity: 1;
--tw-ring-color: rgb(168 85 247 / var(--tw-ring-opacity));
}
.focus\:ring-opacity-50:focus {
--tw-ring-opacity: 0.5;
}
@@ -1846,10 +1785,6 @@ video {
grid-column: span 2 / span 2;
}
.sm\:mt-0 {
margin-top: 0px;
}
.sm\:block {
display: block;
}
@@ -1900,30 +1835,20 @@ video {
margin-left: calc(0.5rem * calc(1 - var(--tw-space-x-reverse)));
}
.sm\:space-x-4 > :not([hidden]) ~ :not([hidden]) {
--tw-space-x-reverse: 0;
margin-right: calc(1rem * var(--tw-space-x-reverse));
margin-left: calc(1rem * calc(1 - var(--tw-space-x-reverse)));
}
.sm\:space-y-0 > :not([hidden]) ~ :not([hidden]) {
--tw-space-y-reverse: 0;
margin-top: calc(0px * calc(1 - var(--tw-space-y-reverse)));
margin-bottom: calc(0px * var(--tw-space-y-reverse));
}
.sm\:rounded-md {
border-radius: 0.375rem;
.sm\:p-12 {
padding: 3rem;
}
.sm\:p-6 {
padding: 1.5rem;
}
.sm\:p-12 {
padding: 3rem;
}
.sm\:px-6 {
padding-left: 1.5rem;
padding-right: 1.5rem;
@@ -1943,11 +1868,6 @@ video {
font-size: 1rem;
line-height: 1.5rem;
}
.sm\:text-sm {
font-size: 0.875rem;
line-height: 1.25rem;
}
}
@media (min-width: 768px) {
@@ -1965,10 +1885,6 @@ video {
grid-template-columns: repeat(5, minmax(0, 1fr));
}
.lg\:p-8 {
padding: 2rem;
}
.lg\:px-8 {
padding-left: 2rem;
padding-right: 2rem;
+1
View File
@@ -70,6 +70,7 @@
<div id="more-menu-dropdown" class="absolute right-0 mt-2 py-2 w-48 bg-white rounded-md shadow-xl z-20 hidden">
<a href="{% url 'search' %}" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">{% trans "Advanced Search" %}</a>
<a href="{% url 'tools' %}" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">{% trans "Tools" %}</a>
<a href="{% url 'help' %}" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">{% trans "Help" %}</a>
</div>
</div>
<!-- 语言选择器 -->
+2
View File
@@ -1,6 +1,7 @@
import os
from pathlib import Path
from django.utils.translation import gettext_lazy as _
import links.patches # 添加这一行在文件最上方
# 构建路径,如 BASE_DIR / 'subdir'
BASE_DIR = Path(__file__).resolve().parent.parent
@@ -17,6 +18,7 @@ INSTALLED_APPS = [
'tailwind',
'new_theme',
'simplemde',
'markdown', # 只需要基本的markdown包
]
ROOT_URLCONF = 'url_manager.urls'