@jant/core 0.6.11 → 0.6.13
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 +47 -30
- package/bin/commands/migrate-text-attachments.js +1 -3
- package/bin/commands/site/export.js +1 -3
- package/bin/commands/telegram/register-webhooks.js +9 -3
- package/bin/lib/site-snapshot.js +1 -5
- package/dist/app-CWJFPjuR.js +6 -0
- package/dist/{app-CpmficmQ.js → app-HnzmsaqU.js} +721 -473
- package/dist/client/.vite/manifest.json +3 -3
- package/dist/client/_assets/{client-Dd9U383b.js → client-3_OaxhTs.js} +1 -1
- package/dist/client/_assets/client-DCrcHVJl.css +2 -0
- package/dist/client/_assets/{client-auth-DkpSdIDz.js → client-auth-TsYq90xm.js} +625 -561
- package/dist/{export-Ba7NJImL.js → export-Dfbq9aot.js} +12 -15
- package/dist/{github-sync-Cb4_6_i7.js → github-sync--PSoE-Ne.js} +1 -1
- package/dist/{github-sync-BD4w2m8-.js → github-sync-BGTLx8Mf.js} +2 -2
- package/dist/index.js +3 -3
- package/dist/node.js +4 -4
- package/package.json +1 -1
- package/src/__tests__/export-service.test.ts +1 -2
- package/src/__tests__/helpers/app.ts +3 -0
- package/src/__tests__/helpers/export-fixtures.ts +0 -1
- package/src/__tests__/import-site-command.test.ts +76 -2
- 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/__tests__/site-header-nav.test.ts +36 -2
- package/src/client/components/__tests__/compose-title-from-heading.test.ts +57 -0
- 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-compose-editor.test.ts +94 -2
- package/src/client/components/__tests__/jant-nav-manager.test.ts +181 -20
- package/src/client/components/__tests__/jant-settings-avatar.test.ts +6 -0
- package/src/client/components/__tests__/jant-settings-general.test.ts +72 -0
- package/src/client/components/compose-title-from-heading.ts +66 -0
- package/src/client/components/compose-types.ts +8 -0
- package/src/client/components/inline-image-issues.ts +17 -0
- package/src/client/components/jant-command-palette.ts +67 -2
- package/src/client/components/jant-compose-dialog.ts +10 -2
- package/src/client/components/jant-compose-editor.ts +78 -1
- package/src/client/components/jant-compose-fullscreen.ts +2 -0
- package/src/client/components/jant-nav-manager.ts +382 -152
- package/src/client/components/jant-settings-general.ts +56 -0
- package/src/client/components/nav-manager-types.ts +14 -10
- 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/site-header-nav.d.ts +1 -0
- package/src/client/site-header-nav.js +105 -47
- package/src/client/tiptap/__tests__/block-insertion.test.ts +35 -0
- package/src/client/tiptap/__tests__/image-input-rules.test.ts +117 -0
- package/src/client/tiptap/__tests__/rehost-images.test.ts +66 -13
- package/src/client/tiptap/create-editor.ts +7 -0
- package/src/client/tiptap/extensions.ts +7 -2
- package/src/client/tiptap/image-input-rules.ts +48 -0
- package/src/client/tiptap/image-node.ts +189 -7
- package/src/client/tiptap/rehost-images.ts +5 -1
- package/src/client-auth.ts +0 -1
- package/src/i18n/__tests__/fallback.test.ts +24 -0
- package/src/i18n/locales/public/en.po +20 -4
- package/src/i18n/locales/public/en.ts +1 -1
- package/src/i18n/locales/public/zh-Hans.po +22 -6
- package/src/i18n/locales/public/zh-Hans.ts +1 -1
- package/src/i18n/locales/public/zh-Hant.po +22 -6
- package/src/i18n/locales/public/zh-Hant.ts +1 -1
- package/src/i18n/locales/settings/en.po +77 -20
- package/src/i18n/locales/settings/en.ts +1 -1
- package/src/i18n/locales/settings/zh-Hans.po +85 -28
- package/src/i18n/locales/settings/zh-Hans.ts +1 -1
- package/src/i18n/locales/settings/zh-Hant.po +79 -22
- package/src/i18n/locales/settings/zh-Hant.ts +1 -1
- package/src/lib/__tests__/feed.test.ts +96 -10
- package/src/lib/__tests__/navigation.test.ts +4 -37
- package/src/lib/__tests__/resolve-config.test.ts +6 -1
- package/src/lib/__tests__/view.test.ts +6 -30
- package/src/lib/feed.ts +63 -3
- package/src/lib/github-sync-site-config.ts +0 -2
- package/src/lib/hugo-markdown.ts +1 -1
- package/src/lib/navigation.ts +5 -22
- package/src/lib/post-display.ts +14 -8
- package/src/lib/resolve-config.ts +9 -2
- package/src/lib/site-header-fragment.tsx +37 -0
- package/src/lib/view.ts +2 -7
- package/src/node/__tests__/cli-site-snapshot.test.ts +45 -45
- package/src/preset.css +60 -18
- package/src/routes/api/__tests__/nav-items.test.ts +26 -0
- package/src/routes/api/export.ts +0 -2
- package/src/routes/api/internal/__tests__/uploads.test.ts +9 -13
- package/src/routes/api/internal/sites.ts +5 -5
- package/src/routes/api/nav-items.ts +25 -4
- package/src/routes/dash/settings.tsx +48 -3
- package/src/routes/feed/__tests__/feed.test.ts +42 -0
- package/src/routes/feed/__tests__/sitemap.test.ts +2 -13
- package/src/routes/feed/feed.ts +1 -0
- package/src/routes/feed/sitemap.ts +2 -6
- package/src/routes/pages/__tests__/draft-about-edit.test.ts +65 -0
- package/src/routes/pages/archive.tsx +1 -0
- package/src/routes/pages/collection.tsx +1 -0
- package/src/routes/pages/featured.tsx +0 -4
- package/src/routes/pages/home.tsx +4 -51
- package/src/routes/pages/latest.tsx +2 -51
- package/src/routes/pages/page.tsx +44 -12
- package/src/services/__tests__/about-page.test.ts +116 -0
- package/src/services/__tests__/github-app-installations.test.ts +10 -10
- package/src/services/__tests__/navigation.test.ts +231 -1
- package/src/services/__tests__/post-timeline.test.ts +175 -2
- package/src/services/__tests__/post.test.ts +33 -0
- package/src/services/__tests__/settings.test.ts +5 -20
- package/src/services/about-page.ts +143 -0
- package/src/services/export-theme/assets/client-site.js +10 -10
- package/src/services/export-theme/layouts/_default/rss.xml +7 -16
- package/src/services/export-theme/layouts/index.html +26 -39
- 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/export.ts +5 -10
- package/src/services/github-app-installations.ts +1 -3
- package/src/services/index.ts +27 -17
- package/src/services/navigation.ts +186 -1
- package/src/services/post.ts +103 -63
- package/src/services/settings.ts +0 -10
- package/src/services/site-admin.ts +0 -2
- package/src/services/upload-session.ts +4 -18
- package/src/styles/components.css +20 -0
- package/src/styles/tokens.css +28 -10
- package/src/styles/ui.css +349 -13
- package/src/types/bindings.ts +0 -1
- package/src/types/config.ts +3 -7
- package/src/types/views.ts +13 -0
- package/src/ui/compose/ComposeDialog.tsx +28 -0
- package/src/ui/dash/appearance/NavigationContent.tsx +58 -1
- package/src/ui/dash/appearance/__tests__/NavigationContent.test.tsx +19 -0
- package/src/ui/dash/settings/AccountMenuContent.tsx +2 -2
- package/src/ui/dash/settings/GeneralContent.tsx +44 -0
- package/src/ui/dash/settings/SettingsRootContent.tsx +3 -3
- package/src/ui/dash/settings/__tests__/GeneralContent.test.tsx +8 -0
- package/src/ui/feed/__tests__/timeline-cards.test.ts +162 -1
- package/src/ui/layouts/SiteLayout.tsx +249 -216
- package/dist/app-DqKkZenB.js +0 -6
- package/dist/client/_assets/client-BhHHVvSY.css +0 -2
- package/src/client/nav-manager-bridge.ts +0 -54
package/src/services/post.ts
CHANGED
|
@@ -119,6 +119,8 @@ export interface PostFilters {
|
|
|
119
119
|
hasReplies?: boolean;
|
|
120
120
|
/** Explicit result sort order */
|
|
121
121
|
sortOrder?: SortOrder;
|
|
122
|
+
/** Ignore global pinned ordering when sorting subscription/feed results. */
|
|
123
|
+
ignorePinnedSort?: boolean;
|
|
122
124
|
limit?: number;
|
|
123
125
|
cursor?: string;
|
|
124
126
|
offset?: number; // offset for page-based pagination
|
|
@@ -137,6 +139,11 @@ interface ThreadRootPageOptions {
|
|
|
137
139
|
offset?: number;
|
|
138
140
|
}
|
|
139
141
|
|
|
142
|
+
interface CollectionFeedEntryOptions extends ThreadRootPageOptions {
|
|
143
|
+
/** Ignore per-collection pinned ordering when sorting collection feeds. */
|
|
144
|
+
ignoreCollectionPinnedSort?: boolean;
|
|
145
|
+
}
|
|
146
|
+
|
|
140
147
|
interface CollectionThreadRootPageOptions extends ThreadRootPageOptions {
|
|
141
148
|
sortOrder?: CollectionSortOrder;
|
|
142
149
|
}
|
|
@@ -314,12 +321,12 @@ export interface PostService {
|
|
|
314
321
|
/** List collection feed entries ordered by latest added-at timestamp */
|
|
315
322
|
listCollectionFeedEntries(
|
|
316
323
|
collectionId: string,
|
|
317
|
-
options?:
|
|
324
|
+
options?: CollectionFeedEntryOptions,
|
|
318
325
|
): Promise<CollectionFeedEntry[]>;
|
|
319
326
|
/** List collection feed entries for a union of collections */
|
|
320
327
|
listCollectionFeedEntriesForCollections(
|
|
321
328
|
collectionIds: string[],
|
|
322
|
-
options?:
|
|
329
|
+
options?: CollectionFeedEntryOptions,
|
|
323
330
|
): Promise<CollectionFeedEntry[]>;
|
|
324
331
|
/** Fetch all published, non-deleted posts for each requested thread root */
|
|
325
332
|
getPublishedThreads(rootIds: string[]): Promise<Map<string, Post[]>>;
|
|
@@ -902,76 +909,90 @@ export function createPostService(
|
|
|
902
909
|
const cursorSortTimestamp = getCursorSortTimestamp(cursorPost);
|
|
903
910
|
const cursorRatingPresence = cursorPost.rating === null ? 0 : 1;
|
|
904
911
|
const cursorRating = cursorPost.rating ?? -1;
|
|
912
|
+
const withPinnedSortKey = (
|
|
913
|
+
keys: [CursorSortKey, ...CursorSortKey[]],
|
|
914
|
+
): [CursorSortKey, ...CursorSortKey[]] =>
|
|
915
|
+
filters.ignorePinnedSort
|
|
916
|
+
? keys
|
|
917
|
+
: [
|
|
918
|
+
{ direction: "desc", expr: pinnedSortExpr, value: cursorPinnedAt },
|
|
919
|
+
...keys,
|
|
920
|
+
];
|
|
905
921
|
|
|
906
922
|
if (filters.featured || filters.sortOrder === undefined) {
|
|
907
923
|
if (filters.featured) {
|
|
908
|
-
return buildLexicographicCursorCondition(
|
|
909
|
-
|
|
924
|
+
return buildLexicographicCursorCondition(
|
|
925
|
+
withPinnedSortKey([
|
|
926
|
+
{
|
|
927
|
+
direction: "desc",
|
|
928
|
+
expr: featuredSortExpr,
|
|
929
|
+
value: cursorFeaturedAt,
|
|
930
|
+
},
|
|
931
|
+
{ direction: "desc", expr: posts.id, value: cursorPost.id },
|
|
932
|
+
]),
|
|
933
|
+
);
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
return buildLexicographicCursorCondition(
|
|
937
|
+
withPinnedSortKey([
|
|
910
938
|
{
|
|
911
939
|
direction: "desc",
|
|
912
|
-
expr:
|
|
913
|
-
value:
|
|
940
|
+
expr: sortTimestampSortExpr,
|
|
941
|
+
value: cursorSortTimestamp,
|
|
914
942
|
},
|
|
915
943
|
{ direction: "desc", expr: posts.id, value: cursorPost.id },
|
|
916
|
-
])
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
return buildLexicographicCursorCondition([
|
|
920
|
-
{ direction: "desc", expr: pinnedSortExpr, value: cursorPinnedAt },
|
|
921
|
-
{
|
|
922
|
-
direction: "desc",
|
|
923
|
-
expr: sortTimestampSortExpr,
|
|
924
|
-
value: cursorSortTimestamp,
|
|
925
|
-
},
|
|
926
|
-
{ direction: "desc", expr: posts.id, value: cursorPost.id },
|
|
927
|
-
]);
|
|
944
|
+
]),
|
|
945
|
+
);
|
|
928
946
|
}
|
|
929
947
|
|
|
930
948
|
if (filters.sortOrder === "oldest") {
|
|
931
|
-
return buildLexicographicCursorCondition(
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
949
|
+
return buildLexicographicCursorCondition(
|
|
950
|
+
withPinnedSortKey([
|
|
951
|
+
{
|
|
952
|
+
direction: "asc",
|
|
953
|
+
expr: sortTimestampSortExpr,
|
|
954
|
+
value: cursorSortTimestamp,
|
|
955
|
+
},
|
|
956
|
+
{ direction: "asc", expr: posts.id, value: cursorPost.id },
|
|
957
|
+
]),
|
|
958
|
+
);
|
|
940
959
|
}
|
|
941
960
|
|
|
942
961
|
if (filters.sortOrder === "rating_desc") {
|
|
943
|
-
return buildLexicographicCursorCondition(
|
|
944
|
-
|
|
962
|
+
return buildLexicographicCursorCondition(
|
|
963
|
+
withPinnedSortKey([
|
|
964
|
+
{
|
|
965
|
+
direction: "desc",
|
|
966
|
+
expr: ratingPresenceExpr,
|
|
967
|
+
value: cursorRatingPresence,
|
|
968
|
+
},
|
|
969
|
+
{ direction: "desc", expr: ratingSortExpr, value: cursorRating },
|
|
970
|
+
{
|
|
971
|
+
direction: "desc",
|
|
972
|
+
expr: sortTimestampSortExpr,
|
|
973
|
+
value: cursorSortTimestamp,
|
|
974
|
+
},
|
|
975
|
+
{ direction: "desc", expr: posts.id, value: cursorPost.id },
|
|
976
|
+
]),
|
|
977
|
+
);
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
return buildLexicographicCursorCondition(
|
|
981
|
+
withPinnedSortKey([
|
|
945
982
|
{
|
|
946
983
|
direction: "desc",
|
|
947
984
|
expr: ratingPresenceExpr,
|
|
948
985
|
value: cursorRatingPresence,
|
|
949
986
|
},
|
|
950
|
-
{ direction: "
|
|
987
|
+
{ direction: "asc", expr: ratingSortExpr, value: cursorRating },
|
|
951
988
|
{
|
|
952
989
|
direction: "desc",
|
|
953
990
|
expr: sortTimestampSortExpr,
|
|
954
991
|
value: cursorSortTimestamp,
|
|
955
992
|
},
|
|
956
993
|
{ direction: "desc", expr: posts.id, value: cursorPost.id },
|
|
957
|
-
])
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
return buildLexicographicCursorCondition([
|
|
961
|
-
{ direction: "desc", expr: pinnedSortExpr, value: cursorPinnedAt },
|
|
962
|
-
{
|
|
963
|
-
direction: "desc",
|
|
964
|
-
expr: ratingPresenceExpr,
|
|
965
|
-
value: cursorRatingPresence,
|
|
966
|
-
},
|
|
967
|
-
{ direction: "asc", expr: ratingSortExpr, value: cursorRating },
|
|
968
|
-
{
|
|
969
|
-
direction: "desc",
|
|
970
|
-
expr: sortTimestampSortExpr,
|
|
971
|
-
value: cursorSortTimestamp,
|
|
972
|
-
},
|
|
973
|
-
{ direction: "desc", expr: posts.id, value: cursorPost.id },
|
|
974
|
-
]);
|
|
994
|
+
]),
|
|
995
|
+
);
|
|
975
996
|
}
|
|
976
997
|
|
|
977
998
|
function toPost(
|
|
@@ -1105,6 +1126,21 @@ export function createPostService(
|
|
|
1105
1126
|
return conditions;
|
|
1106
1127
|
}
|
|
1107
1128
|
|
|
1129
|
+
function buildCollectionThreadActivityExpr(alias: string) {
|
|
1130
|
+
return sql<number>`MAX(
|
|
1131
|
+
COALESCE(
|
|
1132
|
+
(
|
|
1133
|
+
SELECT root.last_activity_at
|
|
1134
|
+
FROM post AS root
|
|
1135
|
+
WHERE root.site_id = ${siteId}
|
|
1136
|
+
AND root.id = ${posts.threadId}
|
|
1137
|
+
),
|
|
1138
|
+
${posts.publishedAt},
|
|
1139
|
+
${posts.updatedAt}
|
|
1140
|
+
)
|
|
1141
|
+
)`.as(alias);
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1108
1144
|
function isMediaAttachmentInput(
|
|
1109
1145
|
attachment: PostAttachmentInput,
|
|
1110
1146
|
): attachment is Extract<PostAttachmentInput, { type: "media" }> {
|
|
@@ -1313,6 +1349,9 @@ export function createPostService(
|
|
|
1313
1349
|
const pinnedSortExpr = sql<number>`coalesce(${posts.pinnedAt}, -1)`;
|
|
1314
1350
|
const featuredSortExpr = sql<number>`coalesce(${posts.featuredAt}, -1)`;
|
|
1315
1351
|
const sortTimestampSortExpr = sql<number>`coalesce(${sortTimestamp}, -1)`;
|
|
1352
|
+
const pinnedOrder = filters.ignorePinnedSort
|
|
1353
|
+
? []
|
|
1354
|
+
: [desc(pinnedSortExpr)];
|
|
1316
1355
|
|
|
1317
1356
|
const baseQuery = db
|
|
1318
1357
|
.select()
|
|
@@ -1323,7 +1362,7 @@ export function createPostService(
|
|
|
1323
1362
|
let query =
|
|
1324
1363
|
filters.featured || filters.sortOrder === undefined
|
|
1325
1364
|
? baseQuery.orderBy(
|
|
1326
|
-
|
|
1365
|
+
...pinnedOrder,
|
|
1327
1366
|
filters.featured
|
|
1328
1367
|
? desc(featuredSortExpr)
|
|
1329
1368
|
: desc(sortTimestampSortExpr),
|
|
@@ -1331,20 +1370,20 @@ export function createPostService(
|
|
|
1331
1370
|
)
|
|
1332
1371
|
: filters.sortOrder === "oldest"
|
|
1333
1372
|
? baseQuery.orderBy(
|
|
1334
|
-
|
|
1373
|
+
...pinnedOrder,
|
|
1335
1374
|
asc(sortTimestampSortExpr),
|
|
1336
1375
|
asc(posts.id),
|
|
1337
1376
|
)
|
|
1338
1377
|
: filters.sortOrder === "rating_desc"
|
|
1339
1378
|
? baseQuery.orderBy(
|
|
1340
|
-
|
|
1379
|
+
...pinnedOrder,
|
|
1341
1380
|
desc(ratingPresence),
|
|
1342
1381
|
desc(posts.rating),
|
|
1343
1382
|
desc(sortTimestampSortExpr),
|
|
1344
1383
|
desc(posts.id),
|
|
1345
1384
|
)
|
|
1346
1385
|
: baseQuery.orderBy(
|
|
1347
|
-
|
|
1386
|
+
...pinnedOrder,
|
|
1348
1387
|
desc(ratingPresence),
|
|
1349
1388
|
asc(posts.rating),
|
|
1350
1389
|
desc(sortTimestampSortExpr),
|
|
@@ -2845,6 +2884,8 @@ export function createPostService(
|
|
|
2845
2884
|
sortOrder === "oldest"
|
|
2846
2885
|
? sql<number>`MIN(${posts.publishedAt})`.as("published_at")
|
|
2847
2886
|
: sql<number>`MAX(${posts.publishedAt})`.as("published_at");
|
|
2887
|
+
const threadActivityAt =
|
|
2888
|
+
buildCollectionThreadActivityExpr("thread_activity_at");
|
|
2848
2889
|
const ratingPresence = sql<number>`MAX(
|
|
2849
2890
|
CASE
|
|
2850
2891
|
WHEN ${posts.rating} IS NULL THEN 0
|
|
@@ -2864,6 +2905,7 @@ export function createPostService(
|
|
|
2864
2905
|
.select({
|
|
2865
2906
|
threadId: posts.threadId,
|
|
2866
2907
|
publishedAt,
|
|
2908
|
+
threadActivityAt,
|
|
2867
2909
|
collectionPinnedAt,
|
|
2868
2910
|
ratingPresence,
|
|
2869
2911
|
ratingValue,
|
|
@@ -2891,12 +2933,12 @@ export function createPostService(
|
|
|
2891
2933
|
desc(collectionPinnedAt),
|
|
2892
2934
|
desc(ratingPresence),
|
|
2893
2935
|
desc(ratingValue),
|
|
2894
|
-
desc(
|
|
2936
|
+
desc(threadActivityAt),
|
|
2895
2937
|
desc(posts.threadId),
|
|
2896
2938
|
)
|
|
2897
2939
|
: baseQuery.orderBy(
|
|
2898
2940
|
desc(collectionPinnedAt),
|
|
2899
|
-
desc(
|
|
2941
|
+
desc(threadActivityAt),
|
|
2900
2942
|
desc(posts.threadId),
|
|
2901
2943
|
);
|
|
2902
2944
|
|
|
@@ -2923,9 +2965,8 @@ export function createPostService(
|
|
|
2923
2965
|
...buildThreadRootPageConditions(options),
|
|
2924
2966
|
buildCollectionMembershipCondition(collectionIds),
|
|
2925
2967
|
];
|
|
2926
|
-
const
|
|
2927
|
-
"
|
|
2928
|
-
);
|
|
2968
|
+
const threadActivityAt =
|
|
2969
|
+
buildCollectionThreadActivityExpr("thread_activity_at");
|
|
2929
2970
|
const collectedAt = sql<number>`MAX(${postCollections.createdAt})`.as(
|
|
2930
2971
|
"collected_at",
|
|
2931
2972
|
);
|
|
@@ -2933,11 +2974,14 @@ export function createPostService(
|
|
|
2933
2974
|
sql<number>`MAX(coalesce(${postCollections.pinnedAt}, -1))`.as(
|
|
2934
2975
|
"collection_pinned_at",
|
|
2935
2976
|
);
|
|
2977
|
+
const pinnedOrder = options.ignoreCollectionPinnedSort
|
|
2978
|
+
? []
|
|
2979
|
+
: [desc(collectionPinnedAt)];
|
|
2936
2980
|
|
|
2937
2981
|
let query = db
|
|
2938
2982
|
.select({
|
|
2939
2983
|
threadId: posts.threadId,
|
|
2940
|
-
|
|
2984
|
+
threadActivityAt,
|
|
2941
2985
|
collectedAt,
|
|
2942
2986
|
collectionPinnedAt,
|
|
2943
2987
|
})
|
|
@@ -2951,11 +2995,7 @@ export function createPostService(
|
|
|
2951
2995
|
)
|
|
2952
2996
|
.where(and(...conditions))
|
|
2953
2997
|
.groupBy(posts.threadId)
|
|
2954
|
-
.orderBy(
|
|
2955
|
-
desc(collectionPinnedAt),
|
|
2956
|
-
desc(publishedAt),
|
|
2957
|
-
desc(posts.threadId),
|
|
2958
|
-
);
|
|
2998
|
+
.orderBy(...pinnedOrder, desc(threadActivityAt), desc(posts.threadId));
|
|
2959
2999
|
|
|
2960
3000
|
if (options.limit !== undefined) {
|
|
2961
3001
|
query = query.limit(options.limit) as typeof query;
|
package/src/services/settings.ts
CHANGED
|
@@ -46,7 +46,6 @@ export interface GeneralSettingsData {
|
|
|
46
46
|
dashboardLanguage?: string;
|
|
47
47
|
cjkSerifFont: string;
|
|
48
48
|
showJantBrandingOnHome: boolean;
|
|
49
|
-
homeDefaultView?: FeedKind;
|
|
50
49
|
mainRssFeed?: FeedKind;
|
|
51
50
|
timeZone: string;
|
|
52
51
|
}
|
|
@@ -404,15 +403,6 @@ export function createSettingsService(
|
|
|
404
403
|
oldCjkSerifFont: opts.oldCjkSerifFont,
|
|
405
404
|
});
|
|
406
405
|
|
|
407
|
-
// Homepage default view: only update if provided (may be managed separately)
|
|
408
|
-
if (data.homeDefaultView !== undefined) {
|
|
409
|
-
if (data.homeDefaultView === "featured") {
|
|
410
|
-
await this.set("HOME_DEFAULT_VIEW", data.homeDefaultView);
|
|
411
|
-
} else {
|
|
412
|
-
await this.remove("HOME_DEFAULT_VIEW");
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
|
|
416
406
|
await this.updateFeedSettings({ mainRssFeed: data.mainRssFeed });
|
|
417
407
|
|
|
418
408
|
return {
|
|
@@ -15,7 +15,6 @@ import { SETTINGS_KEYS } from "../lib/constants.js";
|
|
|
15
15
|
import { ConflictError, NotFoundError } from "../lib/errors.js";
|
|
16
16
|
import { createEntityId } from "../lib/ids.js";
|
|
17
17
|
import { getConfiguredSingleSiteUrl } from "../lib/env.js";
|
|
18
|
-
import { getHomeDefaultViewFromNavItems } from "../lib/navigation.js";
|
|
19
18
|
import { resolveConfig } from "../lib/resolve-config.js";
|
|
20
19
|
import { buildThemeStyle } from "../lib/theme.js";
|
|
21
20
|
import { now } from "../lib/time.js";
|
|
@@ -793,7 +792,6 @@ export function createSiteAdminService(
|
|
|
793
792
|
siteDescription: appConfig.siteDescription,
|
|
794
793
|
siteLanguage: appConfig.siteLanguage,
|
|
795
794
|
showJantBrandingOnHome: appConfig.showJantBrandingOnHome,
|
|
796
|
-
homeDefaultView: getHomeDefaultViewFromNavItems(navItemList),
|
|
797
795
|
mainRssFeed: appConfig.mainRssFeed,
|
|
798
796
|
siteFooter: appConfig.siteFooter,
|
|
799
797
|
showHeaderAvatar: appConfig.showHeaderAvatar,
|
|
@@ -52,10 +52,6 @@ const RELAY_MULTIPART_THRESHOLD = 95 * 1024 * 1024;
|
|
|
52
52
|
const RELAY_MULTIPART_PART_SIZE = 50 * 1024 * 1024;
|
|
53
53
|
const IMMUTABLE_CACHE_CONTROL = "public, max-age=31536000, immutable";
|
|
54
54
|
const DEFAULT_EXPIRED_UPLOAD_CLEANUP_LIMIT = 20;
|
|
55
|
-
// Grace window before a finalized-but-unattached media row (uploaded during
|
|
56
|
-
// compose, `postId IS NULL`) is reaped. Generous enough that an in-progress
|
|
57
|
-
// compose/edit is never affected; based on `createdAt`, no heartbeat needed.
|
|
58
|
-
const ORPHAN_MEDIA_GRACE_SECONDS = 7 * 24 * 60 * 60;
|
|
59
55
|
type CleanupableUploadSessionState = "pending" | "uploaded" | "failed";
|
|
60
56
|
const CLEANUPABLE_UPLOAD_SESSION_STATES = [
|
|
61
57
|
"pending",
|
|
@@ -766,19 +762,6 @@ export function createUploadSessionService(
|
|
|
766
762
|
deletedSessions += 1;
|
|
767
763
|
}
|
|
768
764
|
|
|
769
|
-
// Reap finalized media that was uploaded during compose but never
|
|
770
|
-
// attached to a post (`postId IS NULL`) past the grace window. This now
|
|
771
|
-
// soft-deletes via the media service: the DB row is removed and the
|
|
772
|
-
// storage object is enqueued for deferred deletion. Bounded by the same
|
|
773
|
-
// per-run `limit`; any backlog drains over subsequent runs.
|
|
774
|
-
const orphanIds = await media.listOrphanedMediaIds({
|
|
775
|
-
before: now() - ORPHAN_MEDIA_GRACE_SECONDS,
|
|
776
|
-
limit,
|
|
777
|
-
});
|
|
778
|
-
if (orphanIds.length > 0) {
|
|
779
|
-
await media.deleteByIds(orphanIds, deps.storage);
|
|
780
|
-
}
|
|
781
|
-
|
|
782
765
|
// Physically delete storage objects whose recycle window has elapsed.
|
|
783
766
|
// Skips any object a live media row still references (re-uploads).
|
|
784
767
|
const purgedStorageObjects = await media.purgeDueStorageObjects(
|
|
@@ -789,7 +772,10 @@ export function createUploadSessionService(
|
|
|
789
772
|
return {
|
|
790
773
|
abortedMultipartUploads,
|
|
791
774
|
deletedSessions,
|
|
792
|
-
|
|
775
|
+
// Retained for response compatibility. Finalized media may be referenced
|
|
776
|
+
// from post bodies without a `media.post_id`, so upload cleanup must not
|
|
777
|
+
// infer liveness from attachment state.
|
|
778
|
+
deletedOrphanMedia: 0,
|
|
793
779
|
purgedStorageObjects,
|
|
794
780
|
};
|
|
795
781
|
},
|
|
@@ -1163,6 +1163,26 @@ svg[stroke-width].icon-fine {
|
|
|
1163
1163
|
border-color: var(--color-border);
|
|
1164
1164
|
}
|
|
1165
1165
|
|
|
1166
|
+
.nav-suggestions-list {
|
|
1167
|
+
@apply flex flex-col gap-2;
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
.nav-suggestion-item {
|
|
1171
|
+
@apply flex items-center justify-between gap-3 rounded-lg border px-3 py-2;
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
.nav-suggestion-info {
|
|
1175
|
+
@apply flex min-w-0 flex-col;
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
.nav-suggestion-title {
|
|
1179
|
+
@apply truncate text-sm font-medium;
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
.nav-suggestion-meta {
|
|
1183
|
+
@apply truncate text-xs text-muted-foreground;
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1166
1186
|
/* Collection picker dropdown menu */
|
|
1167
1187
|
.collection-picker {
|
|
1168
1188
|
@apply absolute left-0 z-10 mt-1.5 w-64 overflow-y-auto rounded-lg border py-1.5;
|
package/src/styles/tokens.css
CHANGED
|
@@ -65,8 +65,8 @@
|
|
|
65
65
|
* --type-subtitle 1.8rem (27px) — h3
|
|
66
66
|
* --type-body 1.4rem (21px) — running text, form inputs
|
|
67
67
|
* --type-secondary 1.1rem (16.5px) — meta, nav, sidenotes, UI labels
|
|
68
|
-
* --type-base 1.0rem (15px) —
|
|
69
|
-
* --type-sm 0.9rem (13.5px) —
|
|
68
|
+
* --type-base 1.0rem (15px) — UI buttons, controls
|
|
69
|
+
* --type-sm 0.9rem (13.5px) — small UI text
|
|
70
70
|
* --type-xs 0.8rem (12px) — captions, descriptions, chips
|
|
71
71
|
* --type-2xs 0.65rem (9.75px) — tiny labels, file sizes, badges
|
|
72
72
|
*/
|
|
@@ -84,18 +84,20 @@
|
|
|
84
84
|
mobile overrides below cap sizes for small screens.
|
|
85
85
|
--type-content-scale uniformly scales all content sizes
|
|
86
86
|
without affecting UI controls. */
|
|
87
|
-
--type-content-scale: 0.
|
|
87
|
+
--type-content-scale: 0.8;
|
|
88
88
|
--type-content-display: calc(var(--type-display) * var(--type-content-scale));
|
|
89
89
|
--type-content-title: calc(var(--type-title) * var(--type-content-scale));
|
|
90
90
|
--type-content-subtitle: calc(
|
|
91
91
|
var(--type-subtitle) * var(--type-content-scale)
|
|
92
92
|
);
|
|
93
93
|
--type-content-body: calc(var(--type-body) * var(--type-content-scale));
|
|
94
|
+
--type-content-quote: calc(var(--type-content-body) * 1.16);
|
|
95
|
+
--type-content-quote-leading: 1.4;
|
|
94
96
|
|
|
95
97
|
/* Semantic aliases */
|
|
96
|
-
--type-code: var(--type-base);
|
|
97
|
-
--type-code-block: var(--type-sm);
|
|
98
98
|
--type-body-size: var(--type-content-body);
|
|
99
|
+
--type-code: calc(var(--type-body-size) * 0.94);
|
|
100
|
+
--type-code-block: calc(var(--type-body-size) * 0.9);
|
|
99
101
|
--type-body-tracking: 0;
|
|
100
102
|
--type-ui-title: var(--type-secondary);
|
|
101
103
|
--type-ui-control: var(--type-base);
|
|
@@ -107,14 +109,14 @@
|
|
|
107
109
|
--type-thread-context: var(--type-base);
|
|
108
110
|
--type-thread-context-title: var(--type-secondary);
|
|
109
111
|
--type-thread-context-meta: var(--type-sm);
|
|
110
|
-
--feed-note-title-size: var(--type-content-
|
|
112
|
+
--feed-note-title-size: calc(var(--type-content-body) * 1.36);
|
|
111
113
|
--feed-note-title-leading: var(--type-heading-leading);
|
|
112
|
-
--type-body-leading: 1.
|
|
114
|
+
--type-body-leading: 1.7;
|
|
113
115
|
--type-display-leading: 1.15;
|
|
114
116
|
--type-heading-leading: 1.15;
|
|
115
|
-
--type-heading-weight: var(--fw-
|
|
117
|
+
--type-heading-weight: var(--fw-medium);
|
|
116
118
|
--type-heading-tracking: 0;
|
|
117
|
-
--type-display-weight: var(--fw-
|
|
119
|
+
--type-display-weight: var(--fw-medium);
|
|
118
120
|
--type-display-tracking: 0;
|
|
119
121
|
--type-label-weight: var(--fw-medium);
|
|
120
122
|
--type-label-tracking: 0.08em;
|
|
@@ -264,6 +266,22 @@
|
|
|
264
266
|
var(--site-text-primary) 12%,
|
|
265
267
|
transparent
|
|
266
268
|
);
|
|
269
|
+
--site-code-text: color-mix(
|
|
270
|
+
in srgb,
|
|
271
|
+
var(--site-reading-heading) 86%,
|
|
272
|
+
var(--site-reading-body)
|
|
273
|
+
);
|
|
274
|
+
--site-code-bg: color-mix(in srgb, var(--site-reading-meta) 12%, transparent);
|
|
275
|
+
--site-code-block-bg: color-mix(
|
|
276
|
+
in srgb,
|
|
277
|
+
var(--site-elevated-bg) 94%,
|
|
278
|
+
var(--site-nav-hover-bg)
|
|
279
|
+
);
|
|
280
|
+
--site-code-block-border: color-mix(
|
|
281
|
+
in srgb,
|
|
282
|
+
var(--site-divider) 62%,
|
|
283
|
+
transparent
|
|
284
|
+
);
|
|
267
285
|
--site-feed-divider-color: color-mix(
|
|
268
286
|
in srgb,
|
|
269
287
|
var(--site-text-secondary) 30%,
|
|
@@ -484,7 +502,7 @@
|
|
|
484
502
|
:root {
|
|
485
503
|
/* Content layer — tighter scale for mobile reading.
|
|
486
504
|
Ratio ≈ 1.88 : 1.42 : 1.15 : 1 (display:title:subtitle:body).
|
|
487
|
-
At 15px root × 0.
|
|
505
|
+
At 15px root × 0.8 content-scale: 29.4 / 22.2 / 18 / 15.6px.
|
|
488
506
|
Body is floored to a 16px minimum below (max()) for readability;
|
|
489
507
|
the floor still yields to calc() when the user zooms the root up. */
|
|
490
508
|
--type-content-display: calc(2.45rem * var(--type-content-scale));
|