@jant/core 0.6.12 → 0.6.14

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.
Files changed (120) hide show
  1. package/bin/commands/import-site.js +2 -10
  2. package/bin/commands/migrate-text-attachments.js +1 -3
  3. package/bin/commands/site/export.js +1 -3
  4. package/bin/commands/telegram/register-webhooks.js +9 -3
  5. package/bin/lib/site-snapshot.js +1 -5
  6. package/dist/{app-D-zAhayc.js → app-2i7-WQHg.js} +426 -450
  7. package/dist/app-DZDlmh7C.js +6 -0
  8. package/dist/client/.vite/manifest.json +3 -3
  9. package/dist/client/_assets/{client-S1cdvulk.js → client-BvID5ucz.js} +1 -1
  10. package/dist/client/_assets/client-Cevfpm8H.css +2 -0
  11. package/dist/client/_assets/{client-auth-YW92ZH7u.js → client-auth-v3yvhbgZ.js} +596 -590
  12. package/dist/{export-Ckwh4BiE.js → export-DWn149b7.js} +24 -19
  13. package/dist/{github-sync-BMmK3JZ0.js → github-sync-C0zyF6XS.js} +2 -2
  14. package/dist/{github-sync-Dl9wQMar.js → github-sync-Ckk0sJxK.js} +1 -1
  15. package/dist/index.js +3 -3
  16. package/dist/node.js +4 -4
  17. package/package.json +1 -1
  18. package/src/__tests__/export-service.test.ts +1 -2
  19. package/src/__tests__/helpers/app.ts +3 -0
  20. package/src/__tests__/helpers/export-fixtures.ts +0 -1
  21. package/src/__tests__/import-site-command.test.ts +1 -2
  22. package/src/client/__tests__/site-header-nav.test.ts +36 -2
  23. package/src/client/components/__tests__/compose-title-from-heading.test.ts +57 -0
  24. package/src/client/components/__tests__/jant-command-palette.test.ts +137 -10
  25. package/src/client/components/__tests__/jant-compose-dialog.test.ts +3 -0
  26. package/src/client/components/__tests__/jant-compose-editor.test.ts +94 -2
  27. package/src/client/components/__tests__/jant-nav-manager.test.ts +107 -22
  28. package/src/client/components/__tests__/jant-settings-general.test.ts +1 -0
  29. package/src/client/components/compose-title-from-heading.ts +66 -0
  30. package/src/client/components/compose-types.ts +8 -0
  31. package/src/client/components/inline-image-issues.ts +17 -0
  32. package/src/client/components/jant-command-palette.ts +100 -70
  33. package/src/client/components/jant-compose-dialog.ts +3 -1
  34. package/src/client/components/jant-compose-editor.ts +78 -1
  35. package/src/client/components/jant-compose-fullscreen.ts +2 -0
  36. package/src/client/components/jant-nav-manager.ts +245 -155
  37. package/src/client/components/nav-manager-types.ts +0 -10
  38. package/src/client/site-header-nav.d.ts +1 -0
  39. package/src/client/site-header-nav.js +105 -47
  40. package/src/client/tiptap/__tests__/block-insertion.test.ts +35 -0
  41. package/src/client/tiptap/__tests__/footnotes.test.ts +284 -0
  42. package/src/client/tiptap/__tests__/image-input-rules.test.ts +117 -0
  43. package/src/client/tiptap/__tests__/markdown-clipboard.test.ts +148 -1
  44. package/src/client/tiptap/bubble-menu.ts +3 -3
  45. package/src/client/tiptap/create-editor.ts +7 -0
  46. package/src/client/tiptap/extensions.ts +7 -2
  47. package/src/client/tiptap/footnotes.ts +156 -0
  48. package/src/client/tiptap/image-input-rules.ts +48 -0
  49. package/src/client/tiptap/image-node.ts +189 -7
  50. package/src/client/tiptap/markdown-clipboard.ts +7 -2
  51. package/src/client-auth.ts +0 -1
  52. package/src/i18n/locales/public/en.po +20 -4
  53. package/src/i18n/locales/public/en.ts +1 -1
  54. package/src/i18n/locales/public/zh-Hans.po +20 -4
  55. package/src/i18n/locales/public/zh-Hans.ts +1 -1
  56. package/src/i18n/locales/public/zh-Hant.po +20 -4
  57. package/src/i18n/locales/public/zh-Hant.ts +1 -1
  58. package/src/i18n/locales/settings/en.po +23 -20
  59. package/src/i18n/locales/settings/en.ts +1 -1
  60. package/src/i18n/locales/settings/zh-Hans.po +23 -20
  61. package/src/i18n/locales/settings/zh-Hans.ts +1 -1
  62. package/src/i18n/locales/settings/zh-Hant.po +23 -20
  63. package/src/i18n/locales/settings/zh-Hant.ts +1 -1
  64. package/src/lib/__tests__/markdown-to-tiptap.test.ts +4 -0
  65. package/src/lib/__tests__/navigation.test.ts +4 -37
  66. package/src/lib/__tests__/resolve-config.test.ts +6 -1
  67. package/src/lib/__tests__/view.test.ts +6 -30
  68. package/src/lib/github-sync-site-config.ts +0 -2
  69. package/src/lib/hugo-markdown.ts +1 -1
  70. package/src/lib/markdown-manager.ts +14 -5
  71. package/src/lib/navigation.ts +5 -22
  72. package/src/lib/resolve-config.ts +9 -2
  73. package/src/lib/site-header-fragment.tsx +37 -0
  74. package/src/lib/view.ts +2 -7
  75. package/src/node/__tests__/cli-site-snapshot.test.ts +45 -45
  76. package/src/routes/api/__tests__/nav-items.test.ts +26 -0
  77. package/src/routes/api/export.ts +0 -2
  78. package/src/routes/api/internal/__tests__/uploads.test.ts +9 -13
  79. package/src/routes/api/internal/sites.ts +5 -5
  80. package/src/routes/api/nav-items.ts +25 -4
  81. package/src/routes/dash/settings.tsx +8 -2
  82. package/src/routes/feed/__tests__/feed.test.ts +42 -0
  83. package/src/routes/feed/__tests__/sitemap.test.ts +2 -13
  84. package/src/routes/feed/feed.ts +1 -0
  85. package/src/routes/feed/sitemap.ts +2 -6
  86. package/src/routes/pages/archive.tsx +1 -0
  87. package/src/routes/pages/collection.tsx +1 -0
  88. package/src/routes/pages/featured.tsx +0 -4
  89. package/src/routes/pages/home.tsx +4 -51
  90. package/src/routes/pages/latest.tsx +2 -51
  91. package/src/services/__tests__/github-app-installations.test.ts +10 -10
  92. package/src/services/__tests__/navigation.test.ts +41 -0
  93. package/src/services/__tests__/post-timeline.test.ts +56 -0
  94. package/src/services/__tests__/post.test.ts +33 -0
  95. package/src/services/__tests__/settings.test.ts +5 -20
  96. package/src/services/export-theme/assets/client-site.js +10 -10
  97. package/src/services/export-theme/layouts/_default/rss.xml +5 -15
  98. package/src/services/export-theme/layouts/index.html +26 -39
  99. package/src/services/export.ts +5 -10
  100. package/src/services/github-app-installations.ts +1 -3
  101. package/src/services/navigation.ts +44 -13
  102. package/src/services/post.ts +80 -57
  103. package/src/services/settings.ts +0 -10
  104. package/src/services/site-admin.ts +0 -2
  105. package/src/services/upload-session.ts +4 -18
  106. package/src/styles/tokens.css +2 -2
  107. package/src/styles/ui.css +316 -34
  108. package/src/types/bindings.ts +0 -1
  109. package/src/types/config.ts +3 -7
  110. package/src/ui/compose/ComposeDialog.tsx +28 -0
  111. package/src/ui/dash/appearance/NavigationContent.tsx +1 -1
  112. package/src/ui/dash/settings/AccountMenuContent.tsx +2 -2
  113. package/src/ui/dash/settings/SettingsRootContent.tsx +3 -3
  114. package/src/ui/feed/__tests__/timeline-cards.test.ts +8 -3
  115. package/src/ui/layouts/BaseLayout.tsx +1 -1
  116. package/src/ui/layouts/SiteLayout.tsx +266 -227
  117. package/src/ui/layouts/__tests__/SiteLayout.test.tsx +48 -0
  118. package/dist/app-B5P1b4TF.js +0 -6
  119. package/dist/client/_assets/client-BUxR-E8O.css +0 -2
  120. package/src/client/nav-manager-bridge.ts +0 -54
