@jant/core 0.3.35 → 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/module-RjUF93sV.js +716 -0
- package/dist/client/assets/native-48B9X9Wg.js +1 -0
- package/dist/client/assets/url-FWFqPJPb.js +1 -0
- package/dist/client/client.css +1 -1
- package/dist/client/client.js +4564 -3013
- package/dist/index.js +12885 -8161
- package/package.json +23 -6
- package/src/__tests__/helpers/app.ts +10 -10
- package/src/__tests__/helpers/db.ts +91 -87
- package/src/app.tsx +157 -31
- 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/{lib → client}/avatar-upload.ts +4 -3
- package/src/{lib → client}/collection-form-bridge.ts +2 -2
- package/src/{ui → client}/components/__tests__/jant-collection-form.test.ts +26 -9
- package/src/client/components/__tests__/jant-compose-dialog.test.ts +1140 -0
- package/src/client/components/__tests__/jant-compose-editor.test.ts +504 -0
- package/src/{ui → client}/components/__tests__/jant-post-form.test.ts +37 -17
- package/src/{ui → client}/components/__tests__/jant-settings-avatar.test.ts +2 -2
- package/src/{ui → client}/components/__tests__/jant-settings-general.test.ts +3 -3
- package/src/client/components/collection-sidebar-types.ts +43 -0
- package/src/{ui → client}/components/collection-types.ts +3 -4
- package/src/client/components/compose-types.ts +174 -0
- package/src/client/components/jant-collection-form.ts +667 -0
- package/src/client/components/jant-collection-sidebar.ts +805 -0
- package/src/client/components/jant-compose-dialog.ts +2161 -0
- package/src/client/components/jant-compose-editor.ts +1813 -0
- package/src/client/components/jant-compose-fullscreen.ts +283 -0
- package/src/client/components/jant-media-lightbox.ts +259 -0
- package/src/{ui → client}/components/jant-nav-manager.ts +97 -298
- package/src/{ui → client}/components/jant-post-form.ts +141 -12
- package/src/client/components/jant-post-menu.ts +1019 -0
- package/src/{ui → client}/components/jant-settings-avatar.ts +3 -3
- package/src/{ui → client}/components/jant-settings-general.ts +38 -4
- package/src/client/components/jant-text-preview.ts +232 -0
- package/src/{ui → client}/components/nav-manager-types.ts +6 -18
- package/src/{ui → client}/components/post-form-template.ts +137 -38
- package/src/{ui → client}/components/post-form-types.ts +15 -4
- package/src/client/compose-bridge.ts +583 -0
- package/src/{lib → client}/image-processor.ts +26 -8
- package/src/client/lazy-slugify.ts +51 -0
- package/src/client/media-metadata.ts +247 -0
- package/src/client/multipart-upload.ts +160 -0
- package/src/{lib → client}/nav-manager-bridge.ts +1 -1
- package/src/{lib → client}/post-form-bridge.ts +53 -2
- package/src/{lib → client}/settings-bridge.ts +3 -15
- package/src/client/thread-context.ts +140 -0
- package/src/client/tiptap/bubble-menu.ts +205 -0
- package/src/client/tiptap/create-editor.ts +86 -0
- package/src/client/tiptap/exitable-marks.ts +73 -0
- package/src/client/tiptap/extensions.ts +65 -0
- package/src/client/tiptap/image-node.ts +482 -0
- package/src/client/tiptap/link-toolbar.ts +371 -0
- package/src/client/tiptap/more-break.ts +50 -0
- package/src/client/tiptap/paste-image.ts +129 -0
- package/src/client/tiptap/slash-commands.ts +438 -0
- package/src/{lib → client}/toast.ts +101 -3
- package/src/client/types/sortablejs.d.ts +44 -0
- package/src/client/upload-with-metadata.ts +54 -0
- package/src/client/video-processor.ts +207 -0
- package/src/client.ts +27 -17
- 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 -140
- 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 +783 -1087
- package/src/i18n/locales/en.ts +1 -1
- package/src/i18n/locales/zh-Hans.po +867 -812
- package/src/i18n/locales/zh-Hans.ts +1 -1
- package/src/i18n/locales/zh-Hant.po +878 -823
- 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__/resolve-config.test.ts +2 -2
- package/src/lib/__tests__/schemas.test.ts +186 -65
- 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__/url.test.ts +2 -2
- package/src/lib/__tests__/view.test.ts +140 -65
- package/src/lib/blurhash-placeholder.ts +102 -0
- package/src/lib/constants.ts +3 -1
- package/src/lib/emoji-catalog.ts +963 -0
- package/src/lib/errors.ts +11 -8
- package/src/lib/feed.ts +77 -31
- 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 +22 -12
- package/src/lib/nanoid.ts +29 -0
- package/src/lib/navigation.ts +1 -1
- package/src/lib/render.tsx +24 -5
- package/src/lib/resolve-config.ts +13 -2
- package/src/lib/schemas.ts +226 -58
- 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 +158 -0
- package/src/lib/theme.ts +11 -8
- package/src/lib/timeline.ts +76 -34
- package/src/lib/tiptap-render.ts +191 -0
- package/src/lib/tiptap-to-markdown.ts +305 -0
- package/src/lib/upload.ts +263 -14
- package/src/lib/url.ts +37 -22
- package/src/lib/view.ts +236 -55
- 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/error-handler.ts +3 -3
- package/src/middleware/onboarding.ts +1 -1
- package/src/middleware/secure-headers.ts +40 -0
- package/src/preset.css +83 -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 +57 -31
- 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 +81 -62
- package/src/routes/api/search.ts +3 -4
- package/src/routes/api/upload-multipart.ts +245 -0
- package/src/routes/api/upload.ts +92 -24
- package/src/routes/auth/__tests__/setup.test.ts +20 -60
- package/src/routes/auth/reset.tsx +5 -4
- package/src/routes/auth/setup.tsx +39 -31
- package/src/routes/auth/signin.tsx +13 -14
- package/src/routes/compose.tsx +27 -63
- package/src/routes/dash/__tests__/settings-avatar.test.ts +44 -9
- package/src/routes/dash/custom-urls.tsx +414 -0
- package/src/routes/dash/settings.tsx +475 -99
- package/src/routes/feed/__tests__/rss.test.ts +22 -23
- package/src/routes/feed/rss.ts +6 -2
- 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 +36 -18
- package/src/routes/pages/archive.tsx +177 -37
- package/src/routes/pages/collection.tsx +43 -14
- package/src/routes/pages/collections.tsx +11 -2
- package/src/routes/pages/featured.tsx +27 -3
- package/src/routes/pages/home.tsx +15 -14
- 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 +800 -230
- package/src/services/__tests__/search.test.ts +67 -10
- package/src/services/__tests__/settings.test.ts +3 -3
- 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 +764 -172
- package/src/services/search.ts +161 -74
- package/src/services/settings.ts +6 -2
- package/src/styles/components.css +293 -62
- package/src/styles/tokens.css +93 -5
- package/src/styles/ui.css +4349 -766
- package/src/types/bindings.ts +8 -0
- package/src/types/config.ts +34 -4
- package/src/types/constants.ts +17 -2
- package/src/types/entities.ts +83 -37
- package/src/types/operations.ts +20 -27
- package/src/types/props.ts +52 -17
- package/src/types/views.ts +48 -24
- package/src/ui/color-themes.ts +133 -23
- package/src/ui/compose/ComposeDialog.tsx +255 -16
- 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 +12 -2
- package/src/ui/dash/appearance/AdvancedContent.tsx +71 -59
- package/src/ui/dash/appearance/ColorThemeContent.tsx +48 -33
- package/src/ui/dash/appearance/FontThemeContent.tsx +65 -73
- package/src/ui/dash/appearance/NavigationContent.tsx +106 -135
- package/src/ui/dash/index.ts +0 -3
- package/src/ui/dash/settings/AccountContent.tsx +87 -146
- 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 +78 -0
- package/src/ui/dash/settings/GeneralContent.tsx +3 -62
- package/src/ui/dash/settings/SessionsContent.tsx +159 -0
- package/src/ui/dash/settings/SettingsRootContent.tsx +266 -0
- 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 +116 -103
- package/src/ui/pages/ArchivePage.tsx +923 -95
- package/src/ui/pages/CollectionPage.tsx +6 -35
- package/src/ui/pages/CollectionsPage.tsx +2 -1
- package/src/ui/pages/ComposePage.tsx +54 -0
- package/src/ui/pages/FeaturedPage.tsx +2 -1
- package/src/ui/pages/HomePage.tsx +1 -1
- package/src/ui/pages/PostPage.tsx +30 -45
- package/src/ui/pages/SearchPage.tsx +182 -38
- package/src/ui/shared/AdminBreadcrumb.tsx +29 -0
- package/src/ui/shared/CollectionsSidebar.tsx +239 -4
- package/src/ui/shared/MediaGallery.tsx +475 -41
- 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/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/meta/0003_snapshot.json +0 -821
- package/src/lib/__tests__/sqid.test.ts +0 -65
- package/src/lib/collections-reorder.ts +0 -28
- package/src/lib/compose-bridge.ts +0 -280
- package/src/lib/media-upload.ts +0 -148
- 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/appearance.tsx +0 -240
- package/src/routes/dash/collections.tsx +0 -211
- package/src/routes/dash/index.tsx +0 -103
- package/src/routes/dash/media.tsx +0 -132
- package/src/routes/dash/pages.tsx +0 -239
- package/src/routes/dash/posts.tsx +0 -334
- package/src/routes/dash/redirects.tsx +0 -257
- 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 -203
- package/src/services/path-registry.ts +0 -160
- package/src/services/redirect.ts +0 -97
- package/src/types/sortablejs.d.ts +0 -29
- package/src/ui/components/__tests__/jant-compose-dialog.test.ts +0 -512
- package/src/ui/components/__tests__/jant-compose-editor.test.ts +0 -272
- package/src/ui/components/compose-types.ts +0 -75
- package/src/ui/components/jant-collection-form.ts +0 -512
- package/src/ui/components/jant-compose-dialog.ts +0 -495
- package/src/ui/components/jant-compose-editor.ts +0 -814
- package/src/ui/dash/PageForm.tsx +0 -185
- package/src/ui/dash/PostList.tsx +0 -95
- package/src/ui/dash/appearance/AppearanceNav.tsx +0 -60
- package/src/ui/dash/collections/CollectionForm.tsx +0 -166
- package/src/ui/dash/collections/CollectionsListContent.tsx +0 -146
- package/src/ui/dash/collections/IconPickerGrid.tsx +0 -50
- package/src/ui/dash/collections/ViewCollectionContent.tsx +0 -103
- package/src/ui/dash/media/MediaListContent.tsx +0 -201
- package/src/ui/dash/media/ViewMediaContent.tsx +0 -208
- package/src/ui/dash/pages/PagesContent.tsx +0 -74
- package/src/ui/dash/posts/PostForm.tsx +0 -248
- package/src/ui/dash/settings/SettingsNav.tsx +0 -52
- package/src/ui/layouts/DashLayout.tsx +0 -165
- package/src/ui/pages/SinglePage.tsx +0 -23
- package/src/ui/shared/ThreadView.tsx +0 -136
- /package/src/{ui → client}/components/settings-types.ts +0 -0
|
@@ -1,162 +1,103 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Password settings: change sign-in password
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { useLingui } from "@lingui/react/macro";
|
|
6
|
-
import { SettingsNav } from "./SettingsNav.js";
|
|
7
6
|
|
|
8
|
-
export function AccountContent(
|
|
7
|
+
export function AccountContent() {
|
|
9
8
|
const { t } = useLingui();
|
|
10
9
|
|
|
11
|
-
const profileSignals = JSON.stringify({ userName }).replace(/</g, "\\u003c");
|
|
12
|
-
|
|
13
10
|
return (
|
|
14
|
-
|
|
15
|
-
<
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
11
|
+
<div class="flex flex-col max-w-lg">
|
|
12
|
+
<form
|
|
13
|
+
data-signals="{currentPassword: '', newPassword: '', confirmPassword: ''}"
|
|
14
|
+
data-on:submit__prevent="@post('/settings/account/password')"
|
|
15
|
+
data-indicator="_passwordLoading"
|
|
16
|
+
>
|
|
17
|
+
<h2 class="text-lg font-semibold mb-4">
|
|
18
|
+
{t({
|
|
19
|
+
message: "Change Password",
|
|
20
|
+
comment: "@context: Settings section heading",
|
|
21
|
+
})}
|
|
22
|
+
</h2>
|
|
23
|
+
<div class="flex flex-col gap-4">
|
|
24
|
+
<div class="field">
|
|
25
|
+
<label class="label">
|
|
26
|
+
{t({
|
|
27
|
+
message: "Current Password",
|
|
28
|
+
comment: "@context: Password form field",
|
|
29
|
+
})}
|
|
30
|
+
</label>
|
|
31
|
+
<input
|
|
32
|
+
type="password"
|
|
33
|
+
data-bind="currentPassword"
|
|
34
|
+
class="input"
|
|
35
|
+
required
|
|
36
|
+
autocomplete="current-password"
|
|
37
|
+
/>
|
|
39
38
|
</div>
|
|
40
39
|
|
|
41
|
-
<
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
role="status"
|
|
58
|
-
>
|
|
59
|
-
<path d="M21 12a9 9 0 1 1-6.219-8.56" />
|
|
60
|
-
</svg>
|
|
61
|
-
{t({
|
|
62
|
-
message: "Save Profile",
|
|
63
|
-
comment: "@context: Button to save profile",
|
|
64
|
-
})}
|
|
65
|
-
</button>
|
|
66
|
-
</form>
|
|
67
|
-
|
|
68
|
-
<hr class="my-8" />
|
|
69
|
-
|
|
70
|
-
<form
|
|
71
|
-
data-signals="{currentPassword: '', newPassword: '', confirmPassword: ''}"
|
|
72
|
-
data-on:submit__prevent="@post('/dash/settings/password')"
|
|
73
|
-
data-indicator="_passwordLoading"
|
|
74
|
-
>
|
|
75
|
-
<h2 class="text-lg font-semibold mb-4">
|
|
76
|
-
{t({
|
|
77
|
-
message: "Change Password",
|
|
78
|
-
comment: "@context: Settings section heading",
|
|
79
|
-
})}
|
|
80
|
-
</h2>
|
|
81
|
-
<div class="flex flex-col gap-4">
|
|
82
|
-
<div class="field">
|
|
83
|
-
<label class="label">
|
|
84
|
-
{t({
|
|
85
|
-
message: "Current Password",
|
|
86
|
-
comment: "@context: Password form field",
|
|
87
|
-
})}
|
|
88
|
-
</label>
|
|
89
|
-
<input
|
|
90
|
-
type="password"
|
|
91
|
-
data-bind="currentPassword"
|
|
92
|
-
class="input"
|
|
93
|
-
required
|
|
94
|
-
autocomplete="current-password"
|
|
95
|
-
/>
|
|
96
|
-
</div>
|
|
97
|
-
|
|
98
|
-
<div class="field">
|
|
99
|
-
<label class="label">
|
|
100
|
-
{t({
|
|
101
|
-
message: "New Password",
|
|
102
|
-
comment: "@context: Password form field",
|
|
103
|
-
})}
|
|
104
|
-
</label>
|
|
105
|
-
<input
|
|
106
|
-
type="password"
|
|
107
|
-
data-bind="newPassword"
|
|
108
|
-
class="input"
|
|
109
|
-
required
|
|
110
|
-
minlength={8}
|
|
111
|
-
autocomplete="new-password"
|
|
112
|
-
/>
|
|
113
|
-
</div>
|
|
40
|
+
<div class="field">
|
|
41
|
+
<label class="label">
|
|
42
|
+
{t({
|
|
43
|
+
message: "New Password",
|
|
44
|
+
comment: "@context: Password form field",
|
|
45
|
+
})}
|
|
46
|
+
</label>
|
|
47
|
+
<input
|
|
48
|
+
type="password"
|
|
49
|
+
data-bind="newPassword"
|
|
50
|
+
class="input"
|
|
51
|
+
required
|
|
52
|
+
minlength={8}
|
|
53
|
+
autocomplete="new-password"
|
|
54
|
+
/>
|
|
55
|
+
</div>
|
|
114
56
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
</div>
|
|
57
|
+
<div class="field">
|
|
58
|
+
<label class="label">
|
|
59
|
+
{t({
|
|
60
|
+
message: "Confirm New Password",
|
|
61
|
+
comment: "@context: Password form field",
|
|
62
|
+
})}
|
|
63
|
+
</label>
|
|
64
|
+
<input
|
|
65
|
+
type="password"
|
|
66
|
+
data-bind="confirmPassword"
|
|
67
|
+
class="input"
|
|
68
|
+
required
|
|
69
|
+
minlength={8}
|
|
70
|
+
autocomplete="new-password"
|
|
71
|
+
/>
|
|
131
72
|
</div>
|
|
73
|
+
</div>
|
|
132
74
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
75
|
+
<button
|
|
76
|
+
type="submit"
|
|
77
|
+
class="btn mt-4"
|
|
78
|
+
data-attr:disabled="$_passwordLoading"
|
|
79
|
+
>
|
|
80
|
+
<svg
|
|
81
|
+
data-show="$_passwordLoading"
|
|
82
|
+
style="display:none"
|
|
83
|
+
class="animate-spin size-4"
|
|
84
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
85
|
+
viewBox="0 0 24 24"
|
|
86
|
+
fill="none"
|
|
87
|
+
stroke="currentColor"
|
|
88
|
+
stroke-width="2"
|
|
89
|
+
stroke-linecap="round"
|
|
90
|
+
stroke-linejoin="round"
|
|
91
|
+
role="status"
|
|
137
92
|
>
|
|
138
|
-
<
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
stroke-linecap="round"
|
|
148
|
-
stroke-linejoin="round"
|
|
149
|
-
role="status"
|
|
150
|
-
>
|
|
151
|
-
<path d="M21 12a9 9 0 1 1-6.219-8.56" />
|
|
152
|
-
</svg>
|
|
153
|
-
{t({
|
|
154
|
-
message: "Change Password",
|
|
155
|
-
comment: "@context: Button to change password",
|
|
156
|
-
})}
|
|
157
|
-
</button>
|
|
158
|
-
</form>
|
|
159
|
-
</div>
|
|
160
|
-
</>
|
|
93
|
+
<path d="M21 12a9 9 0 1 1-6.219-8.56" />
|
|
94
|
+
</svg>
|
|
95
|
+
{t({
|
|
96
|
+
message: "Change Password",
|
|
97
|
+
comment: "@context: Button to change password",
|
|
98
|
+
})}
|
|
99
|
+
</button>
|
|
100
|
+
</form>
|
|
101
|
+
</div>
|
|
161
102
|
);
|
|
162
103
|
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Account settings sub-menu — lists Sessions and Password options
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { useLingui } from "@lingui/react/macro";
|
|
6
|
+
|
|
7
|
+
/** Chevron right icon */
|
|
8
|
+
function ChevronRight() {
|
|
9
|
+
return (
|
|
10
|
+
<svg
|
|
11
|
+
class="settings-item-chevron"
|
|
12
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
13
|
+
width="16"
|
|
14
|
+
height="16"
|
|
15
|
+
viewBox="0 0 24 24"
|
|
16
|
+
fill="none"
|
|
17
|
+
stroke="currentColor"
|
|
18
|
+
stroke-width="2"
|
|
19
|
+
stroke-linecap="round"
|
|
20
|
+
stroke-linejoin="round"
|
|
21
|
+
>
|
|
22
|
+
<path d="m9 18 6-6-6-6" />
|
|
23
|
+
</svg>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function AccountMenuItem({
|
|
28
|
+
href,
|
|
29
|
+
icon,
|
|
30
|
+
color,
|
|
31
|
+
name,
|
|
32
|
+
description,
|
|
33
|
+
}: {
|
|
34
|
+
href: string;
|
|
35
|
+
icon: string;
|
|
36
|
+
color: string;
|
|
37
|
+
name: string;
|
|
38
|
+
description: string;
|
|
39
|
+
}) {
|
|
40
|
+
return (
|
|
41
|
+
<a href={href} class="settings-item">
|
|
42
|
+
<span class="settings-item-icon" style={`background-color:${color}`}>
|
|
43
|
+
<span dangerouslySetInnerHTML={{ __html: icon }} />
|
|
44
|
+
</span>
|
|
45
|
+
<span class="settings-item-text">
|
|
46
|
+
<span class="settings-item-name">{name}</span>
|
|
47
|
+
<span class="settings-item-desc">{description}</span>
|
|
48
|
+
</span>
|
|
49
|
+
<ChevronRight />
|
|
50
|
+
</a>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const ICONS = {
|
|
55
|
+
monitor: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="20" height="14" x="2" y="3" rx="2"/><line x1="8" x2="16" y1="21" y2="21"/><line x1="12" x2="12" y1="17" y2="21"/></svg>`,
|
|
56
|
+
lock: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="11" x="3" y="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>`,
|
|
57
|
+
download: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" x2="12" y1="15" y2="3"/></svg>`,
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const COLORS = {
|
|
61
|
+
teal: "oklch(0.55 0.15 185)",
|
|
62
|
+
gray: "oklch(0.55 0.01 250)",
|
|
63
|
+
green: "oklch(0.55 0.18 155)",
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export function AccountMenuContent() {
|
|
67
|
+
const { t } = useLingui();
|
|
68
|
+
|
|
69
|
+
return (
|
|
70
|
+
<div class="settings-root">
|
|
71
|
+
<div>
|
|
72
|
+
<div class="settings-group">
|
|
73
|
+
<AccountMenuItem
|
|
74
|
+
href="/settings/account/sessions"
|
|
75
|
+
icon={ICONS.monitor}
|
|
76
|
+
color={COLORS.teal}
|
|
77
|
+
name={t({
|
|
78
|
+
message: "Sessions",
|
|
79
|
+
comment: "@context: Settings item — session management",
|
|
80
|
+
})}
|
|
81
|
+
description={t({
|
|
82
|
+
message: "Manage active sign-in sessions",
|
|
83
|
+
comment: "@context: Settings item description for sessions",
|
|
84
|
+
})}
|
|
85
|
+
/>
|
|
86
|
+
<AccountMenuItem
|
|
87
|
+
href="/settings/account/password"
|
|
88
|
+
icon={ICONS.lock}
|
|
89
|
+
color={COLORS.gray}
|
|
90
|
+
name={t({
|
|
91
|
+
message: "Password",
|
|
92
|
+
comment: "@context: Settings item — password settings",
|
|
93
|
+
})}
|
|
94
|
+
description={t({
|
|
95
|
+
message: "Change your sign-in password",
|
|
96
|
+
comment:
|
|
97
|
+
"@context: Settings item description for password change",
|
|
98
|
+
})}
|
|
99
|
+
/>
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
|
|
103
|
+
{/* Data */}
|
|
104
|
+
<div>
|
|
105
|
+
<div class="settings-group-label">
|
|
106
|
+
{t({
|
|
107
|
+
message: "Data",
|
|
108
|
+
comment: "@context: Settings group label for data export/import",
|
|
109
|
+
})}
|
|
110
|
+
</div>
|
|
111
|
+
<div class="settings-group">
|
|
112
|
+
<form
|
|
113
|
+
method="post"
|
|
114
|
+
action="/api/export/zola"
|
|
115
|
+
class="settings-export-form"
|
|
116
|
+
>
|
|
117
|
+
<button type="submit" class="settings-item">
|
|
118
|
+
<span
|
|
119
|
+
class="settings-item-icon"
|
|
120
|
+
style={`background-color:${COLORS.green}`}
|
|
121
|
+
>
|
|
122
|
+
<span dangerouslySetInnerHTML={{ __html: ICONS.download }} />
|
|
123
|
+
</span>
|
|
124
|
+
<span class="settings-item-text">
|
|
125
|
+
<span class="settings-item-name">
|
|
126
|
+
{t({
|
|
127
|
+
message: "Export Site",
|
|
128
|
+
comment:
|
|
129
|
+
"@context: Settings item — export site as Zola ZIP",
|
|
130
|
+
})}
|
|
131
|
+
</span>
|
|
132
|
+
<span class="settings-item-desc">
|
|
133
|
+
{t({
|
|
134
|
+
message: "Download as a Zola static site (.zip)",
|
|
135
|
+
comment:
|
|
136
|
+
"@context: Settings item description for site export",
|
|
137
|
+
})}
|
|
138
|
+
</span>
|
|
139
|
+
</span>
|
|
140
|
+
<ChevronRight />
|
|
141
|
+
</button>
|
|
142
|
+
</form>
|
|
143
|
+
</div>
|
|
144
|
+
</div>
|
|
145
|
+
</div>
|
|
146
|
+
);
|
|
147
|
+
}
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API Tokens Settings Page
|
|
3
|
+
*
|
|
4
|
+
* Manage Bearer tokens for programmatic API access.
|
|
5
|
+
* Tokens are shown only once at creation — after that, only the prefix is visible.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { useLingui } from "@lingui/react/macro";
|
|
9
|
+
import type { ApiToken } from "../../../types/entities.js";
|
|
10
|
+
import { formatDate } from "../../../lib/time.js";
|
|
11
|
+
|
|
12
|
+
const API_DOCS_URL = "https://github.com/jant-me/jant/blob/main/docs/API.md";
|
|
13
|
+
|
|
14
|
+
function TokenRow({ token }: { token: ApiToken }) {
|
|
15
|
+
const { t } = useLingui();
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<div class="py-4 flex items-start gap-4 border-b border-border last:border-b-0">
|
|
19
|
+
<div class="flex-1 min-w-0">
|
|
20
|
+
<div class="font-medium">{token.name}</div>
|
|
21
|
+
<div class="text-sm text-muted-foreground mt-0.5">
|
|
22
|
+
<code class="text-xs bg-muted px-1.5 py-0.5 rounded">
|
|
23
|
+
jnt_{token.prefix}...
|
|
24
|
+
</code>
|
|
25
|
+
<span class="mx-2">·</span>
|
|
26
|
+
{t({
|
|
27
|
+
message: `Created ${formatDate(token.createdAt)}`,
|
|
28
|
+
comment: "@context: Token creation date",
|
|
29
|
+
})}
|
|
30
|
+
{token.lastUsedAt && (
|
|
31
|
+
<>
|
|
32
|
+
<span class="mx-2">·</span>
|
|
33
|
+
{t({
|
|
34
|
+
message: `Last used ${formatDate(token.lastUsedAt)}`,
|
|
35
|
+
comment: "@context: Token last used date",
|
|
36
|
+
})}
|
|
37
|
+
</>
|
|
38
|
+
)}
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
41
|
+
<button
|
|
42
|
+
type="button"
|
|
43
|
+
class="btn-sm-ghost text-destructive"
|
|
44
|
+
data-on:click__prevent={`confirm('${t({ message: "Revoke this token? Any scripts using it will stop working.", comment: "@context: Confirm dialog for revoking API token" })}') && @post('/settings/api-tokens/${token.id}/delete')`}
|
|
45
|
+
>
|
|
46
|
+
{t({
|
|
47
|
+
message: "Revoke",
|
|
48
|
+
comment: "@context: Button to revoke API token",
|
|
49
|
+
})}
|
|
50
|
+
</button>
|
|
51
|
+
</div>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function ApiTokensContent({
|
|
56
|
+
tokens,
|
|
57
|
+
siteUrl,
|
|
58
|
+
}: {
|
|
59
|
+
tokens: ApiToken[];
|
|
60
|
+
siteUrl: string;
|
|
61
|
+
}) {
|
|
62
|
+
const { t } = useLingui();
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<div
|
|
66
|
+
class="flex flex-col gap-8 max-w-2xl"
|
|
67
|
+
data-signals="{tokenName: '', _tokenLoading: false, _newPlaintext: '', _tokenCopied: false}"
|
|
68
|
+
>
|
|
69
|
+
{/* New token alert — shown after creation via signal patch */}
|
|
70
|
+
<div data-show="$_newPlaintext" style="display:none">
|
|
71
|
+
<div class="alert" role="alert">
|
|
72
|
+
<strong>
|
|
73
|
+
{t({
|
|
74
|
+
message: "Copy your token now — it won't be shown again.",
|
|
75
|
+
comment: "@context: Warning to copy newly created API token",
|
|
76
|
+
})}
|
|
77
|
+
</strong>
|
|
78
|
+
<section class="mt-2">
|
|
79
|
+
<div class="flex items-center gap-2">
|
|
80
|
+
<code
|
|
81
|
+
class="bg-muted px-3 py-2 rounded break-all select-all flex-1 text-sm"
|
|
82
|
+
data-text="$_newPlaintext"
|
|
83
|
+
>
|
|
84
|
+
{" "}
|
|
85
|
+
</code>
|
|
86
|
+
<button
|
|
87
|
+
type="button"
|
|
88
|
+
class="btn-sm-outline shrink-0"
|
|
89
|
+
data-on:click="navigator.clipboard.writeText($_newPlaintext); $_tokenCopied = true"
|
|
90
|
+
data-text={`$_tokenCopied ? '${t({ message: "Copied", comment: "@context: Feedback after copying API token" })}' : '${t({ message: "Copy Token", comment: "@context: Button to copy API token to clipboard" })}'`}
|
|
91
|
+
>
|
|
92
|
+
{t({
|
|
93
|
+
message: "Copy Token",
|
|
94
|
+
comment: "@context: Button to copy API token to clipboard",
|
|
95
|
+
})}
|
|
96
|
+
</button>
|
|
97
|
+
</div>
|
|
98
|
+
</section>
|
|
99
|
+
</div>
|
|
100
|
+
</div>
|
|
101
|
+
|
|
102
|
+
{/* Generate token form */}
|
|
103
|
+
<div>
|
|
104
|
+
<h2 class="text-lg font-medium mb-4">
|
|
105
|
+
{t({
|
|
106
|
+
message: "API Tokens",
|
|
107
|
+
comment: "@context: Settings section heading",
|
|
108
|
+
})}
|
|
109
|
+
</h2>
|
|
110
|
+
<p class="text-sm text-muted-foreground mb-4">
|
|
111
|
+
{t({
|
|
112
|
+
message:
|
|
113
|
+
"Tokens let you access the API from scripts, shortcuts, and other tools without signing in.",
|
|
114
|
+
comment: "@context: API tokens description",
|
|
115
|
+
})}
|
|
116
|
+
</p>
|
|
117
|
+
<form
|
|
118
|
+
data-on:submit__prevent="@post('/settings/api-tokens')"
|
|
119
|
+
data-indicator="_tokenLoading"
|
|
120
|
+
class="flex gap-2 items-end"
|
|
121
|
+
>
|
|
122
|
+
<div class="field flex-1">
|
|
123
|
+
<label class="label" for="tokenName">
|
|
124
|
+
{t({
|
|
125
|
+
message: "Token name",
|
|
126
|
+
comment: "@context: API token name field label",
|
|
127
|
+
})}
|
|
128
|
+
</label>
|
|
129
|
+
<input
|
|
130
|
+
type="text"
|
|
131
|
+
id="tokenName"
|
|
132
|
+
data-bind="tokenName"
|
|
133
|
+
class="input"
|
|
134
|
+
placeholder={t({
|
|
135
|
+
message: "e.g. iOS Shortcuts",
|
|
136
|
+
comment: "@context: Placeholder for API token name input",
|
|
137
|
+
})}
|
|
138
|
+
required
|
|
139
|
+
/>
|
|
140
|
+
</div>
|
|
141
|
+
<button
|
|
142
|
+
type="submit"
|
|
143
|
+
class="btn"
|
|
144
|
+
data-attr:disabled="$_tokenLoading || !$tokenName.trim()"
|
|
145
|
+
>
|
|
146
|
+
{t({
|
|
147
|
+
message: "Generate Token",
|
|
148
|
+
comment: "@context: Button to create new API token",
|
|
149
|
+
})}
|
|
150
|
+
</button>
|
|
151
|
+
</form>
|
|
152
|
+
</div>
|
|
153
|
+
|
|
154
|
+
{/* Token list */}
|
|
155
|
+
{tokens.length > 0 && (
|
|
156
|
+
<div>
|
|
157
|
+
<h3 class="text-sm font-medium text-muted-foreground uppercase tracking-wide mb-2">
|
|
158
|
+
{t({
|
|
159
|
+
message: "Active Tokens",
|
|
160
|
+
comment: "@context: Heading for list of active API tokens",
|
|
161
|
+
})}
|
|
162
|
+
</h3>
|
|
163
|
+
<div class="border border-border rounded-lg px-4">
|
|
164
|
+
{tokens.map((token) => (
|
|
165
|
+
<TokenRow key={token.id} token={token} />
|
|
166
|
+
))}
|
|
167
|
+
</div>
|
|
168
|
+
</div>
|
|
169
|
+
)}
|
|
170
|
+
|
|
171
|
+
{/* Usage examples */}
|
|
172
|
+
<div>
|
|
173
|
+
<h3 class="text-sm font-medium text-muted-foreground uppercase tracking-wide mb-2">
|
|
174
|
+
{t({
|
|
175
|
+
message: "Usage",
|
|
176
|
+
comment: "@context: Heading for API token usage examples",
|
|
177
|
+
})}
|
|
178
|
+
</h3>
|
|
179
|
+
<div class="flex flex-col gap-3 text-sm">
|
|
180
|
+
<div>
|
|
181
|
+
<div class="text-muted-foreground mb-1">
|
|
182
|
+
{t({
|
|
183
|
+
message: "Create a post with curl:",
|
|
184
|
+
comment: "@context: Label for curl example",
|
|
185
|
+
})}
|
|
186
|
+
</div>
|
|
187
|
+
<pre class="bg-muted px-3 py-2 rounded text-xs overflow-x-auto">
|
|
188
|
+
<code>
|
|
189
|
+
{`curl -X POST ${siteUrl}/api/posts \\
|
|
190
|
+
-H "Authorization: Bearer jnt_YOUR_TOKEN" \\
|
|
191
|
+
-H "Content-Type: application/json" \\
|
|
192
|
+
-d '{"format":"note","body":"Hello from the API"}'`}
|
|
193
|
+
</code>
|
|
194
|
+
</pre>
|
|
195
|
+
</div>
|
|
196
|
+
<div>
|
|
197
|
+
<div class="text-muted-foreground mb-1">
|
|
198
|
+
{t({
|
|
199
|
+
message: "List posts:",
|
|
200
|
+
comment: "@context: Label for list posts curl example",
|
|
201
|
+
})}
|
|
202
|
+
</div>
|
|
203
|
+
<pre class="bg-muted px-3 py-2 rounded text-xs overflow-x-auto">
|
|
204
|
+
<code>
|
|
205
|
+
{`curl ${siteUrl}/api/posts \\
|
|
206
|
+
-H "Authorization: Bearer jnt_YOUR_TOKEN"`}
|
|
207
|
+
</code>
|
|
208
|
+
</pre>
|
|
209
|
+
</div>
|
|
210
|
+
<p class="text-muted-foreground">
|
|
211
|
+
<a
|
|
212
|
+
href={API_DOCS_URL}
|
|
213
|
+
target="_blank"
|
|
214
|
+
rel="noopener noreferrer"
|
|
215
|
+
class="underline hover:text-foreground transition-colors"
|
|
216
|
+
>
|
|
217
|
+
{t({
|
|
218
|
+
message: "API reference",
|
|
219
|
+
comment: "@context: Link to API documentation",
|
|
220
|
+
})}
|
|
221
|
+
</a>
|
|
222
|
+
{" — "}
|
|
223
|
+
{t({
|
|
224
|
+
message: "all available endpoints and request formats.",
|
|
225
|
+
comment: "@context: Description after API reference link",
|
|
226
|
+
})}
|
|
227
|
+
</p>
|
|
228
|
+
</div>
|
|
229
|
+
</div>
|
|
230
|
+
</div>
|
|
231
|
+
);
|
|
232
|
+
}
|