@jant/core 0.6.12 → 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.
Files changed (106) 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-CWJFPjuR.js +6 -0
  7. package/dist/{app-D-zAhayc.js → app-HnzmsaqU.js} +416 -444
  8. package/dist/client/.vite/manifest.json +3 -3
  9. package/dist/client/_assets/{client-S1cdvulk.js → client-3_OaxhTs.js} +1 -1
  10. package/dist/client/_assets/{client-BUxR-E8O.css → client-DCrcHVJl.css} +1 -1
  11. package/dist/client/_assets/{client-auth-YW92ZH7u.js → client-auth-TsYq90xm.js} +570 -567
  12. package/dist/{export-Ckwh4BiE.js → export-Dfbq9aot.js} +10 -13
  13. package/dist/{github-sync-Dl9wQMar.js → github-sync--PSoE-Ne.js} +1 -1
  14. package/dist/{github-sync-BMmK3JZ0.js → github-sync-BGTLx8Mf.js} +2 -2
  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-compose-editor.test.ts +94 -2
  25. package/src/client/components/__tests__/jant-nav-manager.test.ts +107 -22
  26. package/src/client/components/__tests__/jant-settings-general.test.ts +1 -0
  27. package/src/client/components/compose-title-from-heading.ts +66 -0
  28. package/src/client/components/compose-types.ts +8 -0
  29. package/src/client/components/inline-image-issues.ts +17 -0
  30. package/src/client/components/jant-compose-dialog.ts +2 -0
  31. package/src/client/components/jant-compose-editor.ts +78 -1
  32. package/src/client/components/jant-compose-fullscreen.ts +2 -0
  33. package/src/client/components/jant-nav-manager.ts +245 -155
  34. package/src/client/components/nav-manager-types.ts +0 -10
  35. package/src/client/site-header-nav.d.ts +1 -0
  36. package/src/client/site-header-nav.js +105 -47
  37. package/src/client/tiptap/__tests__/block-insertion.test.ts +35 -0
  38. package/src/client/tiptap/__tests__/image-input-rules.test.ts +117 -0
  39. package/src/client/tiptap/create-editor.ts +7 -0
  40. package/src/client/tiptap/extensions.ts +7 -2
  41. package/src/client/tiptap/image-input-rules.ts +48 -0
  42. package/src/client/tiptap/image-node.ts +189 -7
  43. package/src/client-auth.ts +0 -1
  44. package/src/i18n/locales/public/en.po +20 -4
  45. package/src/i18n/locales/public/en.ts +1 -1
  46. package/src/i18n/locales/public/zh-Hans.po +20 -4
  47. package/src/i18n/locales/public/zh-Hans.ts +1 -1
  48. package/src/i18n/locales/public/zh-Hant.po +20 -4
  49. package/src/i18n/locales/public/zh-Hant.ts +1 -1
  50. package/src/i18n/locales/settings/en.po +23 -20
  51. package/src/i18n/locales/settings/en.ts +1 -1
  52. package/src/i18n/locales/settings/zh-Hans.po +23 -20
  53. package/src/i18n/locales/settings/zh-Hans.ts +1 -1
  54. package/src/i18n/locales/settings/zh-Hant.po +23 -20
  55. package/src/i18n/locales/settings/zh-Hant.ts +1 -1
  56. package/src/lib/__tests__/navigation.test.ts +4 -37
  57. package/src/lib/__tests__/resolve-config.test.ts +6 -1
  58. package/src/lib/__tests__/view.test.ts +6 -30
  59. package/src/lib/github-sync-site-config.ts +0 -2
  60. package/src/lib/hugo-markdown.ts +1 -1
  61. package/src/lib/navigation.ts +5 -22
  62. package/src/lib/resolve-config.ts +9 -2
  63. package/src/lib/site-header-fragment.tsx +37 -0
  64. package/src/lib/view.ts +2 -7
  65. package/src/node/__tests__/cli-site-snapshot.test.ts +45 -45
  66. package/src/routes/api/__tests__/nav-items.test.ts +26 -0
  67. package/src/routes/api/export.ts +0 -2
  68. package/src/routes/api/internal/__tests__/uploads.test.ts +9 -13
  69. package/src/routes/api/internal/sites.ts +5 -5
  70. package/src/routes/api/nav-items.ts +25 -4
  71. package/src/routes/dash/settings.tsx +8 -2
  72. package/src/routes/feed/__tests__/feed.test.ts +42 -0
  73. package/src/routes/feed/__tests__/sitemap.test.ts +2 -13
  74. package/src/routes/feed/feed.ts +1 -0
  75. package/src/routes/feed/sitemap.ts +2 -6
  76. package/src/routes/pages/archive.tsx +1 -0
  77. package/src/routes/pages/collection.tsx +1 -0
  78. package/src/routes/pages/featured.tsx +0 -4
  79. package/src/routes/pages/home.tsx +4 -51
  80. package/src/routes/pages/latest.tsx +2 -51
  81. package/src/services/__tests__/github-app-installations.test.ts +10 -10
  82. package/src/services/__tests__/navigation.test.ts +41 -0
  83. package/src/services/__tests__/post-timeline.test.ts +56 -0
  84. package/src/services/__tests__/post.test.ts +33 -0
  85. package/src/services/__tests__/settings.test.ts +5 -20
  86. package/src/services/export-theme/assets/client-site.js +10 -10
  87. package/src/services/export-theme/layouts/_default/rss.xml +5 -15
  88. package/src/services/export-theme/layouts/index.html +26 -39
  89. package/src/services/export.ts +5 -10
  90. package/src/services/github-app-installations.ts +1 -3
  91. package/src/services/navigation.ts +44 -13
  92. package/src/services/post.ts +80 -57
  93. package/src/services/settings.ts +0 -10
  94. package/src/services/site-admin.ts +0 -2
  95. package/src/services/upload-session.ts +4 -18
  96. package/src/styles/tokens.css +2 -2
  97. package/src/styles/ui.css +200 -6
  98. package/src/types/bindings.ts +0 -1
  99. package/src/types/config.ts +3 -7
  100. package/src/ui/compose/ComposeDialog.tsx +28 -0
  101. package/src/ui/dash/appearance/NavigationContent.tsx +1 -1
  102. package/src/ui/dash/settings/AccountMenuContent.tsx +2 -2
  103. package/src/ui/dash/settings/SettingsRootContent.tsx +3 -3
  104. package/src/ui/layouts/SiteLayout.tsx +249 -216
  105. package/dist/app-B5P1b4TF.js +0 -6
  106. package/src/client/nav-manager-bridge.ts +0 -54