package/src/styles/ui.css CHANGED
@@ -848,7 +848,7 @@
848
848
  display: flex;
849
849
  }
850
850
 
851
- .site-header-more-divider-responsive {
851
+ .site-header-more-divider-show-lg {
852
852
  display: block;
853
853
  }
854
854
  }
@@ -865,6 +865,10 @@
865
865
  .site-header-more-link-show-md {
866
866
  display: flex;
867
867
  }
868
+
869
+ .site-header-more-divider-show-md {
870
+ display: block;
871
+ }
868
872
  }
869
873
 
870
874
  @media (max-width: 580px) {
@@ -879,6 +883,10 @@
879
883
  .site-header-more-link-show-sm {
880
884
  display: flex;
881
885
  }
886
+
887
+ .site-header-more-divider-show-sm {
888
+ display: block;
889
+ }
882
890
  }
883
891
 
884
892
  @media (max-width: 480px) {
@@ -5425,6 +5433,30 @@
5425
5433
  color: var(--site-text-primary);
5426
5434
  }
5427
5435
 
5436
+ .compose-attached-done {
5437
+ margin-left: auto;
5438
+ padding: 6px 14px;
5439
+ border: none;
5440
+ border-radius: 8px;
5441
+ background-color: var(--site-text-primary);
5442
+ color: var(--site-elevated-bg);
5443
+ font-size: var(--type-ui-control);
5444
+ font-weight: var(--fw-medium);
5445
+ line-height: 1.25;
5446
+ cursor: pointer;
5447
+ transition: opacity 0.15s ease;
5448
+ }
5449
+
5450
+ .compose-attached-done:hover {
5451
+ opacity: 0.82;
5452
+ }
5453
+
5454
+ .compose-attached-done:focus-visible {
5455
+ outline: 2px solid
5456
+ color-mix(in srgb, var(--site-text-primary) 24%, transparent);
5457
+ outline-offset: 2px;
5458
+ }
5459
+
5428
5460
  .compose-attached-tiptap {
5429
5461
  flex: 1;
5430
5462
  min-height: 0;
@@ -5436,6 +5468,10 @@
5436
5468
  min-height: 100%;
5437
5469
  }
