@jant/core 0.6.11 → 0.6.12
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/import-site.js +45 -20
- package/dist/app-B5P1b4TF.js +6 -0
- package/dist/{app-CpmficmQ.js → app-D-zAhayc.js} +331 -55
- package/dist/client/.vite/manifest.json +3 -3
- package/dist/client/_assets/client-BUxR-E8O.css +2 -0
- package/dist/client/_assets/{client-Dd9U383b.js → client-S1cdvulk.js} +1 -1
- package/dist/client/_assets/{client-auth-DkpSdIDz.js → client-auth-YW92ZH7u.js} +116 -55
- package/dist/{export-Ba7NJImL.js → export-Ckwh4BiE.js} +4 -4
- package/dist/{github-sync-BD4w2m8-.js → github-sync-BMmK3JZ0.js} +2 -2
- package/dist/{github-sync-Cb4_6_i7.js → github-sync-Dl9wQMar.js} +1 -1
- package/dist/index.js +3 -3
- package/dist/node.js +4 -4
- package/package.json +1 -1
- package/src/__tests__/import-site-command.test.ts +75 -0
- package/src/__tests__/vite-dev-plugins.test.ts +34 -0
- package/src/client/__tests__/compose-launch.test.ts +50 -1
- package/src/client/__tests__/compose-shortcuts.test.ts +74 -18
- package/src/client/components/__tests__/jant-command-palette.test.ts +63 -0
- package/src/client/components/__tests__/jant-compose-dialog.test.ts +33 -0
- package/src/client/components/__tests__/jant-compose-editor-rehost-notice.test.ts +20 -1
- package/src/client/components/__tests__/jant-nav-manager.test.ts +77 -1
- package/src/client/components/__tests__/jant-settings-avatar.test.ts +6 -0
- package/src/client/components/__tests__/jant-settings-general.test.ts +71 -0
- package/src/client/components/jant-command-palette.ts +67 -2
- package/src/client/components/jant-compose-dialog.ts +8 -2
- package/src/client/components/jant-nav-manager.ts +142 -2
- package/src/client/components/jant-settings-general.ts +56 -0
- package/src/client/components/nav-manager-types.ts +14 -0
- package/src/client/components/settings-types.ts +30 -0
- package/src/client/compose-launch.ts +30 -0
- package/src/client/compose-shortcuts.ts +46 -3
- package/src/client/tiptap/__tests__/rehost-images.test.ts +66 -13
- package/src/client/tiptap/rehost-images.ts +5 -1
- package/src/i18n/__tests__/fallback.test.ts +24 -0
- package/src/i18n/locales/public/zh-Hans.po +2 -2
- package/src/i18n/locales/public/zh-Hans.ts +1 -1
- package/src/i18n/locales/public/zh-Hant.po +2 -2
- package/src/i18n/locales/public/zh-Hant.ts +1 -1
- package/src/i18n/locales/settings/en.po +54 -0
- package/src/i18n/locales/settings/en.ts +1 -1
- package/src/i18n/locales/settings/zh-Hans.po +64 -10
- package/src/i18n/locales/settings/zh-Hans.ts +1 -1
- package/src/i18n/locales/settings/zh-Hant.po +56 -2
- package/src/i18n/locales/settings/zh-Hant.ts +1 -1
- package/src/lib/__tests__/feed.test.ts +96 -10
- package/src/lib/feed.ts +63 -3
- package/src/lib/post-display.ts +14 -8
- package/src/preset.css +60 -18
- package/src/routes/dash/settings.tsx +41 -2
- package/src/routes/pages/__tests__/draft-about-edit.test.ts +65 -0
- package/src/routes/pages/page.tsx +44 -12
- package/src/services/__tests__/about-page.test.ts +116 -0
- package/src/services/__tests__/navigation.test.ts +190 -1
- package/src/services/__tests__/post-timeline.test.ts +119 -2
- package/src/services/about-page.ts +143 -0
- package/src/services/export-theme/layouts/_default/rss.xml +2 -1
- package/src/services/export-theme/layouts/partials/feed-post-content.xml +25 -1
- package/src/services/export-theme/styles/main.css +46 -14
- package/src/services/index.ts +27 -17
- package/src/services/navigation.ts +155 -1
- package/src/services/post.ts +24 -7
- package/src/styles/components.css +20 -0
- package/src/styles/tokens.css +26 -8
- package/src/styles/ui.css +149 -7
- package/src/types/views.ts +13 -0
- package/src/ui/dash/appearance/NavigationContent.tsx +57 -0
- package/src/ui/dash/appearance/__tests__/NavigationContent.test.tsx +19 -0
- package/src/ui/dash/settings/GeneralContent.tsx +44 -0
- package/src/ui/dash/settings/__tests__/GeneralContent.test.tsx +8 -0
- package/src/ui/feed/__tests__/timeline-cards.test.ts +162 -1
- package/dist/app-DqKkZenB.js +0 -6
- package/dist/client/_assets/client-BhHHVvSY.css +0 -2
package/src/styles/ui.css
CHANGED
|
@@ -2237,13 +2237,13 @@
|
|
|
2237
2237
|
}
|
|
2238
2238
|
|
|
2239
2239
|
.post-header-block {
|
|
2240
|
-
margin-bottom: 1.
|
|
2240
|
+
margin-bottom: 1.7rem;
|
|
2241
2241
|
}
|
|
2242
2242
|
|
|
2243
2243
|
.post-header-block-detail {
|
|
2244
2244
|
display: flex;
|
|
2245
2245
|
flex-direction: column;
|
|
2246
|
-
gap: 0.
|
|
2246
|
+
gap: 0.7rem;
|
|
2247
2247
|
}
|
|
2248
2248
|
|
|
2249
2249
|
.post-header-block .feed-link-title,
|
|
@@ -2944,8 +2944,8 @@
|
|
|
2944
2944
|
padding-left: 0;
|
|
2945
2945
|
font-family: var(--font-serif);
|
|
2946
2946
|
color: var(--site-text-primary);
|
|
2947
|
-
font-size: var(--type-content-
|
|
2948
|
-
line-height: var(--type-
|
|
2947
|
+
font-size: var(--type-content-quote);
|
|
2948
|
+
line-height: var(--type-content-quote-leading);
|
|
2949
2949
|
white-space: pre-line;
|
|
2950
2950
|
text-wrap: pretty;
|
|
2951
2951
|
}
|
|
@@ -4535,6 +4535,7 @@
|
|
|
4535
4535
|
--compose-title-input-size: var(--type-content-subtitle);
|
|
4536
4536
|
--compose-title-input-leading: var(--type-display-leading);
|
|
4537
4537
|
--compose-quote-input-size: var(--type-content-subtitle);
|
|
4538
|
+
--compose-quote-input-leading: 1.32;
|
|
4538
4539
|
--compose-inline-input-size: var(--type-ui-input);
|
|
4539
4540
|
--compose-panel-label-size: var(--type-ui-control);
|
|
4540
4541
|
--compose-panel-hint-size: var(--type-ui-hint);
|
|
@@ -5201,7 +5202,7 @@
|
|
|
5201
5202
|
font-size: var(--compose-quote-input-size);
|
|
5202
5203
|
font-style: normal;
|
|
5203
5204
|
font-weight: var(--fw-regular);
|
|
5204
|
-
line-height:
|
|
5205
|
+
line-height: var(--compose-quote-input-leading);
|
|
5205
5206
|
letter-spacing: -0.02em;
|
|
5206
5207
|
min-height: 9rem;
|
|
5207
5208
|
padding-top: 0.5rem;
|
|
@@ -7428,6 +7429,53 @@
|
|
|
7428
7429
|
position: relative;
|
|
7429
7430
|
}
|
|
7430
7431
|
|
|
7432
|
+
.compose-reply-compose-layout {
|
|
7433
|
+
--compose-title-input-size: var(--type-content-subtitle);
|
|
7434
|
+
--compose-title-input-leading: 1.35;
|
|
7435
|
+
--compose-quote-input-size: calc(var(--type-content-body) * 1.06);
|
|
7436
|
+
--compose-quote-input-leading: 1.42;
|
|
7437
|
+
--compose-inline-input-size: var(--type-base);
|
|
7438
|
+
}
|
|
7439
|
+
|
|
7440
|
+
.compose-reply-compose-layout .compose-quote-wrap {
|
|
7441
|
+
margin-top: 0.4rem;
|
|
7442
|
+
padding: 24px 18px 18px;
|
|
7443
|
+
border-radius: 0.95rem;
|
|
7444
|
+
}
|
|
7445
|
+
|
|
7446
|
+
.compose-reply-compose-layout .compose-link-url-wrap {
|
|
7447
|
+
margin-top: 0.4rem;
|
|
7448
|
+
}
|
|
7449
|
+
|
|
7450
|
+
.compose-reply-compose-layout .compose-quote-mark {
|
|
7451
|
+
top: 0.58rem;
|
|
7452
|
+
left: 0.9rem;
|
|
7453
|
+
width: 1.55rem;
|
|
7454
|
+
}
|
|
7455
|
+
|
|
7456
|
+
.compose-reply-compose-layout .compose-quote-text {
|
|
7457
|
+
min-height: 6.5rem;
|
|
7458
|
+
padding-top: 0.35rem;
|
|
7459
|
+
}
|
|
7460
|
+
|
|
7461
|
+
@media (min-width: 700px) {
|
|
7462
|
+
.compose-reply-compose-layout .compose-quote-wrap {
|
|
7463
|
+
padding: 25px 20px 18px;
|
|
7464
|
+
border-radius: 1rem;
|
|
7465
|
+
}
|
|
7466
|
+
|
|
7467
|
+
.compose-reply-compose-layout .compose-quote-mark {
|
|
7468
|
+
top: 0.62rem;
|
|
7469
|
+
left: 1rem;
|
|
7470
|
+
width: 1.6rem;
|
|
7471
|
+
}
|
|
7472
|
+
|
|
7473
|
+
.compose-reply-compose-layout .compose-quote-text {
|
|
7474
|
+
min-height: 6.5rem;
|
|
7475
|
+
padding-top: 0.35rem;
|
|
7476
|
+
}
|
|
7477
|
+
}
|
|
7478
|
+
|
|
7431
7479
|
/* Continuous vertical line through the rail */
|
|
7432
7480
|
.compose-thread-layout::before {
|
|
7433
7481
|
content: "";
|
|
@@ -7490,6 +7538,78 @@
|
|
|
7490
7538
|
background: transparent;
|
|
7491
7539
|
}
|
|
7492
7540
|
|
|
7541
|
+
.compose-reply-compose-layout .compose-editor-row .compose-attachments-dock {
|
|
7542
|
+
--compose-reply-attachment-width: 96px;
|
|
7543
|
+
--compose-reply-attachment-height: 72px;
|
|
7544
|
+
|
|
7545
|
+
max-height: min(116px, 18dvh);
|
|
7546
|
+
padding-block: 4px 2px;
|
|
7547
|
+
}
|
|
7548
|
+
|
|
7549
|
+
.compose-reply-compose-layout .compose-editor-row .compose-attachments {
|
|
7550
|
+
gap: 6px;
|
|
7551
|
+
padding-block: 0;
|
|
7552
|
+
}
|
|
7553
|
+
|
|
7554
|
+
.compose-reply-compose-layout .compose-editor-row .compose-attachment {
|
|
7555
|
+
gap: 2px;
|
|
7556
|
+
max-width: none;
|
|
7557
|
+
}
|
|
7558
|
+
|
|
7559
|
+
.compose-reply-compose-layout
|
|
7560
|
+
.compose-editor-row
|
|
7561
|
+
.compose-attachment:only-child
|
|
7562
|
+
.compose-attachment-img,
|
|
7563
|
+
.compose-reply-compose-layout
|
|
7564
|
+
.compose-editor-row
|
|
7565
|
+
.compose-attachment:not(:only-child)
|
|
7566
|
+
.compose-attachment-img {
|
|
7567
|
+
width: var(--compose-reply-attachment-width);
|
|
7568
|
+
height: var(--compose-reply-attachment-height);
|
|
7569
|
+
min-width: 0;
|
|
7570
|
+
max-width: var(--compose-reply-attachment-width);
|
|
7571
|
+
max-height: var(--compose-reply-attachment-height);
|
|
7572
|
+
object-fit: cover;
|
|
7573
|
+
object-position: center;
|
|
7574
|
+
}
|
|
7575
|
+
|
|
7576
|
+
.compose-reply-compose-layout
|
|
7577
|
+
.compose-editor-row
|
|
7578
|
+
.compose-attachment-preview-fallback,
|
|
7579
|
+
.compose-reply-compose-layout
|
|
7580
|
+
.compose-editor-row
|
|
7581
|
+
.compose-attachment-processing {
|
|
7582
|
+
width: var(--compose-reply-attachment-width);
|
|
7583
|
+
height: var(--compose-reply-attachment-height);
|
|
7584
|
+
aspect-ratio: auto;
|
|
7585
|
+
}
|
|
7586
|
+
|
|
7587
|
+
.compose-reply-compose-layout
|
|
7588
|
+
.compose-editor-row
|
|
7589
|
+
.compose-attachment-file-card,
|
|
7590
|
+
.compose-reply-compose-layout
|
|
7591
|
+
.compose-editor-row
|
|
7592
|
+
.compose-attachment:not(:only-child)
|
|
7593
|
+
.compose-attachment-file-card {
|
|
7594
|
+
width: 112px;
|
|
7595
|
+
height: var(--compose-reply-attachment-height);
|
|
7596
|
+
gap: 4px;
|
|
7597
|
+
padding: 8px 10px;
|
|
7598
|
+
}
|
|
7599
|
+
|
|
7600
|
+
.compose-reply-compose-layout .compose-editor-row .compose-attachment-alt {
|
|
7601
|
+
padding: 1px 3px;
|
|
7602
|
+
font-size: var(--type-ui-micro);
|
|
7603
|
+
line-height: 1.2;
|
|
7604
|
+
}
|
|
7605
|
+
|
|
7606
|
+
.compose-reply-compose-layout .compose-editor-row .compose-attachment-remove {
|
|
7607
|
+
top: 4px;
|
|
7608
|
+
right: 4px;
|
|
7609
|
+
width: 20px;
|
|
7610
|
+
height: 20px;
|
|
7611
|
+
}
|
|
7612
|
+
|
|
7493
7613
|
/* Dot marker for thread positions */
|
|
7494
7614
|
.compose-thread-dot {
|
|
7495
7615
|
width: var(--site-thread-marker-size);
|
|
@@ -7977,6 +8097,10 @@
|
|
|
7977
8097
|
padding-top: 8px;
|
|
7978
8098
|
}
|
|
7979
8099
|
|
|
8100
|
+
.compose-reply-compose-layout .compose-thread-post-header + .compose-body {
|
|
8101
|
+
padding-top: 12px;
|
|
8102
|
+
}
|
|
8103
|
+
|
|
7980
8104
|
/* Thread format selector: pill-tag style (lighter than the main segmented) */
|
|
7981
8105
|
.compose-thread-segmented {
|
|
7982
8106
|
background: transparent;
|
|
@@ -8700,6 +8824,11 @@
|
|
|
8700
8824
|
font-family: var(--font-body);
|
|
8701
8825
|
}
|
|
8702
8826
|
|
|
8827
|
+
.compose-reply-compose-layout .compose-tiptap-body .tiptap {
|
|
8828
|
+
font-size: var(--type-content-body);
|
|
8829
|
+
line-height: var(--type-body-leading);
|
|
8830
|
+
}
|
|
8831
|
+
|
|
8703
8832
|
.compose-tiptap-thoughts .tiptap {
|
|
8704
8833
|
min-height: 72px;
|
|
8705
8834
|
}
|
|
@@ -8738,23 +8867,36 @@
|
|
|
8738
8867
|
|
|
8739
8868
|
.compose-tiptap-body .tiptap h2 {
|
|
8740
8869
|
font-size: var(--type-content-title);
|
|
8741
|
-
font-style: italic;
|
|
8742
8870
|
margin-top: 2.1rem;
|
|
8743
8871
|
margin-bottom: 1.4rem;
|
|
8744
8872
|
}
|
|
8745
8873
|
|
|
8746
8874
|
.compose-tiptap-body .tiptap h3 {
|
|
8747
8875
|
font-size: var(--type-content-subtitle);
|
|
8748
|
-
font-style: italic;
|
|
8749
8876
|
margin-top: 2rem;
|
|
8750
8877
|
margin-bottom: 1.4rem;
|
|
8751
8878
|
}
|
|
8752
8879
|
|
|
8880
|
+
.compose-reply-compose-layout
|
|
8881
|
+
.compose-tiptap-body
|
|
8882
|
+
.tiptap
|
|
8883
|
+
:is(h1, h2, h3, h4) {
|
|
8884
|
+
font-size: var(--type-secondary);
|
|
8885
|
+
line-height: 1.32;
|
|
8886
|
+
margin-top: 1.4rem;
|
|
8887
|
+
margin-bottom: 0.75rem;
|
|
8888
|
+
}
|
|
8889
|
+
|
|
8753
8890
|
.compose-tiptap-body .tiptap p {
|
|
8754
8891
|
margin-top: 1.4rem;
|
|
8755
8892
|
margin-bottom: 1.4rem;
|
|
8756
8893
|
}
|
|
8757
8894
|
|
|
8895
|
+
.compose-reply-compose-layout .compose-tiptap-body .tiptap p {
|
|
8896
|
+
margin-top: 1rem;
|
|
8897
|
+
margin-bottom: 1rem;
|
|
8898
|
+
}
|
|
8899
|
+
|
|
8758
8900
|
/* In inline-format compose (reply / edit-reply / thread — the editors wrapped
|
|
8759
8901
|
in .compose-editor-row), the format header sits right above the body, so the
|
|
8760
8902
|
first block's 1.4rem top margin reads as a gap between the format pills and
|
package/src/types/views.ts
CHANGED
|
@@ -171,6 +171,19 @@ export interface NavItemView {
|
|
|
171
171
|
freshAt?: number;
|
|
172
172
|
}
|
|
173
173
|
|
|
174
|
+
export type SuggestedNavLinkTargetType = "page" | "collection" | "archive";
|
|
175
|
+
|
|
176
|
+
export type SuggestedNavLinkNavItemType = "link" | "collection";
|
|
177
|
+
|
|
178
|
+
export interface SuggestedNavLink {
|
|
179
|
+
key: string;
|
|
180
|
+
label: string;
|
|
181
|
+
url: string;
|
|
182
|
+
targetType: SuggestedNavLinkTargetType;
|
|
183
|
+
navItemType: SuggestedNavLinkNavItemType;
|
|
184
|
+
collectionId?: string;
|
|
185
|
+
}
|
|
186
|
+
|
|
174
187
|
/**
|
|
175
188
|
* Search result from FTS5
|
|
176
189
|
*/
|
|
@@ -7,12 +7,14 @@ import { useLingui } from "../../../i18n/context.js";
|
|
|
7
7
|
import type {
|
|
8
8
|
CollectionsDirectoryData,
|
|
9
9
|
NavItem,
|
|
10
|
+
SuggestedNavLink,
|
|
10
11
|
SystemNavKey,
|
|
11
12
|
} from "../../../types.js";
|
|
12
13
|
import { SYSTEM_NAV_KEYS } from "../../../types.js";
|
|
13
14
|
import type {
|
|
14
15
|
NavManagerCollection,
|
|
15
16
|
NavManagerLabels,
|
|
17
|
+
NavManagerSuggestedLink,
|
|
16
18
|
SystemNavConfig,
|
|
17
19
|
} from "../../../client/components/nav-manager-types.js";
|
|
18
20
|
import { toPublicHref, toPublicPath } from "../../../lib/url.js";
|
|
@@ -30,12 +32,14 @@ import {
|
|
|
30
32
|
export function NavigationContent({
|
|
31
33
|
navItems,
|
|
32
34
|
directoryData,
|
|
35
|
+
suggestedLinks,
|
|
33
36
|
mainRssFeed,
|
|
34
37
|
siteName,
|
|
35
38
|
sitePathPrefix = "",
|
|
36
39
|
}: {
|
|
37
40
|
navItems: NavItem[];
|
|
38
41
|
directoryData: CollectionsDirectoryData;
|
|
42
|
+
suggestedLinks: SuggestedNavLink[];
|
|
39
43
|
mainRssFeed: string;
|
|
40
44
|
siteName: string;
|
|
41
45
|
sitePathPrefix?: string;
|
|
@@ -61,6 +65,27 @@ export function NavigationContent({
|
|
|
61
65
|
);
|
|
62
66
|
const moreLabel = i18n._(NAV_MORE_LABEL);
|
|
63
67
|
|
|
68
|
+
const suggestedTargetLabels = {
|
|
69
|
+
page: i18n._(
|
|
70
|
+
msg({
|
|
71
|
+
message: "Page",
|
|
72
|
+
comment: "@context: Suggested navigation link target type",
|
|
73
|
+
}),
|
|
74
|
+
),
|
|
75
|
+
collection: i18n._(
|
|
76
|
+
msg({
|
|
77
|
+
message: "Collection",
|
|
78
|
+
comment: "@context: Suggested navigation link target type",
|
|
79
|
+
}),
|
|
80
|
+
),
|
|
81
|
+
archive: i18n._(
|
|
82
|
+
msg({
|
|
83
|
+
message: "Archive",
|
|
84
|
+
comment: "@context: Suggested navigation link target type",
|
|
85
|
+
}),
|
|
86
|
+
),
|
|
87
|
+
} satisfies Record<SuggestedNavLink["targetType"], string>;
|
|
88
|
+
|
|
64
89
|
// Serialize nav items for the Lit component
|
|
65
90
|
const itemsData = navItems.map((item) => {
|
|
66
91
|
// System link URLs are always computed from constants, never from DB
|
|
@@ -110,6 +135,13 @@ export function NavigationContent({
|
|
|
110
135
|
return result;
|
|
111
136
|
})();
|
|
112
137
|
|
|
138
|
+
const suggestedLinksData: NavManagerSuggestedLink[] = suggestedLinks.map(
|
|
139
|
+
(link) => ({
|
|
140
|
+
...link,
|
|
141
|
+
targetLabel: suggestedTargetLabels[link.targetType],
|
|
142
|
+
}),
|
|
143
|
+
);
|
|
144
|
+
|
|
113
145
|
// Build system nav config array for the Lit component
|
|
114
146
|
const systemNavData: SystemNavConfig[] = (
|
|
115
147
|
Object.keys(SYSTEM_NAV_KEYS) as SystemNavKey[]
|
|
@@ -297,6 +329,30 @@ export function NavigationContent({
|
|
|
297
329
|
comment: "@context: Error toast when nav link fields are empty",
|
|
298
330
|
}),
|
|
299
331
|
),
|
|
332
|
+
suggestedLinks: i18n._(
|
|
333
|
+
msg({
|
|
334
|
+
message: "Suggested links",
|
|
335
|
+
comment: "@context: Section heading for suggested nav links",
|
|
336
|
+
}),
|
|
337
|
+
),
|
|
338
|
+
suggestedLinksDescription: i18n._(
|
|
339
|
+
msg({
|
|
340
|
+
message: "Add common destinations that already exist on your site.",
|
|
341
|
+
comment: "@context: Description for suggested nav links",
|
|
342
|
+
}),
|
|
343
|
+
),
|
|
344
|
+
addSuggestedLink: i18n._(
|
|
345
|
+
msg({
|
|
346
|
+
message: "Add",
|
|
347
|
+
comment: "@context: Button to add a suggested nav link",
|
|
348
|
+
}),
|
|
349
|
+
),
|
|
350
|
+
suggestedLinkAdded: i18n._(
|
|
351
|
+
msg({
|
|
352
|
+
message: "Link added to navigation.",
|
|
353
|
+
comment: "@context: Toast after adding a suggested nav link",
|
|
354
|
+
}),
|
|
355
|
+
),
|
|
300
356
|
collection: i18n._(
|
|
301
357
|
msg({
|
|
302
358
|
message: "collection",
|
|
@@ -356,6 +412,7 @@ export function NavigationContent({
|
|
|
356
412
|
labels={escapeJson(labels)}
|
|
357
413
|
system-nav-items={escapeJson(systemNavData)}
|
|
358
414
|
collections={escapeJson(collectionsData)}
|
|
415
|
+
suggested-links={escapeJson(suggestedLinksData)}
|
|
359
416
|
site-name={siteName}
|
|
360
417
|
>
|
|
361
418
|
{/* SSR fallback: static preview until JS hydrates */}
|
|
@@ -22,6 +22,7 @@ function renderNavigationContent(
|
|
|
22
22
|
NavigationContent({
|
|
23
23
|
navItems: [],
|
|
24
24
|
directoryData: { collections: [], items: [], directoryItems: [] },
|
|
25
|
+
suggestedLinks: [],
|
|
25
26
|
mainRssFeed: "latest",
|
|
26
27
|
siteName: "Test Site",
|
|
27
28
|
...props,
|
|
@@ -51,4 +52,22 @@ describe("NavigationContent", () => {
|
|
|
51
52
|
|
|
52
53
|
expect(html).toContain("Built-in links");
|
|
53
54
|
});
|
|
55
|
+
|
|
56
|
+
it("serializes suggested links with translated target labels", () => {
|
|
57
|
+
const html = renderNavigationContent({
|
|
58
|
+
suggestedLinks: [
|
|
59
|
+
{
|
|
60
|
+
key: "now",
|
|
61
|
+
label: "Now",
|
|
62
|
+
url: "/now",
|
|
63
|
+
targetType: "collection",
|
|
64
|
+
navItemType: "collection",
|
|
65
|
+
collectionId: "col_now",
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
expect(html).toContain("suggested-links=");
|
|
71
|
+
expect(html).toContain(""targetLabel":"Collection"");
|
|
72
|
+
});
|
|
54
73
|
});
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import { msg } from "@lingui/core/macro";
|
|
11
11
|
import { useLingui } from "../../../i18n/context.js";
|
|
12
12
|
import type { TimezoneEntry } from "../../../lib/timezones.js";
|
|
13
|
+
import type { AboutPageStatus } from "../../../services/about-page.js";
|
|
13
14
|
|
|
14
15
|
export function GeneralContent({
|
|
15
16
|
siteName,
|
|
@@ -30,6 +31,9 @@ export function GeneralContent({
|
|
|
30
31
|
noindex,
|
|
31
32
|
demoMode,
|
|
32
33
|
timezones,
|
|
34
|
+
aboutPage,
|
|
35
|
+
aboutEditUrl,
|
|
36
|
+
aboutCreateUrl,
|
|
33
37
|
}: {
|
|
34
38
|
siteName: string;
|
|
35
39
|
siteDescription: string;
|
|
@@ -49,6 +53,9 @@ export function GeneralContent({
|
|
|
49
53
|
noindex: boolean;
|
|
50
54
|
demoMode: boolean;
|
|
51
55
|
timezones: TimezoneEntry[];
|
|
56
|
+
aboutPage: AboutPageStatus;
|
|
57
|
+
aboutEditUrl: string;
|
|
58
|
+
aboutCreateUrl: string;
|
|
52
59
|
}) {
|
|
53
60
|
const { i18n } = useLingui();
|
|
54
61
|
|
|
@@ -65,6 +72,38 @@ export function GeneralContent({
|
|
|
65
72
|
comment: "@context: Settings subsection heading for basic site fields",
|
|
66
73
|
}),
|
|
67
74
|
),
|
|
75
|
+
aboutPage: i18n._(
|
|
76
|
+
msg({
|
|
77
|
+
message: "About page",
|
|
78
|
+
comment: "@context: Link label for editing or creating the About page",
|
|
79
|
+
}),
|
|
80
|
+
),
|
|
81
|
+
aboutPagePrompt: i18n._(
|
|
82
|
+
msg({
|
|
83
|
+
message: "Want to write a fuller introduction?",
|
|
84
|
+
comment:
|
|
85
|
+
"@context: Prompt shown below the short site description, before the About page action",
|
|
86
|
+
}),
|
|
87
|
+
),
|
|
88
|
+
createAboutPage: i18n._(
|
|
89
|
+
msg({
|
|
90
|
+
message: "Create About page",
|
|
91
|
+
comment: "@context: Button to create the standard About page",
|
|
92
|
+
}),
|
|
93
|
+
),
|
|
94
|
+
editAboutPage: i18n._(
|
|
95
|
+
msg({
|
|
96
|
+
message: "Edit About page",
|
|
97
|
+
comment: "@context: Link to edit the standard About page",
|
|
98
|
+
}),
|
|
99
|
+
),
|
|
100
|
+
aboutPageConflict: i18n._(
|
|
101
|
+
msg({
|
|
102
|
+
message:
|
|
103
|
+
"/about is already used. Rename that item before creating an About page.",
|
|
104
|
+
comment: "@context: Compact conflict message when /about is occupied",
|
|
105
|
+
}),
|
|
106
|
+
),
|
|
68
107
|
languageAndTime: i18n._(
|
|
69
108
|
msg({
|
|
70
109
|
message: "Language & Time",
|
|
@@ -369,6 +408,8 @@ export function GeneralContent({
|
|
|
369
408
|
{ value: "ko", label: "\uD55C\uAD6D\uC5B4 (Korean)" },
|
|
370
409
|
]).replace(/</g, "\\u003c");
|
|
371
410
|
|
|
411
|
+
const aboutPageJson = JSON.stringify(aboutPage).replace(/</g, "\\u003c");
|
|
412
|
+
|
|
372
413
|
const initialData = JSON.stringify({
|
|
373
414
|
siteName,
|
|
374
415
|
siteDescription,
|
|
@@ -397,6 +438,9 @@ export function GeneralContent({
|
|
|
397
438
|
featured-feed-url={featuredFeedUrl}
|
|
398
439
|
archive-feed-url={archiveFeedUrl}
|
|
399
440
|
demo-mode={demoMode || undefined}
|
|
441
|
+
about-page={aboutPageJson}
|
|
442
|
+
about-edit-url={aboutEditUrl}
|
|
443
|
+
about-create-url={aboutCreateUrl}
|
|
400
444
|
>
|
|
401
445
|
{/* SSR fallback skeleton */}
|
|
402
446
|
<div>
|
|
@@ -37,6 +37,8 @@ function createProps(
|
|
|
37
37
|
siteName: "My Blog",
|
|
38
38
|
siteDescription: "A test blog",
|
|
39
39
|
siteLanguage: "en",
|
|
40
|
+
dashboardLanguage: "en",
|
|
41
|
+
cjkSerifFont: "off",
|
|
40
42
|
siteNameFallback: "Fallback Name",
|
|
41
43
|
siteDescriptionFallback: "Fallback Description",
|
|
42
44
|
mainRssFeed: "featured" as const,
|
|
@@ -49,6 +51,12 @@ function createProps(
|
|
|
49
51
|
showJantBrandingOnHome: false,
|
|
50
52
|
noindex: false,
|
|
51
53
|
demoMode,
|
|
54
|
+
aboutPage: {
|
|
55
|
+
state: "missing" as const,
|
|
56
|
+
path: "/about" as const,
|
|
57
|
+
},
|
|
58
|
+
aboutEditUrl: "/about?edit=1",
|
|
59
|
+
aboutCreateUrl: "/settings/general/about-page",
|
|
52
60
|
timezones: [
|
|
53
61
|
{
|
|
54
62
|
value: "UTC",
|
|
@@ -104,13 +104,43 @@ describe("timeline cards", () => {
|
|
|
104
104
|
});
|
|
105
105
|
|
|
106
106
|
it("preserves authored line breaks in quote cards", () => {
|
|
107
|
+
const tokens = readFileSync(
|
|
108
|
+
new URL("../../../styles/tokens.css", import.meta.url),
|
|
109
|
+
"utf8",
|
|
110
|
+
);
|
|
107
111
|
const css = readFileSync(
|
|
108
112
|
new URL("../../../styles/ui.css", import.meta.url),
|
|
109
113
|
"utf8",
|
|
110
114
|
);
|
|
115
|
+
const exportCss = readFileSync(
|
|
116
|
+
new URL(
|
|
117
|
+
"../../../services/export-theme/styles/main.css",
|
|
118
|
+
import.meta.url,
|
|
119
|
+
),
|
|
120
|
+
"utf8",
|
|
121
|
+
);
|
|
111
122
|
|
|
123
|
+
expect(tokens).toMatch(
|
|
124
|
+
/--feed-note-title-size:\s*calc\(var\(--type-content-body\) \* 1\.36\);/,
|
|
125
|
+
);
|
|
126
|
+
expect(tokens).toMatch(
|
|
127
|
+
/--type-content-quote:\s*calc\(var\(--type-content-body\) \* 1\.16\);/,
|
|
128
|
+
);
|
|
129
|
+
expect(tokens).toMatch(/--type-content-quote-leading:\s*1\.4;/);
|
|
130
|
+
expect(css).toMatch(
|
|
131
|
+
/\.feed-note-title\s*\{[\s\S]*font-size:\s*var\(--feed-note-title-size\);/,
|
|
132
|
+
);
|
|
133
|
+
expect(css).toMatch(
|
|
134
|
+
/\.feed-link-title\s*\{[\s\S]*font-size:\s*var\(--feed-note-title-size\);/,
|
|
135
|
+
);
|
|
136
|
+
expect(exportCss).toMatch(
|
|
137
|
+
/\.post-card-title\s*\{[\s\S]*font-size:\s*var\(--feed-note-title-size\);/,
|
|
138
|
+
);
|
|
112
139
|
expect(css).toMatch(
|
|
113
|
-
/\.feed-quote-content\s*\{[\s\S]*white-space:\s*pre-line;/,
|
|
140
|
+
/\.feed-quote-content\s*\{[\s\S]*font-size:\s*var\(--type-content-quote\);[\s\S]*line-height:\s*var\(--type-content-quote-leading\);[\s\S]*white-space:\s*pre-line;/,
|
|
141
|
+
);
|
|
142
|
+
expect(exportCss).toMatch(
|
|
143
|
+
/\.post-card-quote-content\s*\{[\s\S]*font-size:\s*var\(--type-content-quote\);[\s\S]*line-height:\s*var\(--type-content-quote-leading\);[\s\S]*white-space:\s*pre-line;/,
|
|
114
144
|
);
|
|
115
145
|
});
|
|
116
146
|
|
|
@@ -136,6 +166,123 @@ describe("timeline cards", () => {
|
|
|
136
166
|
expect(css).toContain("margin-bottom: 0;");
|
|
137
167
|
});
|
|
138
168
|
|
|
169
|
+
it("keeps prose list rhythm compact in live and exported reading", () => {
|
|
170
|
+
const presetCss = readFileSync(
|
|
171
|
+
new URL("../../../preset.css", import.meta.url),
|
|
172
|
+
"utf8",
|
|
173
|
+
);
|
|
174
|
+
const exportCss = readFileSync(
|
|
175
|
+
new URL(
|
|
176
|
+
"../../../services/export-theme/styles/main.css",
|
|
177
|
+
import.meta.url,
|
|
178
|
+
),
|
|
179
|
+
"utf8",
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
expect(presetCss).toMatch(
|
|
183
|
+
/:where\(ul,\s*ol\)\s*\{[\s\S]*padding-left:\s*1\.4em;/,
|
|
184
|
+
);
|
|
185
|
+
expect(exportCss).toMatch(
|
|
186
|
+
/ul,\s*[\s\S]*ol\s*\{[\s\S]*padding-left:\s*1\.4em;/,
|
|
187
|
+
);
|
|
188
|
+
expect(presetCss).toMatch(
|
|
189
|
+
/:where\(li,\s*dt\)\s*\{[\s\S]*margin-top:\s*0\.65em;[\s\S]*margin-bottom:\s*0\.65em;/,
|
|
190
|
+
);
|
|
191
|
+
expect(exportCss).toMatch(
|
|
192
|
+
/li\s*\{[\s\S]*margin-top:\s*0\.65em;[\s\S]*margin-bottom:\s*0\.65em;/,
|
|
193
|
+
);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it("uses dedicated markdown code surfaces in live and exported prose", () => {
|
|
197
|
+
const presetCss = readFileSync(
|
|
198
|
+
new URL("../../../preset.css", import.meta.url),
|
|
199
|
+
"utf8",
|
|
200
|
+
);
|
|
201
|
+
const tokenCss = readFileSync(
|
|
202
|
+
new URL("../../../styles/tokens.css", import.meta.url),
|
|
203
|
+
"utf8",
|
|
204
|
+
);
|
|
205
|
+
const exportCss = readFileSync(
|
|
206
|
+
new URL(
|
|
207
|
+
"../../../services/export-theme/styles/main.css",
|
|
208
|
+
import.meta.url,
|
|
209
|
+
),
|
|
210
|
+
"utf8",
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
expect(tokenCss).toContain("--site-code-bg:");
|
|
214
|
+
expect(tokenCss).toContain("--site-code-block-bg:");
|
|
215
|
+
expect(tokenCss).toContain(
|
|
216
|
+
"--type-code: calc(var(--type-body-size) * 0.94);",
|
|
217
|
+
);
|
|
218
|
+
expect(tokenCss).toContain(
|
|
219
|
+
"--type-code-block: calc(var(--type-body-size) * 0.9);",
|
|
220
|
+
);
|
|
221
|
+
expect(presetCss).toContain("background-color: var(--site-code-bg);");
|
|
222
|
+
expect(presetCss).toContain("background-color: var(--site-code-block-bg);");
|
|
223
|
+
expect(exportCss).toContain("background-color: var(--site-code-bg);");
|
|
224
|
+
expect(exportCss).toContain("background-color: var(--site-code-block-bg);");
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
it("keeps markdown h2 and h3 headings upright across surfaces", () => {
|
|
228
|
+
const presetCss = readFileSync(
|
|
229
|
+
new URL("../../../preset.css", import.meta.url),
|
|
230
|
+
"utf8",
|
|
231
|
+
);
|
|
232
|
+
const uiCss = readFileSync(
|
|
233
|
+
new URL("../../../styles/ui.css", import.meta.url),
|
|
234
|
+
"utf8",
|
|
235
|
+
);
|
|
236
|
+
const exportCss = readFileSync(
|
|
237
|
+
new URL(
|
|
238
|
+
"../../../services/export-theme/styles/main.css",
|
|
239
|
+
import.meta.url,
|
|
240
|
+
),
|
|
241
|
+
"utf8",
|
|
242
|
+
);
|
|
243
|
+
|
|
244
|
+
expect(presetCss).not.toMatch(
|
|
245
|
+
/:where\(h[23]\)\s*\{[^}]*font-style:\s*italic;/,
|
|
246
|
+
);
|
|
247
|
+
expect(uiCss).not.toMatch(
|
|
248
|
+
/\.compose-tiptap-body\s+\.tiptap\s+h[23]\s*\{[^}]*font-style:\s*italic;/,
|
|
249
|
+
);
|
|
250
|
+
expect(exportCss).not.toMatch(/h[23]\s*\{[^}]*font-style:\s*italic;/);
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
it("compacts authored headings inside feed body prose", () => {
|
|
254
|
+
const presetCss = readFileSync(
|
|
255
|
+
new URL("../../../preset.css", import.meta.url),
|
|
256
|
+
"utf8",
|
|
257
|
+
);
|
|
258
|
+
const exportCss = readFileSync(
|
|
259
|
+
new URL(
|
|
260
|
+
"../../../services/export-theme/styles/main.css",
|
|
261
|
+
import.meta.url,
|
|
262
|
+
),
|
|
263
|
+
"utf8",
|
|
264
|
+
);
|
|
265
|
+
|
|
266
|
+
expect(presetCss).toContain(
|
|
267
|
+
'[data-post]:not([data-page="post"]) [data-post-body].prose',
|
|
268
|
+
);
|
|
269
|
+
expect(presetCss).toMatch(
|
|
270
|
+
/:where\(h1,\s*h2\)\s*\{[\s\S]*font-size:\s*calc\(var\(--type-content-body\) \* 1\.12\);/,
|
|
271
|
+
);
|
|
272
|
+
expect(presetCss).toMatch(
|
|
273
|
+
/:where\(h3,\s*h4\)\s*\{[\s\S]*font-size:\s*var\(--type-content-body\);/,
|
|
274
|
+
);
|
|
275
|
+
expect(presetCss).toMatch(
|
|
276
|
+
/:where\(h5,\s*h6\)\s*\{[\s\S]*font-size:\s*var\(--type-secondary\);/,
|
|
277
|
+
);
|
|
278
|
+
expect(exportCss).toMatch(
|
|
279
|
+
/\.post-card > \.post-card-summary :is\(h1,\s*h2\),/,
|
|
280
|
+
);
|
|
281
|
+
expect(exportCss).toMatch(
|
|
282
|
+
/\.post-card > \.post-card-summary :is\(h5,\s*h6\),/,
|
|
283
|
+
);
|
|
284
|
+
});
|
|
285
|
+
|
|
139
286
|
it("keeps quote footers on the shared card spacing baseline", () => {
|
|
140
287
|
const css = readFileSync(
|
|
141
288
|
new URL("../../../styles/ui.css", import.meta.url),
|
|
@@ -266,6 +413,20 @@ describe("timeline cards", () => {
|
|
|
266
413
|
);
|
|
267
414
|
});
|
|
268
415
|
|
|
416
|
+
it("keeps titled detail headers spaced as one reading group", () => {
|
|
417
|
+
const css = readFileSync(
|
|
418
|
+
new URL("../../../styles/ui.css", import.meta.url),
|
|
419
|
+
"utf8",
|
|
420
|
+
);
|
|
421
|
+
|
|
422
|
+
expect(css).toMatch(
|
|
423
|
+
/\.post-header-block\s*\{[\s\S]*margin-bottom:\s*1\.7rem;/,
|
|
424
|
+
);
|
|
425
|
+
expect(css).toMatch(
|
|
426
|
+
/\.post-header-block-detail\s*\{[\s\S]*gap:\s*0\.7rem;/,
|
|
427
|
+
);
|
|
428
|
+
});
|
|
429
|
+
|
|
269
430
|
it("renders titled note feed summaries as secondary prose without shrinking detail reading", () => {
|
|
270
431
|
const post = createPostView({
|
|
271
432
|
format: "note",
|