@jant/core 0.3.36 → 0.3.37
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/commands/export.js +1 -1
- package/bin/commands/import-site.js +529 -0
- package/bin/commands/reset-password.js +3 -2
- package/dist/client/assets/heic-to-DIRPI3VF.js +1 -0
- package/dist/client/assets/url-FWFqPJPb.js +1 -0
- package/dist/client/client.css +1 -1
- package/dist/client/client.js +4012 -3276
- package/dist/index.js +10285 -5809
- package/package.json +11 -3
- package/src/__tests__/helpers/app.ts +9 -9
- package/src/__tests__/helpers/db.ts +91 -93
- package/src/app.tsx +157 -27
- package/src/auth.ts +20 -2
- package/src/client/archive-nav.js +187 -0
- package/src/client/audio-player.ts +478 -0
- package/src/client/audio-processor.ts +84 -0
- package/src/client/avatar-upload.ts +3 -2
- package/src/client/components/__tests__/jant-compose-dialog.test.ts +645 -49
- package/src/client/components/__tests__/jant-compose-editor.test.ts +208 -16
- package/src/client/components/__tests__/jant-post-form.test.ts +19 -9
- package/src/client/components/__tests__/jant-settings-avatar.test.ts +2 -2
- package/src/client/components/__tests__/jant-settings-general.test.ts +3 -3
- package/src/client/components/collection-sidebar-types.ts +7 -9
- package/src/client/components/compose-types.ts +101 -4
- package/src/client/components/jant-collection-form.ts +43 -7
- package/src/client/components/jant-collection-sidebar.ts +88 -84
- package/src/client/components/jant-compose-dialog.ts +1655 -219
- package/src/client/components/jant-compose-editor.ts +732 -168
- package/src/client/components/jant-compose-fullscreen.ts +23 -78
- package/src/client/components/jant-media-lightbox.ts +2 -0
- package/src/client/components/jant-nav-manager.ts +24 -284
- package/src/client/components/jant-post-form.ts +89 -9
- package/src/client/components/jant-post-menu.ts +1019 -0
- package/src/client/components/jant-settings-avatar.ts +3 -3
- package/src/client/components/jant-settings-general.ts +38 -4
- package/src/client/components/jant-text-preview.ts +232 -0
- package/src/client/components/nav-manager-types.ts +4 -19
- package/src/client/components/post-form-template.ts +107 -12
- package/src/client/components/post-form-types.ts +11 -4
- package/src/client/compose-bridge.ts +410 -109
- package/src/client/image-processor.ts +26 -8
- package/src/client/media-metadata.ts +247 -0
- package/src/client/multipart-upload.ts +160 -0
- package/src/client/post-form-bridge.ts +52 -1
- package/src/client/settings-bridge.ts +0 -12
- package/src/client/thread-context.ts +140 -0
- package/src/client/tiptap/create-editor.ts +46 -0
- package/src/client/tiptap/extensions.ts +5 -0
- package/src/client/tiptap/image-node.ts +2 -8
- package/src/client/tiptap/paste-image.ts +2 -13
- package/src/client/tiptap/slash-commands.ts +173 -63
- package/src/client/toast.ts +101 -3
- package/src/client/types/sortablejs.d.ts +15 -0
- package/src/client/upload-with-metadata.ts +54 -0
- package/src/client/video-processor.ts +207 -0
- package/src/client.ts +5 -2
- package/src/db/__tests__/migrations.test.ts +118 -0
- package/src/db/index.ts +52 -0
- package/src/db/migrations/0000_baseline.sql +269 -0
- package/src/db/migrations/0001_fts_setup.sql +31 -0
- package/src/db/migrations/meta/0000_snapshot.json +703 -119
- package/src/db/migrations/meta/0001_snapshot.json +1337 -0
- package/src/db/migrations/meta/_journal.json +4 -39
- package/src/db/schema.ts +409 -145
- package/src/i18n/__tests__/detect.test.ts +115 -0
- package/src/i18n/context.tsx +2 -2
- package/src/i18n/detect.ts +85 -1
- package/src/i18n/i18n.ts +1 -1
- package/src/i18n/index.ts +2 -1
- package/src/i18n/locales/en.po +487 -1217
- package/src/i18n/locales/en.ts +1 -1
- package/src/i18n/locales/zh-Hans.po +613 -996
- package/src/i18n/locales/zh-Hans.ts +1 -1
- package/src/i18n/locales/zh-Hant.po +624 -1007
- package/src/i18n/locales/zh-Hant.ts +1 -1
- package/src/i18n/middleware.ts +6 -0
- package/src/index.ts +5 -7
- package/src/lib/__tests__/blurhash-placeholder.test.ts +75 -0
- package/src/lib/__tests__/constants.test.ts +0 -1
- package/src/lib/__tests__/markdown-to-tiptap.test.ts +358 -0
- package/src/lib/__tests__/nanoid.test.ts +26 -0
- package/src/lib/__tests__/schemas.test.ts +181 -63
- package/src/lib/__tests__/slug.test.ts +126 -0
- package/src/lib/__tests__/sse.test.ts +6 -6
- package/src/lib/__tests__/summary.test.ts +264 -0
- package/src/lib/__tests__/theme.test.ts +1 -1
- package/src/lib/__tests__/timeline.test.ts +33 -30
- package/src/lib/__tests__/tiptap-to-markdown.test.ts +346 -0
- package/src/lib/__tests__/view.test.ts +141 -66
- package/src/lib/blurhash-placeholder.ts +102 -0
- package/src/lib/constants.ts +3 -1
- package/src/lib/emoji-catalog.ts +885 -68
- package/src/lib/errors.ts +11 -8
- package/src/lib/feed.ts +78 -32
- package/src/lib/html.ts +2 -1
- package/src/lib/icon-catalog.ts +5033 -1
- package/src/lib/icons.ts +3 -2
- package/src/lib/index.ts +0 -1
- package/src/lib/markdown-to-tiptap.ts +286 -0
- package/src/lib/media-helpers.ts +12 -3
- package/src/lib/nanoid.ts +29 -0
- package/src/lib/navigation.ts +1 -1
- package/src/lib/render.tsx +20 -2
- package/src/lib/resolve-config.ts +6 -2
- package/src/lib/schemas.ts +224 -55
- package/src/lib/search-snippet.ts +34 -0
- package/src/lib/slug.ts +96 -0
- package/src/lib/sse.ts +6 -6
- package/src/lib/storage.ts +115 -7
- package/src/lib/summary.ts +66 -0
- package/src/lib/theme.ts +11 -8
- package/src/lib/timeline.ts +74 -34
- package/src/lib/tiptap-render.ts +5 -10
- package/src/lib/tiptap-to-markdown.ts +305 -0
- package/src/lib/upload.ts +190 -29
- package/src/lib/url.ts +31 -0
- package/src/lib/view.ts +204 -37
- package/src/middleware/__tests__/auth.test.ts +191 -11
- package/src/middleware/__tests__/onboarding.test.ts +12 -10
- package/src/middleware/auth.ts +63 -9
- package/src/middleware/onboarding.ts +1 -1
- package/src/middleware/secure-headers.ts +40 -0
- package/src/preset.css +45 -2
- package/src/routes/__tests__/compose.test.ts +17 -24
- package/src/routes/api/__tests__/collections.test.ts +109 -61
- package/src/routes/api/__tests__/nav-items.test.ts +46 -29
- package/src/routes/api/__tests__/posts.test.ts +132 -68
- package/src/routes/api/__tests__/search.test.ts +15 -2
- package/src/routes/api/__tests__/upload-multipart.test.ts +534 -0
- package/src/routes/api/collections.ts +51 -42
- package/src/routes/api/custom-urls.ts +80 -0
- package/src/routes/api/export.ts +31 -0
- package/src/routes/api/nav-items.ts +23 -19
- package/src/routes/api/posts.ts +43 -39
- package/src/routes/api/search.ts +3 -4
- package/src/routes/api/upload-multipart.ts +245 -0
- package/src/routes/api/upload.ts +85 -19
- package/src/routes/auth/__tests__/setup.test.ts +20 -60
- package/src/routes/auth/setup.tsx +26 -33
- package/src/routes/auth/signin.tsx +3 -7
- package/src/routes/compose.tsx +10 -55
- package/src/routes/dash/__tests__/settings-avatar.test.ts +1 -1
- package/src/routes/dash/custom-urls.tsx +414 -0
- package/src/routes/dash/settings.tsx +304 -232
- package/src/routes/feed/__tests__/rss.test.ts +27 -28
- package/src/routes/feed/rss.ts +6 -4
- package/src/routes/feed/sitemap.ts +2 -12
- package/src/routes/pages/__tests__/collections.test.ts +5 -6
- package/src/routes/pages/__tests__/featured.test.ts +41 -22
- package/src/routes/pages/archive.tsx +175 -39
- package/src/routes/pages/collection.tsx +22 -10
- package/src/routes/pages/collections.tsx +3 -3
- package/src/routes/pages/featured.tsx +28 -4
- package/src/routes/pages/home.tsx +16 -15
- package/src/routes/pages/latest.tsx +1 -11
- package/src/routes/pages/new.tsx +39 -0
- package/src/routes/pages/page.tsx +95 -49
- package/src/routes/pages/search.tsx +1 -1
- package/src/services/__tests__/api-token.test.ts +135 -0
- package/src/services/__tests__/collection.test.ts +275 -227
- package/src/services/__tests__/custom-url.test.ts +213 -0
- package/src/services/__tests__/media.test.ts +162 -22
- package/src/services/__tests__/navigation.test.ts +109 -68
- package/src/services/__tests__/post-timeline.test.ts +205 -32
- package/src/services/__tests__/post.test.ts +713 -234
- package/src/services/__tests__/search.test.ts +67 -10
- package/src/services/api-token.ts +166 -0
- package/src/services/auth.ts +17 -2
- package/src/services/collection.ts +397 -131
- package/src/services/custom-url.ts +188 -0
- package/src/services/export.ts +802 -0
- package/src/services/index.ts +26 -19
- package/src/services/media.ts +100 -22
- package/src/services/navigation.ts +158 -47
- package/src/services/path.ts +339 -0
- package/src/services/post.ts +687 -154
- package/src/services/search.ts +160 -75
- package/src/styles/components.css +58 -7
- package/src/styles/tokens.css +84 -6
- package/src/styles/ui.css +2964 -457
- package/src/types/bindings.ts +4 -1
- package/src/types/config.ts +12 -4
- package/src/types/constants.ts +15 -3
- package/src/types/entities.ts +74 -35
- package/src/types/operations.ts +11 -24
- package/src/types/props.ts +51 -16
- package/src/types/views.ts +45 -22
- package/src/ui/color-themes.ts +133 -23
- package/src/ui/compose/ComposeDialog.tsx +239 -17
- package/src/ui/compose/ComposePrompt.tsx +1 -1
- package/src/ui/dash/CrudPageHeader.tsx +1 -1
- package/src/ui/dash/ListItemRow.tsx +1 -1
- package/src/ui/dash/StatusBadge.tsx +3 -1
- package/src/ui/dash/appearance/AdvancedContent.tsx +22 -1
- package/src/ui/dash/appearance/ColorThemeContent.tsx +22 -2
- package/src/ui/dash/appearance/FontThemeContent.tsx +1 -1
- package/src/ui/dash/appearance/NavigationContent.tsx +5 -45
- package/src/ui/dash/index.ts +0 -3
- package/src/ui/dash/settings/AccountContent.tsx +3 -57
- package/src/ui/dash/settings/AccountMenuContent.tsx +147 -0
- package/src/ui/dash/settings/ApiTokensContent.tsx +232 -0
- package/src/ui/dash/settings/AvatarContent.tsx +8 -0
- package/src/ui/dash/settings/SessionsContent.tsx +159 -0
- package/src/ui/dash/settings/SettingsRootContent.tsx +45 -15
- package/src/ui/feed/LinkCard.tsx +89 -40
- package/src/ui/feed/NoteCard.tsx +39 -25
- package/src/ui/feed/PostStatusBadges.tsx +67 -0
- package/src/ui/feed/QuoteCard.tsx +38 -23
- package/src/ui/feed/ThreadPreview.tsx +90 -26
- package/src/ui/feed/TimelineFeed.tsx +3 -2
- package/src/ui/feed/TimelineItem.tsx +15 -6
- package/src/ui/feed/__tests__/thread-preview.test.ts +107 -0
- package/src/ui/feed/thread-preview-state.ts +61 -0
- package/src/ui/font-themes.ts +2 -2
- package/src/ui/layouts/BaseLayout.tsx +2 -2
- package/src/ui/layouts/SiteLayout.tsx +105 -92
- package/src/ui/pages/ArchivePage.tsx +923 -98
- package/src/ui/pages/ComposePage.tsx +54 -0
- package/src/ui/pages/PostPage.tsx +30 -45
- package/src/ui/pages/SearchPage.tsx +181 -37
- package/src/ui/shared/AdminBreadcrumb.tsx +29 -0
- package/src/ui/shared/CollectionsSidebar.tsx +47 -37
- package/src/ui/shared/MediaGallery.tsx +445 -149
- package/src/ui/shared/PostFooter.tsx +204 -0
- package/src/ui/shared/StarRating.tsx +27 -0
- package/src/ui/shared/__tests__/format-chars.test.ts +35 -0
- package/src/ui/shared/index.ts +0 -1
- package/dist/client/assets/url-8Dj-5CLW.js +0 -1
- package/src/client/media-upload.ts +0 -161
- package/src/client/page-slug-bridge.ts +0 -42
- package/src/db/migrations/0000_square_wallflower.sql +0 -118
- package/src/db/migrations/0001_add_search_fts.sql +0 -34
- package/src/db/migrations/0002_add_media_attachments.sql +0 -3
- package/src/db/migrations/0003_add_navigation_links.sql +0 -8
- package/src/db/migrations/0004_add_storage_provider.sql +0 -3
- package/src/db/migrations/0005_v2_schema_migration.sql +0 -268
- package/src/db/migrations/0006_rename_slug_to_path.sql +0 -5
- package/src/db/migrations/0007_post_collections_m2m.sql +0 -94
- package/src/db/migrations/0008_add_collection_dividers.sql +0 -8
- package/src/db/migrations/0009_drop_collection_show_divider.sql +0 -2
- package/src/db/migrations/0010_add_performance_indexes.sql +0 -16
- package/src/db/migrations/0011_add_path_registry.sql +0 -23
- package/src/db/migrations/0012_add_tiptap_columns.sql +0 -2
- package/src/db/migrations/0013_replace_featured_with_visibility.sql +0 -8
- package/src/db/migrations/meta/0003_snapshot.json +0 -821
- package/src/lib/__tests__/sqid.test.ts +0 -65
- package/src/lib/sqid.ts +0 -79
- package/src/routes/api/__tests__/pages.test.ts +0 -218
- package/src/routes/api/pages.ts +0 -73
- package/src/routes/dash/__tests__/pages.test.ts +0 -226
- package/src/routes/dash/index.tsx +0 -109
- package/src/routes/dash/media.tsx +0 -135
- package/src/routes/dash/pages.tsx +0 -245
- package/src/routes/dash/posts.tsx +0 -338
- package/src/routes/dash/redirects.tsx +0 -263
- package/src/routes/pages/post.tsx +0 -59
- package/src/services/__tests__/page.test.ts +0 -298
- package/src/services/__tests__/path-registry.test.ts +0 -165
- package/src/services/__tests__/redirect.test.ts +0 -159
- package/src/services/page.ts +0 -216
- package/src/services/path-registry.ts +0 -160
- package/src/services/redirect.ts +0 -97
- package/src/ui/dash/PageForm.tsx +0 -187
- package/src/ui/dash/PostList.tsx +0 -95
- package/src/ui/dash/media/MediaListContent.tsx +0 -206
- package/src/ui/dash/media/ViewMediaContent.tsx +0 -208
- package/src/ui/dash/pages/PagesContent.tsx +0 -75
- package/src/ui/dash/posts/PostForm.tsx +0 -260
- package/src/ui/layouts/DashLayout.tsx +0 -247
- package/src/ui/pages/SinglePage.tsx +0 -23
- package/src/ui/shared/ThreadView.tsx +0 -136
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Client-side Media Upload Handler
|
|
3
|
-
*
|
|
4
|
-
* Handles file upload flow:
|
|
5
|
-
* 1. User selects file via [data-media-upload] input
|
|
6
|
-
* 2. Creates placeholder in grid with spinner
|
|
7
|
-
* 3. Processes image via ImageProcessor (resize/convert to WebP)
|
|
8
|
-
* 4. Sets processed file on hidden Datastar form via DataTransfer API
|
|
9
|
-
* 5. Triggers form.requestSubmit() — Datastar handles upload + SSE response
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import { ImageProcessor } from "./image-processor.js";
|
|
13
|
-
import { validateUploadFile } from "../lib/upload.js";
|
|
14
|
-
import { showToast } from "./toast.js";
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Format file size for display
|
|
18
|
-
*/
|
|
19
|
-
function formatFileSize(bytes: number): string {
|
|
20
|
-
if (bytes < 1024) return `${bytes} B`;
|
|
21
|
-
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
22
|
-
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Ensure the media grid exists, removing empty state if needed
|
|
27
|
-
*/
|
|
28
|
-
function ensureGridExists(): HTMLElement {
|
|
29
|
-
let grid = document.getElementById("media-grid");
|
|
30
|
-
if (grid) return grid;
|
|
31
|
-
|
|
32
|
-
document.getElementById("empty-state")?.remove();
|
|
33
|
-
|
|
34
|
-
grid = document.createElement("div");
|
|
35
|
-
grid.id = "media-grid";
|
|
36
|
-
grid.className =
|
|
37
|
-
"grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-4";
|
|
38
|
-
document.getElementById("media-content")?.appendChild(grid);
|
|
39
|
-
return grid;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Create a placeholder card with spinner in the media grid
|
|
44
|
-
*/
|
|
45
|
-
function createPlaceholder(
|
|
46
|
-
fileName: string,
|
|
47
|
-
fileSize: number,
|
|
48
|
-
statusText: string,
|
|
49
|
-
): HTMLElement {
|
|
50
|
-
const placeholder = document.createElement("div");
|
|
51
|
-
placeholder.id = "upload-placeholder";
|
|
52
|
-
placeholder.className = "group relative";
|
|
53
|
-
placeholder.innerHTML = `
|
|
54
|
-
<div class="aspect-square bg-muted rounded-lg overflow-hidden border flex items-center justify-center">
|
|
55
|
-
<div class="text-center px-2">
|
|
56
|
-
<svg class="animate-spin h-6 w-6 text-muted-foreground mx-auto mb-2" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
|
57
|
-
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
|
58
|
-
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
|
59
|
-
</svg>
|
|
60
|
-
<span id="upload-status" class="text-xs text-muted-foreground">${statusText}</span>
|
|
61
|
-
</div>
|
|
62
|
-
</div>
|
|
63
|
-
<div class="mt-2 text-xs truncate" title="${fileName}">${fileName}</div>
|
|
64
|
-
<div class="text-xs text-muted-foreground">${formatFileSize(fileSize)}</div>
|
|
65
|
-
`;
|
|
66
|
-
return placeholder;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Replace placeholder content with an error message
|
|
71
|
-
*/
|
|
72
|
-
function showPlaceholderError(
|
|
73
|
-
placeholder: HTMLElement,
|
|
74
|
-
fileName: string,
|
|
75
|
-
errorMessage: string,
|
|
76
|
-
): void {
|
|
77
|
-
placeholder.innerHTML = `
|
|
78
|
-
<div class="aspect-square bg-destructive/10 rounded-lg overflow-hidden border border-destructive flex items-center justify-center">
|
|
79
|
-
<div class="text-center px-2">
|
|
80
|
-
<span class="text-xs text-destructive">${errorMessage}</span>
|
|
81
|
-
</div>
|
|
82
|
-
</div>
|
|
83
|
-
<div class="mt-2 text-xs truncate text-destructive">${fileName}</div>
|
|
84
|
-
<button type="button" class="text-xs text-muted-foreground hover:underline" onclick="this.closest('.group').remove()">Remove</button>
|
|
85
|
-
`;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Handle the upload flow for a selected file
|
|
90
|
-
*/
|
|
91
|
-
async function handleUpload(
|
|
92
|
-
input: HTMLInputElement,
|
|
93
|
-
file: File,
|
|
94
|
-
): Promise<void> {
|
|
95
|
-
const maxFileSizeMB = parseInt(input.dataset.maxFileSize || "500", 10) || 500;
|
|
96
|
-
const processingText = input.dataset.textProcessing || "Processing...";
|
|
97
|
-
const uploadingText = input.dataset.textUploading || "Uploading...";
|
|
98
|
-
const errorText =
|
|
99
|
-
input.dataset.textError || "Upload failed. Please try again.";
|
|
100
|
-
|
|
101
|
-
// Validate before creating placeholder — reject immediately with toast
|
|
102
|
-
const validationError = validateUploadFile(file, { maxFileSizeMB });
|
|
103
|
-
if (validationError) {
|
|
104
|
-
showToast(validationError, "error");
|
|
105
|
-
input.value = "";
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
const grid = ensureGridExists();
|
|
110
|
-
const placeholder = createPlaceholder(file.name, file.size, processingText);
|
|
111
|
-
grid.prepend(placeholder);
|
|
112
|
-
|
|
113
|
-
try {
|
|
114
|
-
// Process images client-side (resize, convert to WebP); upload non-images as-is
|
|
115
|
-
const toUpload = file.type.startsWith("image/")
|
|
116
|
-
? await ImageProcessor.processToFile(file)
|
|
117
|
-
: file;
|
|
118
|
-
|
|
119
|
-
// Update status
|
|
120
|
-
const statusEl = document.getElementById("upload-status");
|
|
121
|
-
if (statusEl) statusEl.textContent = uploadingText;
|
|
122
|
-
|
|
123
|
-
// Set processed file on hidden form input via DataTransfer API
|
|
124
|
-
const formInput = document.getElementById(
|
|
125
|
-
"upload-file-input",
|
|
126
|
-
) as HTMLInputElement | null;
|
|
127
|
-
const form = document.getElementById(
|
|
128
|
-
"upload-form",
|
|
129
|
-
) as HTMLFormElement | null;
|
|
130
|
-
if (!formInput || !form) throw new Error("Upload form not found");
|
|
131
|
-
|
|
132
|
-
const dt = new DataTransfer();
|
|
133
|
-
dt.items.add(toUpload);
|
|
134
|
-
formInput.files = dt.files;
|
|
135
|
-
|
|
136
|
-
// Trigger Datastar-intercepted form submission
|
|
137
|
-
form.requestSubmit();
|
|
138
|
-
} catch (err) {
|
|
139
|
-
const message = err instanceof Error ? err.message : errorText;
|
|
140
|
-
showPlaceholderError(placeholder, file.name, message);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// Reset file input so the same file can be re-selected
|
|
144
|
-
input.value = "";
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* Initialize media upload via event delegation
|
|
149
|
-
*/
|
|
150
|
-
function initMediaUpload(): void {
|
|
151
|
-
document.addEventListener("change", (e) => {
|
|
152
|
-
const input = (e.target as HTMLElement).closest(
|
|
153
|
-
"[data-media-upload]",
|
|
154
|
-
) as HTMLInputElement | null;
|
|
155
|
-
if (!input?.files?.[0]) return;
|
|
156
|
-
|
|
157
|
-
handleUpload(input, input.files[0]);
|
|
158
|
-
});
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
initMediaUpload();
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Page Slug Bridge
|
|
3
|
-
*
|
|
4
|
-
* Auto-generates a slug from the page title in create mode.
|
|
5
|
-
* Listens for `input` events on the title field inside `[data-page-form]`
|
|
6
|
-
* and writes the slugified value into the slug input, dispatching an
|
|
7
|
-
* `input` event so Datastar picks up the signal change.
|
|
8
|
-
*
|
|
9
|
-
* Skipped in edit mode (detected via `data-page-edit` attribute).
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import { preloadSlug, slugify } from "./lazy-slugify.js";
|
|
13
|
-
|
|
14
|
-
function init() {
|
|
15
|
-
const form = document.querySelector<HTMLFormElement>("[data-page-form]");
|
|
16
|
-
if (!form || form.hasAttribute("data-page-edit")) return;
|
|
17
|
-
|
|
18
|
-
preloadSlug();
|
|
19
|
-
|
|
20
|
-
const titleInput = form.querySelector<HTMLInputElement>(
|
|
21
|
-
'[data-bind="title"]',
|
|
22
|
-
);
|
|
23
|
-
const slugInput = form.querySelector<HTMLInputElement>('[data-bind="slug"]');
|
|
24
|
-
if (!titleInput || !slugInput) return;
|
|
25
|
-
|
|
26
|
-
titleInput.addEventListener("input", () => {
|
|
27
|
-
const currentTitle = titleInput.value;
|
|
28
|
-
slugify(currentTitle).then((slug) => {
|
|
29
|
-
if (titleInput.value === currentTitle) {
|
|
30
|
-
slugInput.value = slug;
|
|
31
|
-
slugInput.dispatchEvent(new Event("input", { bubbles: true }));
|
|
32
|
-
}
|
|
33
|
-
});
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// Run on DOMContentLoaded if the document isn't ready yet, otherwise run now.
|
|
38
|
-
if (document.readyState === "loading") {
|
|
39
|
-
document.addEventListener("DOMContentLoaded", init);
|
|
40
|
-
} else {
|
|
41
|
-
init();
|
|
42
|
-
}
|
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
CREATE TABLE `account` (
|
|
2
|
-
`id` text PRIMARY KEY NOT NULL,
|
|
3
|
-
`account_id` text NOT NULL,
|
|
4
|
-
`provider_id` text NOT NULL,
|
|
5
|
-
`user_id` text NOT NULL,
|
|
6
|
-
`access_token` text,
|
|
7
|
-
`refresh_token` text,
|
|
8
|
-
`id_token` text,
|
|
9
|
-
`access_token_expires_at` integer,
|
|
10
|
-
`refresh_token_expires_at` integer,
|
|
11
|
-
`scope` text,
|
|
12
|
-
`password` text,
|
|
13
|
-
`created_at` integer NOT NULL,
|
|
14
|
-
`updated_at` integer NOT NULL,
|
|
15
|
-
FOREIGN KEY (`user_id`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE no action
|
|
16
|
-
);
|
|
17
|
-
--> statement-breakpoint
|
|
18
|
-
CREATE TABLE `collections` (
|
|
19
|
-
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
|
20
|
-
`path` text,
|
|
21
|
-
`title` text NOT NULL,
|
|
22
|
-
`description` text,
|
|
23
|
-
`created_at` integer NOT NULL,
|
|
24
|
-
`updated_at` integer NOT NULL
|
|
25
|
-
);
|
|
26
|
-
--> statement-breakpoint
|
|
27
|
-
CREATE UNIQUE INDEX `collections_path_unique` ON `collections` (`path`);--> statement-breakpoint
|
|
28
|
-
CREATE TABLE `media` (
|
|
29
|
-
`id` text PRIMARY KEY NOT NULL,
|
|
30
|
-
`post_id` integer,
|
|
31
|
-
`filename` text NOT NULL,
|
|
32
|
-
`original_name` text NOT NULL,
|
|
33
|
-
`mime_type` text NOT NULL,
|
|
34
|
-
`size` integer NOT NULL,
|
|
35
|
-
`r2_key` text NOT NULL,
|
|
36
|
-
`width` integer,
|
|
37
|
-
`height` integer,
|
|
38
|
-
`alt` text,
|
|
39
|
-
`created_at` integer NOT NULL,
|
|
40
|
-
FOREIGN KEY (`post_id`) REFERENCES `posts`(`id`) ON UPDATE no action ON DELETE no action
|
|
41
|
-
);
|
|
42
|
-
--> statement-breakpoint
|
|
43
|
-
CREATE TABLE `post_collections` (
|
|
44
|
-
`post_id` integer NOT NULL,
|
|
45
|
-
`collection_id` integer NOT NULL,
|
|
46
|
-
`added_at` integer NOT NULL,
|
|
47
|
-
PRIMARY KEY(`post_id`, `collection_id`),
|
|
48
|
-
FOREIGN KEY (`post_id`) REFERENCES `posts`(`id`) ON UPDATE no action ON DELETE no action,
|
|
49
|
-
FOREIGN KEY (`collection_id`) REFERENCES `collections`(`id`) ON UPDATE no action ON DELETE no action
|
|
50
|
-
);
|
|
51
|
-
--> statement-breakpoint
|
|
52
|
-
CREATE TABLE `posts` (
|
|
53
|
-
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
|
54
|
-
`type` text NOT NULL,
|
|
55
|
-
`visibility` text DEFAULT 'quiet' NOT NULL,
|
|
56
|
-
`title` text,
|
|
57
|
-
`path` text,
|
|
58
|
-
`content` text,
|
|
59
|
-
`content_html` text,
|
|
60
|
-
`source_url` text,
|
|
61
|
-
`source_name` text,
|
|
62
|
-
`source_domain` text,
|
|
63
|
-
`reply_to_id` integer,
|
|
64
|
-
`thread_id` integer,
|
|
65
|
-
`deleted_at` integer,
|
|
66
|
-
`published_at` integer NOT NULL,
|
|
67
|
-
`created_at` integer NOT NULL,
|
|
68
|
-
`updated_at` integer NOT NULL
|
|
69
|
-
);
|
|
70
|
-
--> statement-breakpoint
|
|
71
|
-
CREATE TABLE `redirects` (
|
|
72
|
-
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
|
73
|
-
`from_path` text NOT NULL,
|
|
74
|
-
`to_path` text NOT NULL,
|
|
75
|
-
`type` integer DEFAULT 301 NOT NULL,
|
|
76
|
-
`created_at` integer NOT NULL
|
|
77
|
-
);
|
|
78
|
-
--> statement-breakpoint
|
|
79
|
-
CREATE UNIQUE INDEX `redirects_from_path_unique` ON `redirects` (`from_path`);--> statement-breakpoint
|
|
80
|
-
CREATE TABLE `session` (
|
|
81
|
-
`id` text PRIMARY KEY NOT NULL,
|
|
82
|
-
`expires_at` integer NOT NULL,
|
|
83
|
-
`token` text NOT NULL,
|
|
84
|
-
`created_at` integer NOT NULL,
|
|
85
|
-
`updated_at` integer NOT NULL,
|
|
86
|
-
`ip_address` text,
|
|
87
|
-
`user_agent` text,
|
|
88
|
-
`user_id` text NOT NULL,
|
|
89
|
-
FOREIGN KEY (`user_id`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE no action
|
|
90
|
-
);
|
|
91
|
-
--> statement-breakpoint
|
|
92
|
-
CREATE UNIQUE INDEX `session_token_unique` ON `session` (`token`);--> statement-breakpoint
|
|
93
|
-
CREATE TABLE `settings` (
|
|
94
|
-
`key` text PRIMARY KEY NOT NULL,
|
|
95
|
-
`value` text NOT NULL,
|
|
96
|
-
`updated_at` integer NOT NULL
|
|
97
|
-
);
|
|
98
|
-
--> statement-breakpoint
|
|
99
|
-
CREATE TABLE `user` (
|
|
100
|
-
`id` text PRIMARY KEY NOT NULL,
|
|
101
|
-
`name` text NOT NULL,
|
|
102
|
-
`email` text NOT NULL,
|
|
103
|
-
`email_verified` integer DEFAULT false NOT NULL,
|
|
104
|
-
`image` text,
|
|
105
|
-
`role` text DEFAULT 'admin',
|
|
106
|
-
`created_at` integer NOT NULL,
|
|
107
|
-
`updated_at` integer NOT NULL
|
|
108
|
-
);
|
|
109
|
-
--> statement-breakpoint
|
|
110
|
-
CREATE UNIQUE INDEX `user_email_unique` ON `user` (`email`);--> statement-breakpoint
|
|
111
|
-
CREATE TABLE `verification` (
|
|
112
|
-
`id` text PRIMARY KEY NOT NULL,
|
|
113
|
-
`identifier` text NOT NULL,
|
|
114
|
-
`value` text NOT NULL,
|
|
115
|
-
`expires_at` integer NOT NULL,
|
|
116
|
-
`created_at` integer,
|
|
117
|
-
`updated_at` integer
|
|
118
|
-
);
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
-- FTS5 virtual table for full-text search (trigram tokenizer for CJK support)
|
|
2
|
-
CREATE VIRTUAL TABLE IF NOT EXISTS posts_fts USING fts5(
|
|
3
|
-
title,
|
|
4
|
-
content,
|
|
5
|
-
content='posts',
|
|
6
|
-
content_rowid='id',
|
|
7
|
-
tokenize='trigram'
|
|
8
|
-
);
|
|
9
|
-
|
|
10
|
-
-- Populate FTS with existing posts
|
|
11
|
-
INSERT INTO posts_fts(rowid, title, content)
|
|
12
|
-
SELECT id, COALESCE(title, ''), COALESCE(content, '')
|
|
13
|
-
FROM posts WHERE deleted_at IS NULL;
|
|
14
|
-
|
|
15
|
-
-- Trigger: sync FTS on INSERT
|
|
16
|
-
CREATE TRIGGER posts_fts_insert AFTER INSERT ON posts
|
|
17
|
-
WHEN NEW.deleted_at IS NULL
|
|
18
|
-
BEGIN
|
|
19
|
-
INSERT INTO posts_fts(rowid, title, content)
|
|
20
|
-
VALUES (NEW.id, COALESCE(NEW.title, ''), COALESCE(NEW.content, ''));
|
|
21
|
-
END;
|
|
22
|
-
|
|
23
|
-
-- Trigger: sync FTS on UPDATE
|
|
24
|
-
CREATE TRIGGER posts_fts_update AFTER UPDATE ON posts BEGIN
|
|
25
|
-
DELETE FROM posts_fts WHERE rowid = OLD.id;
|
|
26
|
-
INSERT INTO posts_fts(rowid, title, content)
|
|
27
|
-
SELECT NEW.id, COALESCE(NEW.title, ''), COALESCE(NEW.content, '')
|
|
28
|
-
WHERE NEW.deleted_at IS NULL;
|
|
29
|
-
END;
|
|
30
|
-
|
|
31
|
-
-- Trigger: sync FTS on DELETE
|
|
32
|
-
CREATE TRIGGER posts_fts_delete AFTER DELETE ON posts BEGIN
|
|
33
|
-
DELETE FROM posts_fts WHERE rowid = OLD.id;
|
|
34
|
-
END;
|
|
@@ -1,268 +0,0 @@
|
|
|
1
|
-
-- v2 Schema Migration
|
|
2
|
-
-- Restructures posts, creates pages, updates collections, replaces navigation_links with nav_items
|
|
3
|
-
|
|
4
|
-
-- Disable FK checks for migration (dropping/recreating tables with cross-references)
|
|
5
|
-
PRAGMA foreign_keys = OFF;
|
|
6
|
-
--> statement-breakpoint
|
|
7
|
-
|
|
8
|
-
-- =============================================================================
|
|
9
|
-
-- 1. Create pages table (before modifying posts, migrate type='page' data)
|
|
10
|
-
-- =============================================================================
|
|
11
|
-
|
|
12
|
-
CREATE TABLE `pages` (
|
|
13
|
-
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
|
14
|
-
`slug` text NOT NULL,
|
|
15
|
-
`title` text,
|
|
16
|
-
`body` text,
|
|
17
|
-
`body_html` text,
|
|
18
|
-
`status` text DEFAULT 'published' NOT NULL,
|
|
19
|
-
`created_at` integer NOT NULL,
|
|
20
|
-
`updated_at` integer NOT NULL
|
|
21
|
-
);
|
|
22
|
-
--> statement-breakpoint
|
|
23
|
-
CREATE UNIQUE INDEX `pages_slug_unique` ON `pages` (`slug`);
|
|
24
|
-
--> statement-breakpoint
|
|
25
|
-
|
|
26
|
-
-- Migrate type='page' posts into pages table
|
|
27
|
-
INSERT INTO `pages` (`slug`, `title`, `body`, `body_html`, `status`, `created_at`, `updated_at`)
|
|
28
|
-
SELECT
|
|
29
|
-
CASE
|
|
30
|
-
WHEN `path` IS NOT NULL AND `path` != '' THEN REPLACE(`path`, '/', '')
|
|
31
|
-
ELSE 'page-' || `id`
|
|
32
|
-
END,
|
|
33
|
-
`title`,
|
|
34
|
-
`content`,
|
|
35
|
-
`content_html`,
|
|
36
|
-
CASE WHEN `visibility` = 'draft' THEN 'draft' ELSE 'published' END,
|
|
37
|
-
`created_at`,
|
|
38
|
-
`updated_at`
|
|
39
|
-
FROM `posts`
|
|
40
|
-
WHERE `type` = 'page';
|
|
41
|
-
--> statement-breakpoint
|
|
42
|
-
|
|
43
|
-
-- =============================================================================
|
|
44
|
-
-- 2. Restructure posts table (create new → migrate → drop old → rename)
|
|
45
|
-
-- =============================================================================
|
|
46
|
-
|
|
47
|
-
CREATE TABLE `posts_new` (
|
|
48
|
-
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
|
49
|
-
`format` text DEFAULT 'note' NOT NULL,
|
|
50
|
-
`status` text DEFAULT 'published' NOT NULL,
|
|
51
|
-
`featured` integer DEFAULT 0 NOT NULL,
|
|
52
|
-
`pinned` integer DEFAULT 0 NOT NULL,
|
|
53
|
-
`slug` text,
|
|
54
|
-
`title` text,
|
|
55
|
-
`url` text,
|
|
56
|
-
`body` text,
|
|
57
|
-
`body_html` text,
|
|
58
|
-
`quote_text` text,
|
|
59
|
-
`rating` integer,
|
|
60
|
-
`collection_id` integer,
|
|
61
|
-
`reply_to_id` integer,
|
|
62
|
-
`thread_id` integer,
|
|
63
|
-
`deleted_at` integer,
|
|
64
|
-
`published_at` integer NOT NULL,
|
|
65
|
-
`created_at` integer NOT NULL,
|
|
66
|
-
`updated_at` integer NOT NULL,
|
|
67
|
-
FOREIGN KEY (`collection_id`) REFERENCES `collections`(`id`) ON UPDATE no action ON DELETE set null
|
|
68
|
-
);
|
|
69
|
-
--> statement-breakpoint
|
|
70
|
-
|
|
71
|
-
-- Migrate data from old posts to new posts (excluding type='page')
|
|
72
|
-
INSERT INTO `posts_new` (
|
|
73
|
-
`id`, `format`, `status`, `featured`, `pinned`,
|
|
74
|
-
`slug`, `title`, `url`, `body`, `body_html`, `quote_text`, `rating`,
|
|
75
|
-
`collection_id`, `reply_to_id`, `thread_id`,
|
|
76
|
-
`deleted_at`, `published_at`, `created_at`, `updated_at`
|
|
77
|
-
)
|
|
78
|
-
SELECT
|
|
79
|
-
`id`,
|
|
80
|
-
-- format mapping: article→note, image→note, note→note, link→link, quote→quote
|
|
81
|
-
CASE
|
|
82
|
-
WHEN `type` IN ('article', 'image', 'note') THEN 'note'
|
|
83
|
-
WHEN `type` = 'link' THEN 'link'
|
|
84
|
-
WHEN `type` = 'quote' THEN 'quote'
|
|
85
|
-
ELSE 'note'
|
|
86
|
-
END,
|
|
87
|
-
-- status mapping from visibility
|
|
88
|
-
CASE WHEN `visibility` = 'draft' THEN 'draft' ELSE 'published' END,
|
|
89
|
-
-- featured mapping from visibility
|
|
90
|
-
CASE WHEN `visibility` = 'featured' THEN 1 ELSE 0 END,
|
|
91
|
-
-- pinned: default 0
|
|
92
|
-
0,
|
|
93
|
-
-- slug: migrate from path (strip leading /)
|
|
94
|
-
CASE
|
|
95
|
-
WHEN `path` IS NOT NULL AND `path` != '' THEN REPLACE(`path`, '/', '')
|
|
96
|
-
ELSE NULL
|
|
97
|
-
END,
|
|
98
|
-
`title`,
|
|
99
|
-
`source_url`,
|
|
100
|
-
`content`,
|
|
101
|
-
`content_html`,
|
|
102
|
-
-- quote_text: for quote type, content was the quote; set to null (manual fix may be needed)
|
|
103
|
-
NULL,
|
|
104
|
-
-- rating: null
|
|
105
|
-
NULL,
|
|
106
|
-
-- collection_id: migrate from post_collections (take first collection for each post)
|
|
107
|
-
(SELECT `collection_id` FROM `post_collections` WHERE `post_collections`.`post_id` = `posts`.`id` LIMIT 1),
|
|
108
|
-
`reply_to_id`,
|
|
109
|
-
`thread_id`,
|
|
110
|
-
`deleted_at`,
|
|
111
|
-
`published_at`,
|
|
112
|
-
`created_at`,
|
|
113
|
-
`updated_at`
|
|
114
|
-
FROM `posts`
|
|
115
|
-
WHERE `type` != 'page';
|
|
116
|
-
--> statement-breakpoint
|
|
117
|
-
|
|
118
|
-
-- Update media references to point at new table (foreign keys reference posts)
|
|
119
|
-
-- media.post_id still works since IDs are preserved
|
|
120
|
-
--> statement-breakpoint
|
|
121
|
-
|
|
122
|
-
-- Drop old posts table and rename new one
|
|
123
|
-
DROP TABLE `posts`;
|
|
124
|
-
--> statement-breakpoint
|
|
125
|
-
ALTER TABLE `posts_new` RENAME TO `posts`;
|
|
126
|
-
--> statement-breakpoint
|
|
127
|
-
CREATE UNIQUE INDEX `posts_slug_unique` ON `posts` (`slug`);
|
|
128
|
-
--> statement-breakpoint
|
|
129
|
-
|
|
130
|
-
-- =============================================================================
|
|
131
|
-
-- 3. Update collections table (add new columns, rename path→slug)
|
|
132
|
-
-- =============================================================================
|
|
133
|
-
|
|
134
|
-
CREATE TABLE `collections_new` (
|
|
135
|
-
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
|
136
|
-
`slug` text NOT NULL,
|
|
137
|
-
`title` text NOT NULL,
|
|
138
|
-
`description` text,
|
|
139
|
-
`icon` text,
|
|
140
|
-
`sort_order` text DEFAULT 'newest' NOT NULL,
|
|
141
|
-
`position` integer DEFAULT 0 NOT NULL,
|
|
142
|
-
`show_divider` integer DEFAULT 0 NOT NULL,
|
|
143
|
-
`created_at` integer NOT NULL,
|
|
144
|
-
`updated_at` integer NOT NULL
|
|
145
|
-
);
|
|
146
|
-
--> statement-breakpoint
|
|
147
|
-
CREATE UNIQUE INDEX `collections_new_slug_unique` ON `collections_new` (`slug`);
|
|
148
|
-
--> statement-breakpoint
|
|
149
|
-
|
|
150
|
-
INSERT INTO `collections_new` (`id`, `slug`, `title`, `description`, `icon`, `sort_order`, `position`, `show_divider`, `created_at`, `updated_at`)
|
|
151
|
-
SELECT
|
|
152
|
-
`id`,
|
|
153
|
-
COALESCE(`path`, 'collection-' || `id`),
|
|
154
|
-
`title`,
|
|
155
|
-
`description`,
|
|
156
|
-
NULL,
|
|
157
|
-
'newest',
|
|
158
|
-
0,
|
|
159
|
-
0,
|
|
160
|
-
`created_at`,
|
|
161
|
-
`updated_at`
|
|
162
|
-
FROM `collections`;
|
|
163
|
-
--> statement-breakpoint
|
|
164
|
-
|
|
165
|
-
DROP TABLE `collections`;
|
|
166
|
-
--> statement-breakpoint
|
|
167
|
-
ALTER TABLE `collections_new` RENAME TO `collections`;
|
|
168
|
-
--> statement-breakpoint
|
|
169
|
-
CREATE UNIQUE INDEX `collections_slug_unique` ON `collections` (`slug`);
|
|
170
|
-
--> statement-breakpoint
|
|
171
|
-
|
|
172
|
-
-- =============================================================================
|
|
173
|
-
-- 4. Replace navigation_links with nav_items
|
|
174
|
-
-- =============================================================================
|
|
175
|
-
|
|
176
|
-
CREATE TABLE `nav_items` (
|
|
177
|
-
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
|
178
|
-
`type` text DEFAULT 'link' NOT NULL,
|
|
179
|
-
`label` text NOT NULL,
|
|
180
|
-
`url` text NOT NULL,
|
|
181
|
-
`page_id` integer,
|
|
182
|
-
`position` integer DEFAULT 0 NOT NULL,
|
|
183
|
-
`created_at` integer NOT NULL,
|
|
184
|
-
`updated_at` integer NOT NULL,
|
|
185
|
-
FOREIGN KEY (`page_id`) REFERENCES `pages`(`id`) ON UPDATE no action ON DELETE cascade
|
|
186
|
-
);
|
|
187
|
-
--> statement-breakpoint
|
|
188
|
-
|
|
189
|
-
-- Migrate existing navigation_links as type='link'
|
|
190
|
-
INSERT INTO `nav_items` (`type`, `label`, `url`, `page_id`, `position`, `created_at`, `updated_at`)
|
|
191
|
-
SELECT 'link', `label`, `url`, NULL, `position`, `created_at`, `updated_at`
|
|
192
|
-
FROM `navigation_links`;
|
|
193
|
-
--> statement-breakpoint
|
|
194
|
-
|
|
195
|
-
DROP TABLE `navigation_links`;
|
|
196
|
-
--> statement-breakpoint
|
|
197
|
-
|
|
198
|
-
-- =============================================================================
|
|
199
|
-
-- 5. Drop post_collections table (replaced by posts.collection_id)
|
|
200
|
-
-- =============================================================================
|
|
201
|
-
|
|
202
|
-
DROP TABLE `post_collections`;
|
|
203
|
-
--> statement-breakpoint
|
|
204
|
-
|
|
205
|
-
-- =============================================================================
|
|
206
|
-
-- 6. Rebuild FTS5 (column rename: content→body, add quote_text)
|
|
207
|
-
-- =============================================================================
|
|
208
|
-
|
|
209
|
-
-- Drop old FTS triggers
|
|
210
|
-
DROP TRIGGER IF EXISTS posts_fts_insert;
|
|
211
|
-
--> statement-breakpoint
|
|
212
|
-
DROP TRIGGER IF EXISTS posts_fts_update;
|
|
213
|
-
--> statement-breakpoint
|
|
214
|
-
DROP TRIGGER IF EXISTS posts_fts_delete;
|
|
215
|
-
--> statement-breakpoint
|
|
216
|
-
|
|
217
|
-
-- Drop old FTS table
|
|
218
|
-
DROP TABLE IF EXISTS posts_fts;
|
|
219
|
-
--> statement-breakpoint
|
|
220
|
-
|
|
221
|
-
-- Create new FTS table with updated columns
|
|
222
|
-
CREATE VIRTUAL TABLE IF NOT EXISTS posts_fts USING fts5(
|
|
223
|
-
title,
|
|
224
|
-
body,
|
|
225
|
-
quote_text,
|
|
226
|
-
content=posts,
|
|
227
|
-
content_rowid=id,
|
|
228
|
-
tokenize='trigram'
|
|
229
|
-
);
|
|
230
|
-
--> statement-breakpoint
|
|
231
|
-
|
|
232
|
-
-- Populate FTS with migrated data
|
|
233
|
-
INSERT INTO posts_fts(rowid, title, body, quote_text)
|
|
234
|
-
SELECT id, COALESCE(title, ''), COALESCE(body, ''), COALESCE(quote_text, '')
|
|
235
|
-
FROM posts WHERE deleted_at IS NULL;
|
|
236
|
-
--> statement-breakpoint
|
|
237
|
-
|
|
238
|
-
-- Trigger: sync FTS on INSERT
|
|
239
|
-
CREATE TRIGGER posts_fts_insert AFTER INSERT ON posts
|
|
240
|
-
WHEN NEW.deleted_at IS NULL
|
|
241
|
-
BEGIN
|
|
242
|
-
INSERT INTO posts_fts(rowid, title, body, quote_text)
|
|
243
|
-
VALUES (NEW.id, COALESCE(NEW.title, ''), COALESCE(NEW.body, ''), COALESCE(NEW.quote_text, ''));
|
|
244
|
-
END;
|
|
245
|
-
--> statement-breakpoint
|
|
246
|
-
|
|
247
|
-
-- Trigger: sync FTS on UPDATE
|
|
248
|
-
CREATE TRIGGER posts_fts_update AFTER UPDATE ON posts BEGIN
|
|
249
|
-
DELETE FROM posts_fts WHERE rowid = OLD.id;
|
|
250
|
-
INSERT INTO posts_fts(rowid, title, body, quote_text)
|
|
251
|
-
SELECT NEW.id, COALESCE(NEW.title, ''), COALESCE(NEW.body, ''), COALESCE(NEW.quote_text, '')
|
|
252
|
-
WHERE NEW.deleted_at IS NULL;
|
|
253
|
-
END;
|
|
254
|
-
--> statement-breakpoint
|
|
255
|
-
|
|
256
|
-
-- Trigger: sync FTS on DELETE
|
|
257
|
-
CREATE TRIGGER posts_fts_delete AFTER DELETE ON posts BEGIN
|
|
258
|
-
DELETE FROM posts_fts WHERE rowid = OLD.id;
|
|
259
|
-
END;
|
|
260
|
-
--> statement-breakpoint
|
|
261
|
-
|
|
262
|
-
-- =============================================================================
|
|
263
|
-
-- 7. Re-enable FK checks and verify integrity
|
|
264
|
-
-- =============================================================================
|
|
265
|
-
|
|
266
|
-
PRAGMA foreign_keys = ON;
|
|
267
|
-
--> statement-breakpoint
|
|
268
|
-
PRAGMA foreign_key_check;
|