5438
5470
 
5471
+ .compose-attached-tiptap .tiptap > :first-child {
5472
+ margin-top: 0;
5473
+ }
5474
+
5439
5475
  .compose-attached-tiptap::-webkit-scrollbar {
5440
5476
  width: 3px;
5441
5477
  }
@@ -7008,6 +7044,14 @@
7008
7044
  max-width: 100%;
7009
7045
  }
7010
7046
 
7047
+ .compose-attachment:has(.compose-attachment-preview-fallback) {
7048
+ width: 112px;
7049
+ }
7050
+
7051
+ .compose-attachment:only-child:has(.compose-attachment-preview-fallback) {
7052
+ width: min(100%, 160px);
7053
+ }
7054
+
7011
7055
  .compose-attachment-thumb {
7012
7056
  position: relative;
7013
7057
  border-radius: var(--media-radius, 0.5rem);
@@ -7015,6 +7059,13 @@
7015
7059
  background-color: var(--site-nav-hover-bg);
7016
7060
  }
7017
7061
 
7062
+ .compose-attachment:has(.compose-attachment-preview-fallback)
7063
+ .compose-attachment-thumb,
7064
+ .compose-attachment:has(.compose-attachment-preview-fallback)
7065
+ .compose-attachment-sortable {
7066
+ width: 100%;
7067
+ }
7068
+
7018
7069
  .compose-attachment-sortable {
7019
7070
  display: block;
7020
7071
  cursor: grab;
@@ -7065,8 +7116,11 @@
7065
7116
  display: flex;
7066
7117
  align-items: center;
7067
7118
  justify-content: center;
7068
- width: 120px;
7069
- aspect-ratio: 1 / 1;
7119
+ flex-direction: column;
7120
+ gap: 6px;
7121
+ width: 100%;
7122
+ aspect-ratio: 4 / 3;
7123
+ padding: 10px;
7070
7124
  border: 1px solid color-mix(in srgb, var(--site-divider) 88%, transparent);
7071
7125
  border-radius: var(--media-radius, 0.5rem);
7072
7126
  background: linear-gradient(
@@ -7078,9 +7132,24 @@
7078
7132
  }
7079
7133
 
7080
7134
  .compose-attachment-preview-fallback svg {
7135
+ width: 24px;
7136
+ height: 24px;
7081
7137
  opacity: 0.82;
7082
7138
  }
7083
7139
 
7140
+ .compose-attachment-preview-fallback-label {
7141
+ max-width: 100%;
7142
+ overflow: hidden;
7143
+ color: var(--site-text-secondary);
7144
+ font-family: var(--font-ui);
7145
+ font-size: var(--type-2xs);
7146
+ font-weight: var(--fw-medium);
7147
+ line-height: 1.2;
7148
+ text-align: center;
7149
+ text-overflow: ellipsis;
7150
+ white-space: nowrap;
7151
+ }
7152
+
7084
7153
  /* Single image: constrain to container width. min-width keeps the preview
7085
7154
  wide enough for the remove button; a very long image overflows it and is
7086
7155
  cropped from the top (object-fit: cover) instead of shrinking to a sliver. */
@@ -7093,8 +7162,7 @@
7093
7162
  }
7094
7163
 
