@jxsuite/studio 3.0.0 → 4.1.0
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/README.md +5 -5
- package/dist/chunks/{2020-xp8vnwma.js → 2020-n6tfwnda.js} +2 -2
- package/dist/chunks/Overlay.dev-etqjwyf9.js +23 -0
- package/dist/chunks/cf-account-picker-en86v7x7.js +163 -0
- package/dist/chunks/{cf-settings-zj2cj1ar.js → cf-settings-45fgyx8y.js} +6 -5
- package/dist/chunks/{components-z6cnkyy1.js → components-zmx10t62.js} +6 -5
- package/dist/chunks/content-source-48xg86wj.js +49 -0
- package/dist/chunks/{convert-to-repeater-6vvx96n2.js → convert-to-repeater-43tfn7cs.js} +33 -25
- package/dist/chunks/entry-commands-e63f1xc0.js +46 -0
- package/dist/chunks/{extension-sections-kwzvpemr.js → extension-sections-txv08xxg.js} +34 -26
- package/dist/chunks/file-ops-wbh4hhsx.js +48 -0
- package/dist/chunks/files-3kfkwhk4.js +93 -0
- package/dist/chunks/{focus-trap.esm-ggawf1hy.js → focus-trap.esm-9t2drp0g.js} +6 -5
- package/dist/chunks/{format-host-8fr3545e.js → format-host-w16q81tf.js} +3 -3
- package/dist/chunks/fs-events-hrrg328g.js +21 -0
- package/dist/chunks/{git-panel-g01d93vc.js → git-panel-532whr1y.js} +22 -16
- package/dist/chunks/{index-nj44jpzj.js → index-wcnt3r9g.js} +2 -2
- package/dist/chunks/{jx-validate-jj9rnt4n.js → jx-validate-8er6gbxy.js} +6 -4
- package/dist/chunks/layers-panel-07hm7g5j.js +67 -0
- package/dist/chunks/layers-q75tj2mf.js +45 -0
- package/dist/chunks/lit-html-j055mkh8.js +22 -0
- package/dist/chunks/monaco-buffer-dqw0xxpx.js +25 -0
- package/dist/chunks/{monaco-setup-a5wkmbgh.js → monaco-setup-vx9bhp1d.js} +4 -4
- package/dist/chunks/notify-yjm79n4s.js +30 -0
- package/dist/chunks/parse-24d0gay3.js +19 -0
- package/dist/chunks/platform-ba1j1gbh.js +16 -0
- package/dist/chunks/publish-panel-v9st96ja.js +29 -0
- package/dist/chunks/quick-search-zccjp6gv.js +66 -0
- package/dist/chunks/references-3qx8fkyp.js +29 -0
- package/dist/chunks/right-panel-vm3b6kwy.js +46 -0
- package/dist/chunks/sp-overlay.dev-qgvhnxf4.js +18 -0
- package/dist/chunks/sp-popover.dev-n2etmqdp.js +9 -0
- package/dist/chunks/sp-progress-circle.dev-hryhj0es.js +15 -0
- package/dist/chunks/sp-tooltip.dev-4sastdnw.js +15 -0
- package/dist/chunks/{sp-tray.dev-64v4pxw8.js → sp-tray.dev-tkbscm06.js} +5 -5
- package/dist/chunks/{studio-wdt9y92a.js → studio-0bq6eh7v.js} +2 -2
- package/dist/chunks/studio-1ngbejft.js +181 -0
- package/dist/chunks/studio-1wagb550.js +118 -0
- package/dist/chunks/{studio-j595e748.js → studio-2b946z26.js} +172 -95
- package/dist/chunks/{studio-pkp61mh8.js → studio-3bqpr0zh.js} +25 -17
- package/dist/chunks/{studio-k4jvjcfx.js → studio-4b5z1x43.js} +46 -12
- package/dist/chunks/{studio-ry1vz5sv.js → studio-4n2b5gzb.js} +15 -13
- package/dist/chunks/{studio-p5dja96s.js → studio-4r1pv6zf.js} +2 -2
- package/dist/chunks/{studio-kvfx9z2c.js → studio-4rzyrtjz.js} +70 -31
- package/dist/chunks/{studio-tzx3xmvx.js → studio-5fx3g9jg.js} +33 -52
- package/dist/chunks/{studio-rh1cnamv.js → studio-5myknm0a.js} +565 -135
- package/dist/chunks/studio-6yw4y3my.js +91 -0
- package/dist/chunks/{studio-0rqqhrdy.js → studio-7nte2hj4.js} +2 -2
- package/dist/chunks/studio-92ab2jqe.js +282 -0
- package/dist/chunks/{studio-6z5eyjwn.js → studio-950k0dx6.js} +72 -135
- package/dist/chunks/{studio-sas7mt3k.js → studio-9ab8gqsm.js} +2 -2
- package/dist/chunks/studio-9n4mrmnb.js +184 -0
- package/dist/chunks/{studio-hcxzyk3x.js → studio-ahn3pdgx.js} +6 -6
- package/dist/chunks/studio-b55y6ewx.js +498 -0
- package/dist/chunks/{studio-s519zc99.js → studio-bgxt93ga.js} +3 -3
- package/dist/chunks/studio-cft9r4r7.js +119 -0
- package/dist/chunks/{studio-dc8s8heb.js → studio-cmb03bg5.js} +6904 -8321
- package/dist/chunks/studio-dm19q43r.js +195 -0
- package/dist/chunks/{studio-2x9zbxmx.js → studio-ef49pnzx.js} +11 -9
- package/dist/chunks/{studio-ay8wcr2b.js → studio-eqq1w638.js} +7 -9
- package/dist/chunks/{studio-5efrtn1m.js → studio-ew0djmhg.js} +23 -19
- package/dist/chunks/{studio-tr20m8g8.js → studio-f3wqjtk5.js} +39 -3
- package/dist/chunks/{studio-xntfdy1x.js → studio-fbf92ckf.js} +36 -23
- package/dist/chunks/{studio-4q0wznzf.js → studio-gcye7qka.js} +442 -126
- package/dist/chunks/{studio-7p35254k.js → studio-gfgb8pe6.js} +36 -193
- package/dist/chunks/studio-jc994dnr.js +1066 -0
- package/dist/chunks/{studio-hj2se583.js → studio-jkftxyvz.js} +69 -3423
- package/dist/chunks/{studio-5x5774ak.js → studio-kryvsw6j.js} +3 -3
- package/dist/chunks/{studio-kfpek536.js → studio-kyh9avvv.js} +2 -2
- package/dist/chunks/{studio-twsafr1e.js → studio-m3hwn7y1.js} +3 -3
- package/dist/chunks/studio-msd847zn.js +1315 -0
- package/dist/chunks/{studio-g5gdqnyd.js → studio-mtfnaqs9.js} +19 -19
- package/dist/chunks/{studio-7ejemhej.js → studio-n6j9t10q.js} +6 -6
- package/dist/chunks/{studio-vhk5fmfv.js → studio-n8b9wqwy.js} +5 -5
- package/dist/chunks/{studio-cjm7fdjf.js → studio-p4ngc046.js} +8 -104
- package/dist/chunks/studio-q43a5y9h.js +394 -0
- package/dist/chunks/studio-q7v10cg2.js +106 -0
- package/dist/chunks/{studio-6kv3apa9.js → studio-q9jd1a60.js} +27 -11
- package/dist/chunks/studio-qcy05dp8.js +1743 -0
- package/dist/chunks/{studio-a0yp7syn.js → studio-qjy2z9bv.js} +29 -132
- package/dist/chunks/{studio-2w11grav.js → studio-qq6jym04.js} +3 -3
- package/dist/chunks/{studio-3fk1ac9z.js → studio-rdx8jcxy.js} +4 -4
- package/dist/chunks/{studio-4mqb9a0j.js → studio-rn9ta2mz.js} +4 -4
- package/dist/chunks/studio-t7qb9db8.js +665 -0
- package/dist/chunks/studio-tmc5emrw.js +8 -0
- package/dist/chunks/{studio-r5xbtctf.js → studio-trxrabqf.js} +292 -2297
- package/dist/chunks/studio-wcy8trfx.js +263 -0
- package/dist/chunks/{studio-3dkcd4c5.js → studio-x4qkdat5.js} +62 -57
- package/dist/chunks/studio-x813zp48.js +195 -0
- package/dist/chunks/{studio-qpfq7xhm.js → studio-xrapxwwc.js} +22 -285
- package/dist/chunks/{studio-xm3dbxvn.js → studio-zczps70q.js} +2 -2
- package/dist/chunks/{studio-8ezdegcx.js → studio-zqazsct2.js} +40 -36
- package/dist/chunks/workspace-fjx52ryv.js +89 -0
- package/dist/iframe-entry.js +141 -56
- package/dist/manifest.json +7 -0
- package/dist/studio.js +2605 -1950
- package/favicon.ico +0 -0
- package/index.html +1 -0
- package/package.json +20 -18
- package/src/about/about-modal.ts +4 -3
- package/src/browse/library-pane.ts +57 -3
- package/src/canvas/canvas-render.ts +23 -1
- package/src/canvas/iframe-patch.ts +47 -21
- package/src/canvas/iframe-render.ts +22 -6
- package/src/commands/app-commands.ts +4 -0
- package/src/commands/command-args.ts +29 -0
- package/src/commands/context.ts +15 -1
- package/src/commands/defaults.ts +29 -1
- package/src/commands/levels.ts +16 -0
- package/src/commands/live-context.ts +3 -0
- package/src/content/collection-match.ts +267 -0
- package/src/content/entry-commands.ts +38 -29
- package/src/content/entry-fields.ts +6 -0
- package/src/editor/shortcuts.ts +5 -0
- package/src/files/file-ops.ts +10 -42
- package/src/files/files.ts +487 -59
- package/src/files/fs-events.ts +9 -0
- package/src/files/gitignore.ts +432 -0
- package/src/files/media-paths.ts +13 -42
- package/src/files/media-usage.ts +41 -131
- package/src/files/serialize-document.ts +60 -0
- package/src/format/convert-file.ts +511 -0
- package/src/format/format-choices.ts +199 -0
- package/src/grid/grid-view.ts +16 -2
- package/src/hosting/document.ts +2 -0
- package/src/hosting/layout.ts +8 -0
- package/src/media/media-open.ts +58 -0
- package/src/media/media-pane.ts +310 -0
- package/src/new-project/import-tab.ts +357 -163
- package/src/new-project/location-fields.ts +8 -5
- package/src/new-project/new-project-modal.ts +27 -34
- package/src/packages/ensure-deps.ts +14 -7
- package/src/packages/jxsuite-update.ts +11 -11
- package/src/page-params.ts +10 -46
- package/src/panels/activity-bar.ts +45 -16
- package/src/panels/ai-chat/attached-context.ts +6 -1
- package/src/panels/ai-chat/chat-view.ts +255 -13
- package/src/panels/ai-chat/composer.ts +31 -97
- package/src/panels/ai-panel.ts +116 -2
- package/src/panels/navigator-panels.ts +7 -6
- package/src/panels/settings-menu.ts +799 -0
- package/src/panels/toolbar.ts +135 -15
- package/src/platforms/cloud.ts +261 -70
- package/src/platforms/devserver.ts +67 -33
- package/src/preview/preview-overlay.ts +226 -0
- package/src/publish/publish-panel.ts +62 -2
- package/src/services/ai-ask.ts +247 -0
- package/src/services/ai-import-tools.ts +532 -0
- package/src/services/ai-models.ts +108 -1
- package/src/services/ai-project-tools.ts +20 -26
- package/src/services/ai-system-prompt.ts +88 -2
- package/src/services/ai-turn-signal.ts +70 -0
- package/src/services/connection.ts +74 -0
- package/src/services/context-manager.ts +101 -1
- package/src/services/document-assistant.ts +45 -4
- package/src/services/import-client.ts +33 -6
- package/src/services/import-run.ts +158 -0
- package/src/services/import-seed.ts +120 -0
- package/src/services/jx-validate.ts +58 -6
- package/src/services/project-adoption.ts +71 -0
- package/src/services/references.ts +0 -0
- package/src/services/settings/definitions.ts +17 -0
- package/src/services/tool-executor.ts +45 -2
- package/src/settings/contexts-section.ts +45 -3
- package/src/settings/dependencies-editor.ts +43 -23
- package/src/settings/general-settings.ts +30 -12
- package/src/settings/preferences-accounts.ts +193 -12
- package/src/settings/preferences-dialog.ts +93 -59
- package/src/settings/preferences-sections.ts +57 -0
- package/src/settings/settings-document.ts +65 -9
- package/src/studio.ts +33 -6
- package/src/tabs/transact.ts +42 -19
- package/src/types.ts +129 -7
- package/src/ui/ai-managed-connect.ts +62 -13
- package/src/ui/ai-model-picker.ts +189 -0
- package/src/ui/cf-account-picker.ts +200 -0
- package/src/ui/choice-field.ts +72 -0
- package/src/ui/layers.ts +83 -5
- package/src/utils/studio-utils.ts +22 -52
- package/styles/forced-colors.css +7 -0
- package/styles/overlays.css +256 -0
- package/styles/panels.css +13 -1
- package/styles/shell.css +147 -0
- package/dist/chunks/Overlay.dev-d2ajy6sj.js +0 -23
- package/dist/chunks/content-source-7fy9qrm0.js +0 -40
- package/dist/chunks/files-xcybanxe.js +0 -70
- package/dist/chunks/layers-panel-27essgkc.js +0 -60
- package/dist/chunks/publish-panel-09sy2bab.js +0 -22
- package/dist/chunks/quick-search-sefb61v8.js +0 -55
- package/dist/chunks/right-panel-9r32jd5j.js +0 -39
- package/dist/chunks/sp-overlay.dev-s18hymn9.js +0 -18
- package/dist/chunks/sp-popover.dev-dj9x7e7a.js +0 -9
- package/dist/chunks/sp-progress-circle.dev-0zxcyz3r.js +0 -15
- package/dist/chunks/sp-tooltip.dev-5c6d1kjz.js +0 -15
- package/dist/chunks/studio-bw5dz6km.js +0 -608
- package/dist/chunks/studio-c31yb4t6.js +0 -55
- package/src/canvas/site-style-css.ts +0 -117
package/src/files/media-usage.ts
CHANGED
|
@@ -1,164 +1,74 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Media usage — "which pages use this image?", asked
|
|
2
|
+
* Media usage — "which pages use this image?", asked once.
|
|
3
3
|
*
|
|
4
|
-
* `services/references.ts`
|
|
5
|
-
* what every destructive confirmation says out loud. Media
|
|
6
|
-
*
|
|
7
|
-
* somewhere else.** `public/hero.jpg` is written `/hero.jpg`, which the compiler — and therefore
|
|
8
|
-
* the sweep — resolves to `hero.jpg`. Ask about the file and every reference to it is invisible;
|
|
9
|
-
* the query succeeds, returns zero, and the delete dialog says nothing else refers to it while
|
|
10
|
-
* seven pages break. A confident zero is the one answer a destructive dialog must never invent.
|
|
4
|
+
* `services/references.ts` answers "where is this used?" for every kind of file, and its answer is
|
|
5
|
+
* what every destructive confirmation says out loud. Media needs three things on top of it, and no
|
|
6
|
+
* longer needs the fourth this module was built for.
|
|
11
7
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* round trip), the same `invalidateUsages` on any filesystem event, the same `usageWarning`
|
|
17
|
-
* sentence in the confirmation, and the same `usageHeadline` wording on a panel.
|
|
8
|
+
* **What it still does.** A media path is normalized before it becomes a query (a leading `./` is
|
|
9
|
+
* not part of a file's identity), an empty path is a FAILURE rather than "nothing uses it", and
|
|
10
|
+
* {@link mediaUsageHeadline} renders the one line a media surface shows beside a file — where a
|
|
11
|
+
* failed query says **unknown**, which is a different fact from "unused" and has to read like one.
|
|
18
12
|
*
|
|
19
|
-
* **
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
13
|
+
* **What it no longer does, and why that matters.** A media file's authored reference usually
|
|
14
|
+
* resolves somewhere else: `public/hero.jpg` is written `/hero.jpg`, and a content asset is written
|
|
15
|
+
* `./images/hero.png` and republished at its collection's mount. `findReferences` used to resolve a
|
|
16
|
+
* rooted reference against the project root ALONE, so asking it about the file matched none of
|
|
17
|
+
* those and returned a confident zero — the one answer a destructive dialog must never invent. This
|
|
18
|
+
* module's answer was to enumerate every authored spelling of the file and union the results.
|
|
19
|
+
*
|
|
20
|
+
* That was a workaround in the wrong place. A client-side union can make a COUNT come out right and
|
|
21
|
+
* can do nothing at all about the REWRITE, which happens inside the engine — so the engine learned
|
|
22
|
+
* to resolve every lane itself (issue 239/241, `site-architecture.md` §9.3), and the union became a
|
|
23
|
+
* second implementation of a rule that now has one. It is gone: one path in, one query, one answer,
|
|
24
|
+
* and the lanes are the engine's business.
|
|
23
25
|
*
|
|
24
26
|
* @docs studio/projects/media
|
|
25
27
|
*/
|
|
26
28
|
|
|
27
|
-
import {
|
|
29
|
+
import { normalizeProjectPath } from "./media-paths";
|
|
28
30
|
import { loadUsages, peekUsages, retryUsages, usageHeadline } from "../services/references";
|
|
29
|
-
import type {
|
|
30
|
-
import type { ReferenceFile, ReferenceHit, ReferencesResult } from "../types";
|
|
31
|
+
import type { UsageState } from "../services/references";
|
|
31
32
|
|
|
32
|
-
/**
|
|
33
|
-
|
|
34
|
-
*
|
|
35
|
-
* One per authored form. An empty list means there is no question to ask (an empty path), which
|
|
36
|
-
* {@link loadMediaUsages} reports as a failure rather than as "nothing uses it".
|
|
37
|
-
*/
|
|
38
|
-
export function mediaUsageQueries(path: string): UsageQuery[] {
|
|
39
|
-
return authoredRefTargets(path).map((target) => ({ path: target }));
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/** Merge one file's hits into the accumulator, summing counts per `(refType, ref)`. */
|
|
43
|
-
function mergeFile(into: Map<string, ReferenceFile>, file: ReferenceFile): void {
|
|
44
|
-
const existing = into.get(file.path);
|
|
45
|
-
if (!existing) {
|
|
46
|
-
into.set(file.path, { count: file.count, path: file.path, refs: [...file.refs] });
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
for (const hit of file.refs) {
|
|
50
|
-
const same = existing.refs.find(
|
|
51
|
-
(candidate: ReferenceHit) => candidate.refType === hit.refType && candidate.ref === hit.ref,
|
|
52
|
-
);
|
|
53
|
-
if (same) {
|
|
54
|
-
same.count += hit.count;
|
|
55
|
-
} else {
|
|
56
|
-
existing.refs.push({ ...hit });
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
existing.count += file.count;
|
|
60
|
-
}
|
|
33
|
+
/** The state a caller gets for a path that names no file. */
|
|
34
|
+
const NO_FILE: UsageState = { message: "no media file to look for", status: "failed" };
|
|
61
35
|
|
|
62
36
|
/**
|
|
63
|
-
*
|
|
37
|
+
* The query for a media file, or null when the path names nothing.
|
|
64
38
|
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
* whichever lane happened to be first.
|
|
39
|
+
* Normalizing here rather than at each call site is the whole of what this wrapper adds over
|
|
40
|
+
* `loadUsages`: the file tree, a drop handler and a library tile do not agree on whether a path
|
|
41
|
+
* carries a leading `./`, and two spellings of one file must not become two cache entries.
|
|
69
42
|
*/
|
|
70
|
-
function
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
for (const result of results) {
|
|
74
|
-
for (const file of result.files) {
|
|
75
|
-
mergeFile(files, file);
|
|
76
|
-
}
|
|
77
|
-
for (const error of result.errors) {
|
|
78
|
-
if (!errors.has(error.path)) {
|
|
79
|
-
errors.set(error.path, error);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
const merged = [...files.values()].toSorted((a, b) => a.path.localeCompare(b.path));
|
|
84
|
-
return {
|
|
85
|
-
errors: [...errors.values()],
|
|
86
|
-
files: merged,
|
|
87
|
-
filesReferencing: merged.length,
|
|
88
|
-
path,
|
|
89
|
-
refsTotal: merged.reduce((sum, file) => sum + file.count, 0),
|
|
90
|
-
tagName: null,
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Reduce the lanes to one state.
|
|
96
|
-
*
|
|
97
|
-
* Precedence is strictly least-informative-wins, because every step down is a step away from a
|
|
98
|
-
* number the caller could act on: unsupported (the host cannot answer at all) beats failed (it
|
|
99
|
-
* tried and could not) beats pending beats a real union.
|
|
100
|
-
*/
|
|
101
|
-
function combine(path: string, states: readonly UsageState[]): UsageState {
|
|
102
|
-
if (states.some((state) => state.status === "unsupported")) {
|
|
103
|
-
return { status: "unsupported" };
|
|
104
|
-
}
|
|
105
|
-
const failure = states.find((state) => state.status === "failed");
|
|
106
|
-
if (failure) {
|
|
107
|
-
return failure;
|
|
108
|
-
}
|
|
109
|
-
if (states.some((state) => state.status === "pending")) {
|
|
110
|
-
return { status: "pending" };
|
|
111
|
-
}
|
|
112
|
-
return {
|
|
113
|
-
result: unionResults(
|
|
114
|
-
path,
|
|
115
|
-
states.map((state) => (state as { result: ReferencesResult }).result),
|
|
116
|
-
),
|
|
117
|
-
status: "ready",
|
|
118
|
-
};
|
|
43
|
+
function mediaQuery(path: string): { path: string } | null {
|
|
44
|
+
const normalized = normalizeProjectPath(path);
|
|
45
|
+
return normalized === "" ? null : { path: normalized };
|
|
119
46
|
}
|
|
120
47
|
|
|
121
48
|
/**
|
|
122
49
|
* The answer right now, WITHOUT starting a request — the synchronous read a lit template needs.
|
|
123
50
|
*
|
|
124
|
-
* `null` means
|
|
125
|
-
*
|
|
51
|
+
* `null` means the question has never been asked, and the caller decides whether this paint is the
|
|
52
|
+
* one that asks (see {@link loadMediaUsages}).
|
|
126
53
|
*/
|
|
127
54
|
export function peekMediaUsages(path: string): UsageState | null {
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
return null;
|
|
131
|
-
}
|
|
132
|
-
const states: UsageState[] = [];
|
|
133
|
-
for (const query of queries) {
|
|
134
|
-
const state = peekUsages(query);
|
|
135
|
-
if (state === null) {
|
|
136
|
-
return null;
|
|
137
|
-
}
|
|
138
|
-
states.push(state);
|
|
139
|
-
}
|
|
140
|
-
return combine(path, states);
|
|
55
|
+
const query = mediaQuery(path);
|
|
56
|
+
return query === null ? null : peekUsages(query);
|
|
141
57
|
}
|
|
142
58
|
|
|
143
59
|
/**
|
|
144
|
-
* Ask
|
|
60
|
+
* Ask, or join the ask already in flight. Never rejects — a failure is a STATE, because the caller
|
|
145
61
|
* has to render something and "unknown" is the honest something.
|
|
146
62
|
*/
|
|
147
63
|
export async function loadMediaUsages(path: string): Promise<UsageState> {
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
return { message: "no media file to look for", status: "failed" };
|
|
151
|
-
}
|
|
152
|
-
return combine(path, await Promise.all(queries.map((query) => loadUsages(query))));
|
|
64
|
+
const query = mediaQuery(path);
|
|
65
|
+
return query === null ? NO_FILE : loadUsages(query);
|
|
153
66
|
}
|
|
154
67
|
|
|
155
|
-
/** Forget
|
|
68
|
+
/** Forget the answer and ask again — the Retry behind a failed media count. */
|
|
156
69
|
export async function retryMediaUsages(path: string): Promise<UsageState> {
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
return { message: "no media file to look for", status: "failed" };
|
|
160
|
-
}
|
|
161
|
-
return combine(path, await Promise.all(queries.map((query) => retryUsages(query))));
|
|
70
|
+
const query = mediaQuery(path);
|
|
71
|
+
return query === null ? NO_FILE : retryUsages(query);
|
|
162
72
|
}
|
|
163
73
|
|
|
164
74
|
/**
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A tab's document, as the bytes a file would hold.
|
|
3
|
+
*
|
|
4
|
+
* Its own module because four callers need it and one of them is `file-ops` itself: the save path
|
|
5
|
+
* writes what this returns, the source view shows it, the collab layer publishes it, and the live
|
|
6
|
+
* preview overlay sends it. Leaving it in `file-ops` meant the overlay had to import the module
|
|
7
|
+
* that imports the overlay, and the collab layer had already worked around the same shape by
|
|
8
|
+
* injecting the function at boot. One home resolves both.
|
|
9
|
+
*
|
|
10
|
+
* That every one of them goes through this function is the point rather than an accident: what a
|
|
11
|
+
* reader sees in a preview, what a peer sees in a session and what lands on disk are the same
|
|
12
|
+
* bytes, so they cannot drift.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { getGridController } from "../grid/grid-controller";
|
|
16
|
+
import {
|
|
17
|
+
defaultContentFormat,
|
|
18
|
+
formatByName,
|
|
19
|
+
formatSerialize,
|
|
20
|
+
loadFormats,
|
|
21
|
+
} from "../format/format-host";
|
|
22
|
+
import type { Tab } from "../tabs/tab";
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Serialize the current document to its output format. Format tabs round-trip through the format
|
|
26
|
+
* class's serialize capability; everything else is native JSON.
|
|
27
|
+
*
|
|
28
|
+
* @param {import("../tabs/tab.js").Tab} tab
|
|
29
|
+
* @returns {Promise<string>}
|
|
30
|
+
*/
|
|
31
|
+
export async function serializeDocument(tab: Tab): Promise<string> {
|
|
32
|
+
// Grid tabs serialize through their source (pending edits included) — e.g. the Monaco source
|
|
33
|
+
// View of a CSV grid tab shows the live file text.
|
|
34
|
+
const grid = getGridController(tab);
|
|
35
|
+
const gridText = grid?.serializeForSource();
|
|
36
|
+
if (gridText) {
|
|
37
|
+
return gridText;
|
|
38
|
+
}
|
|
39
|
+
await loadFormats();
|
|
40
|
+
const sourceFormat = formatByName(tab.doc.sourceFormat);
|
|
41
|
+
if (sourceFormat?.capabilities.serialize) {
|
|
42
|
+
const fm = tab.doc.content?.frontmatter || {};
|
|
43
|
+
const doc = tab.doc.document;
|
|
44
|
+
const fullDoc = { ...fm, ...doc, children: doc.children ?? [] };
|
|
45
|
+
return formatSerialize(sourceFormat.name, fullDoc, { mode: "roundtrip" });
|
|
46
|
+
}
|
|
47
|
+
if (tab.doc.mode === "content") {
|
|
48
|
+
const format = defaultContentFormat();
|
|
49
|
+
if (format) {
|
|
50
|
+
const fm = tab.doc.content?.frontmatter ?? {};
|
|
51
|
+
const hasFrontmatter = Object.keys(fm).length > 0;
|
|
52
|
+
const fullDoc = { ...fm, ...tab.doc.document };
|
|
53
|
+
return formatSerialize(format.name, fullDoc, {
|
|
54
|
+
frontmatter: hasFrontmatter,
|
|
55
|
+
mode: "roundtrip",
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return JSON.stringify(tab.doc.document, null, 2);
|
|
60
|
+
}
|