@@ -7,7 +7,7 @@
7
7
  * - SortableJS drag-and-drop reorder with immediate preview update
8
8
  * - Add link forms
9
9
  * - System nav item toggles with immediate list/preview update
10
- * - Dispatches events for update/delete (handled by bridge)
10
+ * - Applies server-rendered site header fragments after saved changes
11
11
  *
12
12
  * Light DOM only — BaseCoat and Tailwind classes apply directly.
13
13
  */
@@ -27,16 +27,24 @@ import {
27
27
  import { showConfirmDialog } from "../confirm.js";
28
28
  import { showToast } from "../toast.js";
29
29
  import { publicPath } from "../runtime-paths.js";
30
+ import { destroySiteHeaderNav, initSiteHeaderNav } from "../site-header-nav.js";
30
31
  import type {
31
32
  NavManagerCollection,
32
33
  NavManagerItem,
33
34
  NavManagerLabels,
34
- NavManagerUpdateDetail,
35
- NavManagerDeleteDetail,
36
35
  NavManagerSuggestedLink,
37
36
  SystemNavConfig,
38
37
  } from "./nav-manager-types.js";
39
38
 
39
+ const SITE_HEADER_REQUEST_HEADER = "X-Jant-Site-Header";
40
+ const INCLUDE_SITE_HEADER_RESPONSE = "include";
41
+
42
+ type NavManagerMutationResponse<T extends object> = T & {
43
+ headerHtml?: string;
44
+ };
45
+
46
+ type SortableEndEvent = Parameters<NonNullable<SortableOptions["onEnd"]>>[0];
47
+
40
48
  export class JantNavManager extends LitElement {
41
49
  static properties = {
42
50
  items: { type: Array },
@@ -174,6 +182,69 @@ export class JantNavManager extends LitElement {
174
182
  this.#closePreviewMore();
175
183
  }
176
184
 
185
+ #jsonMutationHeaders(): Record<string, string> {
186
+ return {
187
+ "Content-Type": "application/json",
188
+ Accept: "application/json",
189
+ [SITE_HEADER_REQUEST_HEADER]: INCLUDE_SITE_HEADER_RESPONSE,
190
+ };
191
+ }
192
+
193
+ #deleteMutationHeaders(): Record<string, string> {
194
+ return {
195
+ Accept: "application/json",
196
+ [SITE_HEADER_REQUEST_HEADER]: INCLUDE_SITE_HEADER_RESPONSE,
197
+ };
198
+ }
199
+
200
+ #stripHeaderHtml<T extends object>(
201
+ response: NavManagerMutationResponse<T>,
202
+ ): T {
203
+ const body = { ...response };
204
+ delete (body as { headerHtml?: string }).headerHtml;
205
+ return body as T;
206
+ }
207
+
208
+ #normalizeItem(item: NavManagerItem): NavManagerItem {
209
+ const systemLabel =
210
+ item.type === "system"
211
+ ? this.systemNavItems.find((config) => config.key === item.systemKey)
212
+ ?.label
213
+ : undefined;
214
+ const displayLabel =
215
+ item.label.trim() || systemLabel || item.displayLabel || item.label;
216
+
217
+ return { ...item, displayLabel };
218
+ }
219
+
220
+ #applySiteHeaderHtml(headerHtml: string | undefined) {
221
+ if (!headerHtml) return;
222
+
223
+ const template = document.createElement("template");
224
+ template.innerHTML = headerHtml.trim();
225
+
226
+ const selectors = [
227
+ '[data-site-header-fragment="header"]',
228
+ '[data-site-header-fragment="drawer-backdrop"]',
229
+ '[data-site-header-fragment="drawer"]',
230
+ ];
231
+ let replaced = false;
232
+
233
+ destroySiteHeaderNav(document);
234
+ for (const selector of selectors) {
235
+ const current = document.querySelector<HTMLElement>(selector);
236
+ const next = template.content.querySelector<HTMLElement>(selector);
237
+ if (!current || !next) continue;
238
+
239
+ current.replaceWith(next);
240
+ replaced = true;
241
+ }
242
+
243
+ if (replaced) {
244
+ initSiteHeaderNav(document);
245
+ }
246
+ }
247
+
177
248
  // ===========================================================================