7095
7164
  .compose-attachment:only-child .compose-attachment-preview-fallback {
7096
- width: min(100%, 320px);
7097
- max-width: 100%;
7165
+ width: 100%;
7098
7166
  aspect-ratio: 4 / 3;
7099
7167
  }
7100
7168
 
@@ -7107,8 +7175,13 @@
7107
7175
  }
7108
7176
 
7109
7177
  .compose-attachment:not(:only-child) .compose-attachment-preview-fallback {
7110
- width: 150px;
7111
- aspect-ratio: 3 / 4;
7178
+ width: 100%;
7179
+ aspect-ratio: 4 / 3;
7180
+ }
7181
+
7182
+ .compose-attachment:only-child .compose-attachment-preview-fallback svg {
7183
+ width: 28px;
7184
+ height: 28px;
7112
7185
  }
7113
7186
 
7114
7187
  .compose-attachment-overlay {
@@ -7197,6 +7270,14 @@
7197
7270
  box-shadow 0.18s ease;
7198
7271
  }
7199
7272
 
7273
+ .compose-attachment:has(.compose-attachment-preview-fallback)
7274
+ .compose-attachment-remove {
7275
+ top: 5px;
7276
+ right: 5px;
7277
+ width: 24px;
7278
+ height: 24px;
7279
+ }
7280
+
7200
7281
  .compose-attachment-remove:hover {
7201
7282
  transform: translateY(-0.5px);
7202
7283
  background-color: color-mix(in srgb, black 58%, transparent);
@@ -7222,6 +7303,13 @@
7222
7303
  pointer-events: none;
7223
7304
  }
7224
7305
 
7306
+ .compose-attachment:has(.compose-attachment-preview-fallback)
7307
+ .compose-attachment-remove
7308
+ svg {
7309
+ width: 12px;
7310
+ height: 12px;
7311
+ }
7312
+
7225
7313
  .compose-attachment-alt {
7226
7314
  border: none;
7227
7315
  background: transparent;
@@ -7556,6 +7644,12 @@
7556
7644
  max-width: none;
7557
7645
  }
7558
7646
 
7647
+ .compose-reply-compose-layout
7648
+ .compose-editor-row
7649
+ .compose-attachment:has(.compose-attachment-preview-fallback) {
7650
+ width: var(--compose-reply-attachment-width);
7651
+ }
7652
+
7559
7653
  .compose-reply-compose-layout
7560
7654
  .compose-editor-row
7561
7655
  .compose-attachment:only-child
@@ -7584,6 +7678,14 @@
7584
7678
  aspect-ratio: auto;
7585
7679
  }
7586
7680
 
7681
+ .compose-reply-compose-layout
7682
+ .compose-editor-row
7683
+ .compose-attachment-preview-fallback
7684
+ svg {
7685
+ width: 28px;
7686
+ height: 28px;
7687
+ }
7688
+
7587
7689
  .compose-reply-compose-layout
7588
7690
  .compose-editor-row
7589
7691
  .compose-attachment-file-card,
@@ -7669,28 +7771,9 @@
7669
7771
 
7670
7772
  /*
7671
7773
  * Reply context clones feed/detail card internals into a much narrower shell.
7672
- * Reset the reading widths and heading scale so the preview uses the thread
7673
- * context ladder instead of the original page/card layout.
7774
+ * Scale headings to the thread context ladder instead of the original
7775
+ * page/card layout.
7674
7776
  */
7675
- .compose-reply-context-body
7676
- :is(
7677
- .post-detail-title,
7678
- .post-detail-body,
7679
- .post-body-summary,
7680
- .feed-note-title,
7681
- .feed-link-title,
7682
- .feed-quote,
7683
- .feed-quote-attribution,
7684
- .feed-quote-commentary,
7685
- .feed-link-domain,
7686
- .feed-continue-link,
7687
- [data-post-body].prose,
7688
- [data-post-meta]
7689
- ) {
7690
- width: 100%;
7691
- max-width: none;
7692
- }
7693
-
7694
7777
  .compose-reply-context-body
7695
7778
  :is(.post-detail-title, .feed-note-title, .feed-link-title),
7696
7779
  .compose-reply-context-body .prose :is(h1, h2, h3, h4) {
@@ -7910,11 +7993,11 @@
7910
7993
  }
7911
7994
 
7912
7995
  .compose-thread-layout {
7913
- padding-top: 0.45rem;
7996
+ padding-top: 0.75rem;
7914
7997
  }
7915
7998
 
7916
7999
  .compose-thread-layout::before {
7917
- top: 0.45rem;
8000
+ top: 0.75rem;
7918
8001
  }
7919
8002
 
7920
8003
  .compose-reply-context {
@@ -8564,9 +8647,13 @@
8564
8647
  }
8565
8648
  }
8566
8649
 
8650
+ .command-palette-results-container {
8651
+ display: flex;
8652
+ flex-direction: column;
8653
+ min-height: 0;
8654
+ }
8655
+
8567
8656
  .command-palette-results {
8568
- list-style: none;
8569
- margin: 0;
8570
8657
  padding: 0.25rem 0;
8571
8658
  max-height: 20rem;
8572
8659
  overflow-y: auto;
@@ -8608,6 +8695,7 @@
8608
8695
  .command-palette-result-body {
8609
8696
  display: flex;
8610
8697
  flex-direction: column;
8698
+ flex: 1;
8611
8699
  gap: 0.125rem;
8612
8700
  min-width: 0;
8613
8701
  }
@@ -8628,6 +8716,37 @@
8628
8716
  text-overflow: ellipsis;
8629
8717
  }
8630
8718
 
8719
+ .command-palette-search-action {
8720
+ flex-shrink: 0;
8721
+ border-top: 1px solid var(--color-border);
8722
+ padding-block: 0.625rem;
8723
+ background-color: color-mix(
8724
+ in srgb,
8725
+ var(--color-muted) 32%,
8726
+ var(--compose-paper-bg)
8727
+ );
8728
+ }
8729
+
8730
+ .command-palette-search-action:hover,
8731
+ .command-palette-search-action.command-palette-result-selected {
8732
+ background-color: var(--color-accent);
8733
+ }
8734
+
8735
+ .command-palette-result-shortcut {
8736
+ flex-shrink: 0;
8737
+ margin-left: auto;
8738
+ border: 1px solid var(--color-border);
8739
+ border-radius: 0.25rem;
8740
+ padding: 0.125rem 0.375rem;
8741
+ background-color: var(--compose-paper-bg);
8742
+ color: var(--color-muted-foreground);
8743
+ font-family: inherit;
8744
+ font-size: 0.6875rem;
8745
+ font-weight: 500;
8746
+ line-height: 1.25rem;
8747
+ white-space: nowrap;
8748
+ }
8749
+
8631
8750
  .command-palette-empty {
8632
8751
  padding: 1.5rem 1rem;
8633
8752
  text-align: center;
@@ -9094,6 +9213,134 @@
9094
9213
  display: block;
9095
9214
  }
9096
9215
 
9216
+ .tiptap-image-figure[data-load-state="missing"] img {
9217
+ display: none;
9218
+ }
9219
+
9220
+ .tiptap-image-missing {
9221
+ display: none;
9222
+ align-items: center;
9223
+ justify-content: center;
9224
+ gap: 14px;
9225
+ min-height: clamp(132px, 26vw, 220px);
9226
+ padding: 18px;
9227
+ border-radius: 4px;
9228
+ background: linear-gradient(
9229
+ 135deg,
9230
+ color-mix(in srgb, var(--site-elevated-bg) 88%, var(--site-accent)),
9231
+ var(--site-elevated-bg)
9232
+ );
9233
+ color: var(--site-text-secondary);
9234
+ text-align: left;
9235
+ }
9236
+
9237
+ .tiptap-image-figure[data-load-state="missing"] .tiptap-image-missing {
9238
+ display: flex;
9239
+ }
9240
+
9241
+ .tiptap-image-missing:focus-visible {
9242
+ outline: 2px solid var(--ring);
9243
+ outline-offset: 3px;
9244
+ }
9245
+
9246
+ .tiptap-image-missing-icon {
9247
+ display: inline-flex;
9248
+ align-items: center;
9249
+ justify-content: center;
9250
+ width: 38px;
9251
+ height: 38px;
9252
+ border-radius: 999px;
9253
+ border: 1px solid var(--site-divider);
9254
+ background: var(--compose-paper-bg);
9255
+ color: var(--site-text-secondary);
9256
+ flex-shrink: 0;
9257
+ }
9258
+
9259
+ .tiptap-image-missing-copy {
9260
+ display: grid;
9261
+ gap: 3px;
9262
+ min-width: 0;
9263
+ flex: 1 1 auto;
9264
+ }
9265
+
9266
+ .tiptap-image-missing-copy strong {
9267
+ color: var(--site-text-primary);
9268
+ font-family: var(--font-ui);
9269
+ font-size: var(--type-sm);
9270
+ line-height: 1.2;
9271
+ }
9272
+
9273
+ .tiptap-image-missing-source {
9274
+ max-width: 100%;
9275
+ overflow: hidden;
9276
+ color: var(--site-text-secondary);
9277
+ font-family: var(--font-mono);
9278
+ font-size: var(--type-2xs);
9279
+ line-height: 1.35;
9280
+ text-overflow: ellipsis;
9281
+ white-space: nowrap;
9282
+ }
9283
+
9284
+ .tiptap-image-missing-actions {
9285
+ display: flex;
9286
+ align-items: center;
9287
+ gap: 6px;
9288
+ flex-wrap: wrap;
9289
+ justify-content: flex-end;
9290
+ flex: 0 0 auto;
9291
+ }
9292
+
9293
+ .tiptap-image-missing-actions button {
9294
+ border: 1px solid var(--site-divider);
9295
+ border-radius: 5px;
9296
+ padding: 5px 8px;
9297
+ background: var(--compose-paper-bg);
9298
+ color: var(--site-text-primary);
9299
+ font-family: var(--font-ui);
9300
+ font-size: var(--type-2xs);
9301
+ line-height: 1.2;
9302
+ cursor: pointer;
9303
+ transition:
9304
+ border-color 0.16s ease,
9305
+ color 0.16s ease,
9306
+ background-color 0.16s ease;
9307
+ }
9308
+
9309
+ .tiptap-image-missing-actions button:hover {
9310
+ border-color: color-mix(
9311
+ in srgb,
9312
+ var(--site-accent) 45%,
9313
+ var(--site-divider)
9314
+ );
9315
+ color: var(--site-accent);
9316
+ }
9317
+
9318
+ .tiptap-image-missing-actions button:focus-visible {
9319
+ outline: 2px solid var(--ring);
9320
+ outline-offset: 2px;
9321
+ }
9322
+
9323
+ .tiptap-image-missing-actions button:disabled {
9324
+ cursor: not-allowed;
9325
+ opacity: 0.45;
9326
+ }
9327
+
9328
+ .tiptap-image-missing-delete {
9329
+ color: var(--destructive);
9330
+ }
9331
+
9332
+ @media (max-width: 640px) {
9333
+ .tiptap-image-missing {
9334
+ align-items: stretch;
9335
+ flex-direction: column;
9336
+ }
9337
+
9338
+ .tiptap-image-missing-actions,
9339
+ .tiptap-image-missing-actions button {
9340
+ width: 100%;
9341
+ }
9342
+ }
9343
+
9097
9344
  /* Layout variants — in compact dialog, wide/full just fill the container */