178
249
  // SortableJS
179
250
  // ===========================================================================
@@ -211,119 +282,115 @@ export class JantNavManager extends LitElement {
211
282
  this.#revertNextSibling = captureSortableRevertNextSibling(evt);
212
283
  },
213
284
  onEnd: (evt) => {
214
- const targetList = evt.to;
215
- const sourceList = evt.from;
216
- const crossList = sourceList !== targetList;
217
- const targetPlacement: "header" | "more" =
218
- targetList.id === "nav-items-header" ? "header" : "more";
219
- const headerList = this.querySelector<HTMLElement>("#nav-items-header");
220
- const moreList = this.querySelector<HTMLElement>("#nav-items-more");
221
-
222
- const movedId = evt.item?.dataset?.navId;
223
- if (!movedId || !headerList || !moreList) {
224
- this.#revertNextSibling = null;
225
- return;
226
- }
285
+ void this.#handleSortableEnd(evt);
286
+ },
287
+ };
288
+ }
227
289
 
228
- const headerIds = readSortableDataIds(
229
- headerList,
230
- "[data-nav-id]",
231
- "navId",
232
- );
233
- const moreIds = readSortableDataIds(moreList, "[data-nav-id]", "navId");
234
-
235
- if (crossList) {
236
- evt.item.parentNode?.removeChild(evt.item);
237
- if (this.#revertNextSibling) {
238
- sourceList.insertBefore(evt.item, this.#revertNextSibling);
239
- } else if (
240
- evt.oldIndex != null &&
241
- evt.oldIndex < sourceList.children.length
242
- ) {
243
- sourceList.insertBefore(
244
- evt.item,
245
- sourceList.children[evt.oldIndex] ?? null,
246
- );
247
- } else {
248
- sourceList.appendChild(evt.item);
249
- }
250
- } else {
251
- revertSortableDomMove(targetList, evt, this.#revertNextSibling);
252
- }
290
+ async #handleSortableEnd(evt: SortableEndEvent) {
291
+ const targetList = evt.to;
292
+ const sourceList = evt.from;
293
+ const crossList = sourceList !== targetList;
294
+ const targetPlacement: "header" | "more" =
295
+ targetList.id === "nav-items-header" ? "header" : "more";
296
+ const headerList = this.querySelector<HTMLElement>("#nav-items-header");
297
+ const moreList = this.querySelector<HTMLElement>("#nav-items-more");
298
+ const previousItems = this._items.map((item) => ({ ...item }));
253
299
 
254
- this.#revertNextSibling = null;
255
- this.#destroySortables();
300
+ const movedId = evt.item?.dataset?.navId;
301
+ if (!movedId || !headerList || !moreList) {
302
+ this.#revertNextSibling = null;
303
+ return;
304
+ }
256
305
 
257
- const orderedIds = [...new Set([...headerIds, ...moreIds])];
258
- const itemMap = new Map(
259
- this._items.map((item) => [
260
- item.id,
261
- item.id === movedId
262
- ? { ...item, placement: targetPlacement }
263
- : { ...item },
264
- ]),
306
+ const headerIds = readSortableDataIds(headerList, "[data-nav-id]", "navId");
307
+ const moreIds = readSortableDataIds(moreList, "[data-nav-id]", "navId");
308
+
309
+ if (crossList) {
310
+ evt.item.parentNode?.removeChild(evt.item);
311
+ if (this.#revertNextSibling) {
312
+ sourceList.insertBefore(evt.item, this.#revertNextSibling);
313
+ } else if (
314
+ evt.oldIndex != null &&
315
+ evt.oldIndex < sourceList.children.length
316
+ ) {
317
+ sourceList.insertBefore(
318
+ evt.item,
319
+ sourceList.children[evt.oldIndex] ?? null,
265
320
  );
266
- const reorderedItems = orderedIds
267
- .map((id) => itemMap.get(id))
268
- .filter((item): item is NavManagerItem => item !== undefined);
269
- const remainingItems = this._items
270
- .filter((item) => !orderedIds.includes(item.id))
271
- .map((item) =>
272
- item.id === movedId
273
- ? { ...item, placement: targetPlacement }
274
- : { ...item },
275
- );
276
- this._items = [...reorderedItems, ...remainingItems];
277
-
278
- const targetIds = targetPlacement === "header" ? headerIds : moreIds;
279
- const {
280
- movedId: persistedId,
281
- afterId,
282
- beforeId,
283
- } = getSortableMove(targetIds, evt.newIndex);
284
- if (!persistedId) return;
285
-
286
- if (crossList) {
287
- fetch(`/api/nav-items/${movedId}`, {
288
- method: "PUT",
289
- headers: { "Content-Type": "application/json" },
290
- body: JSON.stringify({ placement: targetPlacement }),
291
- })
292
- .then(async (res) => {
293
- if (!res.ok) return false;
294
-
295
- const moveRes = await fetch(
296
- `/api/nav-items/${persistedId}/move`,
297
- {
298
- method: "PUT",
299
- headers: { "Content-Type": "application/json" },
300
- body: JSON.stringify({
301
- after: afterId ?? null,
302
- before: beforeId ?? null,
303
- }),
304
- },
305
- );
306
- return moveRes.ok;
307
- })
308
- .then((ok) => {
309
- if (ok) showToast(this.labels.placementSaved);
310
- else showToast(this.labels.saveFailed, "error");
311
- });
312
- } else {
313
- fetch(`/api/nav-items/${persistedId}/move`, {
314
- method: "PUT",
315
- headers: { "Content-Type": "application/json" },
316
- body: JSON.stringify({
317
- after: afterId ?? null,
318
- before: beforeId ?? null,
319
- }),
320
- }).then((res) => {
321
- if (res.ok) showToast(this.labels.orderSaved);
322
- else showToast(this.labels.saveFailed, "error");
323
- });
324
- }
325
- },
326
- };
321
+ } else {
322
+ sourceList.appendChild(evt.item);
323
+ }
324
+ } else {
325
+ revertSortableDomMove(targetList, evt, this.#revertNextSibling);
326
+ }
327
+
328
+ this.#revertNextSibling = null;
329
+ this.#destroySortables();
330
+
331
+ const orderedIds = [...new Set([...headerIds, ...moreIds])];
332
+ const itemMap = new Map(
333
+ this._items.map((item) => [
334
+ item.id,
335
+ item.id === movedId
336
+ ? { ...item, placement: targetPlacement }
337
+ : { ...item },
338
+ ]),
339
+ );
340
+ const reorderedItems = orderedIds
341
+ .map((id) => itemMap.get(id))
342
+ .filter((item): item is NavManagerItem => item !== undefined);
343
+ const remainingItems = this._items
344
+ .filter((item) => !orderedIds.includes(item.id))
345
+ .map((item) =>
346
+ item.id === movedId
347
+ ? { ...item, placement: targetPlacement }
348
+ : { ...item },
349
+ );
350
+ this._items = [...reorderedItems, ...remainingItems];
351
+
352
+ const targetIds = targetPlacement === "header" ? headerIds : moreIds;
353
+ const {
354
+ movedId: persistedId,
355
+ afterId,
356
+ beforeId,
357
+ } = getSortableMove(targetIds, evt.newIndex);
358
+ if (!persistedId) return;
359
+
360
+ try {
361
+ if (crossList) {
362
+ const placementRes = await fetch(`/api/nav-items/${movedId}`, {
363
+ method: "PUT",
364
+ headers: {
365
+ "Content-Type": "application/json",
366
+ Accept: "application/json",
367
+ },
368
+ body: JSON.stringify({ placement: targetPlacement }),
369
+ });
370
+ if (!placementRes.ok) throw new Error(`HTTP ${placementRes.status}`);
371
+ }
372
+
373
+ const moveRes = await fetch(`/api/nav-items/${persistedId}/move`, {
374
+ method: "PUT",
375
+ headers: this.#jsonMutationHeaders(),
376
+ body: JSON.stringify({
377
+ after: afterId ?? null,
378
+ before: beforeId ?? null,
379
+ }),
380
+ });
381
+ if (!moveRes.ok) throw new Error(`HTTP ${moveRes.status}`);
382
+
383
+ const moved =
384
+ (await moveRes.json()) as NavManagerMutationResponse<NavManagerItem>;
385
+ this.#applySiteHeaderHtml(moved.headerHtml);
386
+ showToast(
387
+ crossList ? this.labels.placementSaved : this.labels.orderSaved,
388
+ );
389
+ } catch {
390
+ this.#destroySortables();
391
+ this._items = previousItems;
392
+ showToast(this.labels.saveFailed, "error");
393
+ }
327
394
  }
328
395
 
329
396
  // ===========================================================================
@@ -340,25 +407,36 @@ export class JantNavManager extends LitElement {
340
407
  }
341
408
  }
342
409
 
343
- #handleUpdate(item: NavManagerItem) {
410
+ async #handleUpdate(item: NavManagerItem) {
344
411
  const label = this._editLabel.trim();
345
412
  if (!label && item.type !== "system") {
346
413
  showToast(this.labels.labelRequired, "error");
347
414
  return;
348
415
  }
349
416
 
350
- const detail: NavManagerUpdateDetail = {
351
- id: item.id,
352
- label,
353
- ...(item.type === "link" && { url: this._editUrl.trim() }),
354
- };
417
+ try {
418
+ const res = await fetch(`/api/nav-items/${item.id}`, {
419
+ method: "PUT",
420
+ headers: this.#jsonMutationHeaders(),
421
+ body: JSON.stringify({
422
+ label,
423
+ ...(item.type === "link" && { url: this._editUrl.trim() }),
424
+ }),
425
+ });
426
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
355
427
 
356
- this.dispatchEvent(
357
- new CustomEvent<NavManagerUpdateDetail>("jant:nav-update", {
358
- bubbles: true,
359
- detail,
360
- }),
361
- );
428
+ const response =
429
+ (await res.json()) as NavManagerMutationResponse<NavManagerItem>;
430
+ const updated = this.#normalizeItem(this.#stripHeaderHtml(response));
431
+ this.#destroySortables();
432
+ this._items = this._items.map((current) =>
433
+ current.id === updated.id ? updated : current,
434
+ );
435
+ this._editingId = null;
436
+ this.#applySiteHeaderHtml(response.headerHtml);
437
+ } catch {
438
+ showToast(this.labels.saveFailed, "error");
439
+ }
362
440
  }
363
441
 
364
442
  async #handleDelete(item: NavManagerItem) {
@@ -376,12 +454,23 @@ export class JantNavManager extends LitElement {
376
454
  });
377
455
  if (!confirmed) return;
378
456
 
379
- this.dispatchEvent(
380
- new CustomEvent<NavManagerDeleteDetail>("jant:nav-delete", {
381
- bubbles: true,
382
- detail: { id: item.id },
383
- }),
384
- );
457
+ try {
458
+ const res = await fetch(`/api/nav-items/${item.id}`, {
459
+ method: "DELETE",
460
+ headers: this.#deleteMutationHeaders(),
461
+ });
462
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
463
+
464
+ const response = (await res.json()) as NavManagerMutationResponse<{
465
+ success: true;
466
+ }>;
467
+ this.#destroySortables();
468
+ this._items = this._items.filter((current) => current.id !== item.id);
469
+ this._editingId = null;
470
+ this.#applySiteHeaderHtml(response.headerHtml);
471
+ } catch {
472
+ showToast(this.labels.deleteFailed, "error");
473
+ }
385
474
  }
386
475
 
387
476
  // ===========================================================================
@@ -400,21 +489,21 @@ export class JantNavManager extends LitElement {
400
489
  try {
401
490
  const res = await fetch("/api/nav-items", {
402
491
  method: "POST",
403
- headers: {
404
- "Content-Type": "application/json",
405
- Accept: "application/json",
406
- },
492
+ headers: this.#jsonMutationHeaders(),
407
493
  body: JSON.stringify({ type: "link", label, url, placement: "header" }),
408
494
  });
409
495
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
410
496
 
411
- const created: NavManagerItem = await res.json();
497
+ const response =
498
+ (await res.json()) as NavManagerMutationResponse<NavManagerItem>;
499
+ const created = this.#normalizeItem(this.#stripHeaderHtml(response));
412
500
  this.#destroySortables();
413
501
  this._items = [...this._items, created];
414
502
  this._newLinkLabel = "";
415
503
  this._newLinkUrl = "";
416
504
  this._showLinkForm = false;
417
505
  document.removeEventListener("click", this.#closeLinkForm);
506
+ this.#applySiteHeaderHtml(response.headerHtml);
418
507
  } catch {
419
508
  showToast(this.labels.saveFailed, "error");
420
509
  } finally {
@@ -433,10 +522,7 @@ export class JantNavManager extends LitElement {
433
522
  try {
434
523
  const res = await fetch("/api/nav-items", {
435
524
  method: "POST",
436
- headers: {
437
- "Content-Type": "application/json",
438
- Accept: "application/json",
439
- },
525
+ headers: this.#jsonMutationHeaders(),
440
526
  body: JSON.stringify({
441
527
  type: "collection",
442
528
  collectionId,
@@ -445,10 +531,13 @@ export class JantNavManager extends LitElement {
445
531
  });
446
532
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
447
533
 
448
- const created: NavManagerItem = await res.json();
534
+ const response =
535
+ (await res.json()) as NavManagerMutationResponse<NavManagerItem>;
536
+ const created = this.#normalizeItem(this.#stripHeaderHtml(response));
449
537
  this.#destroySortables();
450
538
  this._items = [...this._items, created];
451
539
  this.#closeCollectionPicker();
540
+ this.#applySiteHeaderHtml(response.headerHtml);
452
541
  } catch {
453
542
  showToast(this.labels.saveFailed, "error");
454
543
  } finally {
@@ -530,17 +619,17 @@ export class JantNavManager extends LitElement {
530
619
 
531
620
  const res = await fetch("/api/nav-items", {
532
621
  method: "POST",
533
- headers: {
534
- "Content-Type": "application/json",
535
- Accept: "application/json",
536
- },
622
+ headers: this.#jsonMutationHeaders(),
537
623
  body: JSON.stringify(body),
538
624
  });
539
625
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
540
626
 
541
- const created: NavManagerItem = await res.json();
627
+ const response =
628
+ (await res.json()) as NavManagerMutationResponse<NavManagerItem>;
629
+ const created = this.#normalizeItem(this.#stripHeaderHtml(response));
542
630
  this.#destroySortables();
543
631
  this._items = [...this._items, created];
632
+ this.#applySiteHeaderHtml(response.headerHtml);
544
633
  showToast(this.labels.suggestedLinkAdded);
545
634
  } catch {
546
635
  showToast(this.labels.saveFailed, "error");
@@ -566,10 +655,7 @@ export class JantNavManager extends LitElement {
566
655
  if (enabled) {
567
656
  const res = await fetch("/api/nav-items", {
568
657
  method: "POST",
569
- headers: {
570
- "Content-Type": "application/json",
571
- Accept: "application/json",
572
- },
658
+ headers: this.#jsonMutationHeaders(),
573
659
  body: JSON.stringify({
574
660
  type: "system",
575
661
  systemKey: config.key,
@@ -577,12 +663,12 @@ export class JantNavManager extends LitElement {
577
663
  });
578
664
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
579
665
 
580
- const created: NavManagerItem = await res.json();
666
+ const response =
667
+ (await res.json()) as NavManagerMutationResponse<NavManagerItem>;
668
+ const created = this.#normalizeItem(this.#stripHeaderHtml(response));
581
669
  this.#destroySortables();
582
- this._items = [
583
- ...this._items,
584
- { ...created, displayLabel: config.label },
585
- ];
670
+ this._items = [...this._items, created];
671
+ this.#applySiteHeaderHtml(response.headerHtml);
586
672
  } else {
587
673
  const existing = this._items.find(
588
674
  (item) => item.type === "system" && item.systemKey === config.key,
@@ -590,12 +676,16 @@ export class JantNavManager extends LitElement {
590
676
  if (existing) {
591
677
  const res = await fetch(`/api/nav-items/${existing.id}`, {
592
678
  method: "DELETE",
593
- headers: { Accept: "application/json" },
679
+ headers: this.#deleteMutationHeaders(),
594
680
  });
595
681
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
596
682
 
683
+ const response = (await res.json()) as NavManagerMutationResponse<{
684
+ success: true;
685
+ }>;
597
686
  this.#destroySortables();
598
687
  this._items = this._items.filter((item) => item.id !== existing.id);
688
+ this.#applySiteHeaderHtml(response.headerHtml);
599
689
  }
600
690
  }
601
691
  } catch {
@@ -81,13 +81,3 @@ export interface NavManagerLabels {
81
81
  placementSaved: string;
82
82
  cancel: string;
83
83
  }
84
-
85
- export interface NavManagerUpdateDetail {
86
- id: string;
87
- label: string;
88
- url?: string;
89
- }
90
-
91
- export interface NavManagerDeleteDetail {
92
- id: string;
93
- }
@@ -1 +1,2 @@
1
1
  export function initSiteHeaderNav(root?: object): void;
2
+ export function destroySiteHeaderNav(root?: object): void;