9098
9345
  .tiptap-image-figure[data-layout="wide"],
9099
9346
  .tiptap-image-figure[data-layout="full"] {
@@ -9605,6 +9852,7 @@
9605
9852
  vertical-align: super;
9606
9853
  color: var(--site-accent);
9607
9854
  white-space: nowrap;
9855
+ cursor: pointer;
9608
9856
  }
9609
9857
 
9610
9858
  .compose-tiptap-body
@@ -9640,13 +9888,22 @@
9640
9888
 
9641
9889
  .compose-tiptap-body
9642
9890
  .tiptap
9643
- > :not(.tiptap-footnote-definition)
9644
- + .tiptap-footnote-definition {
9891
+ > :not(.tiptap-footnote-definition, .ProseMirror-gapcursor)
9892
+ ~ .tiptap-footnote-definition {
9645
9893
  margin-top: 2rem;
9646
9894
  padding-top: 1rem;
9647
9895
  border-top: 1px solid var(--site-divider);
9648
9896
  }
9649
9897
 
9898
+ .compose-tiptap-body
9899
+ .tiptap
9900
+ > .tiptap-footnote-definition
9901
+ ~ .tiptap-footnote-definition {
9902
+ margin-top: 1rem;
9903
+ padding-top: 0;
9904
+ border-top: 0;
9905
+ }
9906
+
9650
9907
  .compose-tiptap-body .tiptap .tiptap-footnote-definition > :first-child {
9651
9908
  margin-top: 0;
9652
9909
  }
@@ -11964,3 +12221,28 @@
11964
12221
  );
11965
12222
  color: color-mix(in srgb, var(--site-text-secondary) 80%, transparent);
11966
12223
  }
12224
+
12225
+ /*
12226
+ * Compose reply context renders cloned feed/detail post HTML. Keep this
12227
+ * outside @layer components so it can override the unlayered global post-body
12228
+ * width rules that intentionally make normal desktop post text use the 55%
12229
+ * reading column.
12230
+ */
12231
+ .compose-reply-context-body
12232
+ :is(
12233
+ .post-detail-title,
12234
+ .post-detail-body,
12235
+ .post-body-summary,
12236
+ .feed-note-title,
12237
+ .feed-link-title,
12238
+ .feed-quote,
12239
+ .feed-quote-attribution,
12240
+ .feed-quote-commentary,
12241
+ .feed-link-domain,
12242
+ .feed-continue-link,
12243
+ [data-post-body].prose,
12244
+ [data-post-meta]
12245
+ ) {
12246
+ width: 100%;
12247
+ max-width: none;
12248
+ }
@@ -36,7 +36,6 @@ export interface Bindings {
36
36
  SITE_DESCRIPTION?: EnvBindingValue;
37
37
  SITE_LANGUAGE?: EnvBindingValue;
38
38
  DASHBOARD_LANGUAGE?: EnvBindingValue;
39
- HOME_DEFAULT_VIEW?: EnvBindingValue;
40
39
  MAIN_RSS_FEED?: EnvBindingValue;
41
40
  TIME_ZONE?: EnvBindingValue;
42
41
  SITE_FOOTER?: EnvBindingValue;
@@ -4,6 +4,8 @@
4
4
  * Single Source of Truth for all configuration fields.
5
5
  */
6
6
 
7
+ import type { FeedKind } from "./constants.js";
8
+
7
9
  /**
8
10
  * Configuration Registry - Single Source of Truth
9
11
  *
@@ -53,11 +55,6 @@ export const CONFIG_FIELDS = {
53
55
  envOnly: false,
54
56
  envKeys: ["CJK_SERIF_FONT"],
55
57
  },
56
- HOME_DEFAULT_VIEW: {
57
- defaultValue: "latest",
58
- envOnly: false,
59
- envKeys: ["HOME_DEFAULT_VIEW"],
60
- },
61
58
  MAIN_RSS_FEED: {
62
59
  defaultValue: "featured",
63
60
  envOnly: false,
@@ -470,8 +467,7 @@ export interface AppConfig {
470
467
  dashboardLanguage: string;
471
468
  /** CJK serif font locale: "off", "zh-Hans", "zh-Hant", "ja", or "ko" */
472
469
  cjkSerifFont: string;
473
- homeDefaultView: string;
474
- mainRssFeed: string;
470
+ mainRssFeed: FeedKind;
475
471
  /** Canonical IANA timezone identifier used for date/time display. */
476
472
  timeZone: string;
477
473
  siteFooter: string;
@@ -59,6 +59,34 @@ export const ComposeForm: FC<ComposeFormProps> = ({
59
59
  "@context: Toast when several pasted remote images couldn't be rehosted; {count} is the number of images",
60
60
  }),
61
61
  ),
62
+ brokenImageUnavailable: i18n._(
63
+ msg({
64
+ message: "Image unavailable",
65
+ comment:
66
+ "@context: Inline editor placeholder title shown when an image cannot load",
67
+ }),
68
+ ),
69
+ brokenImageDelete: i18n._(
70
+ msg({
71
+ message: "Delete image",
72
+ comment:
73
+ "@context: Inline editor button label for removing a broken image",
74
+ }),
75
+ ),
76
+ brokenImageReplace: i18n._(
77
+ msg({
78
+ message: "Replace image",
79
+ comment:
80
+ "@context: Inline editor button label for replacing a broken image",
81
+ }),
82
+ ),
83
+ brokenImageOpen: i18n._(
84
+ msg({
85
+ message: "Open image URL",
86
+ comment:
87
+ "@context: Inline editor button label for opening the original URL of a broken image",
88
+ }),
89
+ ),
62
90
  note: i18n._(
63
91
  msg({
64
92
  message: "Note",
@@ -268,7 +268,7 @@ export function NavigationContent({
268
268
  systemLinksDescription: i18n._(
269
269
  msg({
270
270
  message:
271
- "Toggle built-in navigation items. Their order controls what shows in the header and which feed the homepage opens first.",
271
+ "Show or hide built-in destinations in the header and More menu.",
272
272
  comment: "@context: Description for built-in nav toggles",
273
273
  }),
274
274
  ),
@@ -66,7 +66,7 @@ export function AccountMenuContent({
66
66
  : i18n._(
67
67
  msg({
68
68
  message:
69
- "Manage sign-in security, exports, and irreversible actions.",
69
+ "Manage sign-in security, site exports, and irreversible actions.",
70
70
  comment:
71
71
  "@context: Intro text on the account settings menu page below the title",
72
72
  }),
@@ -105,7 +105,7 @@ export function AccountMenuContent({
105
105
  <h1 class="page-intro-title page-intro-title-compact">
106
106
  {i18n._(
107
107
  msg({
108
- message: "Account",
108
+ message: "Account & Data",
109
109
  comment: "@context: Page title for the account settings menu",
110
110
  }),
111
111
  )}
@@ -51,7 +51,7 @@ export function SettingsRootContent({
51
51
  )
52
52
  : i18n._(
53
53
  msg({
54
- message: "Sessions and password",
54
+ message: "Sign-in, export, and deletion",
55
55
  comment:
56
56
  "@context: Settings item description for account settings on the settings home page",
57
57
  }),
@@ -171,7 +171,7 @@ export function SettingsRootContent({
171
171
  )}
172
172
  description={i18n._(
173
173
  msg({
174
- message: "Header links, home feed, and overflow menu",
174
+ message: "Header links, built-in destinations, and overflow menu",
175
175
  comment: "@context: Settings item description for navigation",
176
176
  }),
177
177
  )}
@@ -333,7 +333,7 @@ export function SettingsRootContent({
333
333
  tone="subtle"
334
334
  name={i18n._(
335
335
  msg({
336
- message: "Account",
336
+ message: "Account & Data",
337
337
  comment: "@context: Settings item — account settings",
338
338
  }),
339
339
  )}
@@ -352,10 +352,15 @@ describe("timeline cards", () => {
352
352
  new URL("../../../styles/ui.css", import.meta.url),
353
353
  "utf8",
354
354
  );
355
-
356
- expect(uiCss).toMatch(
357
- /\.compose-reply-context-body\s*:is\([\s\S]*\[data-post-body\]\.prose[\s\S]*\)\s*\{[\s\S]*width:\s*100%;[\s\S]*max-width:\s*none;/,
355
+ const unlayeredCssStart = uiCss.indexOf(" * Home description");
356
+ const resetRule = uiCss.match(
357
+ /\.compose-reply-context-body\s*:is\([^{}]*\[data-post-body\]\.prose[^{}]*\)\s*\{[^{}]*width:\s*100%;[^{}]*max-width:\s*none;[^{}]*\}/,
358
358
  );
359
+
360
+ expect(unlayeredCssStart).toBeGreaterThan(0);
361
+ expect(resetRule?.index).toBeGreaterThan(unlayeredCssStart);
362
+ expect(resetRule?.[0]).toContain("width: 100%;");
363
+ expect(resetRule?.[0]).toContain("max-width: none;");
359
364
  });
360
365
 
361
366
  it("keeps link and quote attachments hidden in compact mode", () => {
@@ -434,7 +434,7 @@ export const BaseLayout: FC<PropsWithChildren<BaseLayoutProps>> = ({
434
434
  responsive header layout before external CSS/JS loads */}
435
435
  <style
436
436
  dangerouslySetInnerHTML={{
437
- __html: `.site-header-search-link,.site-header-hamburger,.site-header-more-responsive-only,.site-header-more-link-responsive,.site-header-more-divider-responsive{display:none!important}@media(max-width:1200px){.site-header-search-form{display:none!important}.site-header-search-link{display:inline-flex!important}}@media(max-width:960px){.site-header-link-collapse-lg{display:none!important}.site-header-more-responsive-only.site-header-more-tier-lg{display:inline-flex!important}.site-header-more-link-show-lg{display:flex!important}.site-header-more-divider-responsive{display:block!important}}@media(max-width:780px){.site-header-link-collapse-md{display:none!important}.site-header-more-responsive-only.site-header-more-tier-md{display:inline-flex!important}.site-header-more-link-show-md{display:flex!important}}@media(max-width:580px){.site-header-link-collapse-sm{display:none!important}.site-header-more-responsive-only.site-header-more-tier-sm{display:inline-flex!important}.site-header-more-link-show-sm{display:flex!important}}@media(max-width:480px){.site-header-nav,.site-header-more{display:none!important}.site-header-search-slot{display:flex!important}.site-header-hamburger{display:flex!important}.site-header-right{margin-left:.35rem}}`,
437
+ __html: `.site-header-search-link,.site-header-hamburger,.site-header-more-responsive-only,.site-header-more-link-responsive,.site-header-more-divider-responsive{display:none!important}@media(max-width:1200px){.site-header-search-form{display:none!important}.site-header-search-link{display:inline-flex!important}}@media(max-width:960px){.site-header-link-collapse-lg{display:none!important}.site-header-more-responsive-only.site-header-more-tier-lg{display:inline-flex!important}.site-header-more-link-show-lg{display:flex!important}.site-header-more-divider-show-lg{display:block!important}}@media(max-width:780px){.site-header-link-collapse-md{display:none!important}.site-header-more-responsive-only.site-header-more-tier-md{display:inline-flex!important}.site-header-more-link-show-md{display:flex!important}.site-header-more-divider-show-md{display:block!important}}@media(max-width:580px){.site-header-link-collapse-sm{display:none!important}.site-header-more-responsive-only.site-header-more-tier-sm{display:inline-flex!important}.site-header-more-link-show-sm{display:flex!important}.site-header-more-divider-show-sm{display:block!important}}@media(max-width:480px){.site-header-nav,.site-header-more{display:none!important}.site-header-search-slot{display:flex!important}.site-header-hamburger{display:flex!important}.site-header-right{margin-left:.35rem}}`,
438
438
  }}
439
439
  />
440
440
  {themeStyle && (