@brianmsm/quarto-bento 0.0.0-stage → 0.1.1
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/LICENSE +21 -0
- package/README.md +93 -2
- package/bin/qbento-npm.mjs +62 -0
- package/distribution/LICENSE +21 -0
- package/distribution/PACKAGING.md +59 -0
- package/distribution/bin/qbento +5 -0
- package/distribution/bin/qbento.cmd +4 -0
- package/distribution/manifest.json +720 -0
- package/distribution/runtime/LICENSE +21 -0
- package/distribution/runtime/_extensions/qbento/_extension.yml +24 -0
- package/distribution/runtime/_extensions/qbento/capture.lua +21 -0
- package/distribution/runtime/_extensions/qbento/guard.lua +7 -0
- package/distribution/runtime/_extensions/qbento/post.ts +2 -0
- package/distribution/runtime/_extensions/qbento/pre.ts +2 -0
- package/distribution/runtime/_extensions/qbento/producer-activation.js +129 -0
- package/distribution/runtime/src/bridge/browser.js +168 -0
- package/distribution/runtime/src/bridge/isolation.ts +55 -0
- package/distribution/runtime/src/browser/chromium.ts +218 -0
- package/distribution/runtime/src/cli.ts +232 -0
- package/distribution/runtime/src/core/agent.ts +546 -0
- package/distribution/runtime/src/core/annotations.ts +384 -0
- package/distribution/runtime/src/core/capture.ts +203 -0
- package/distribution/runtime/src/core/component-actions.ts +550 -0
- package/distribution/runtime/src/core/component-frames.ts +59 -0
- package/distribution/runtime/src/core/component-sync.ts +290 -0
- package/distribution/runtime/src/core/design-diagnostics.ts +78 -0
- package/distribution/runtime/src/core/design-registry.ts +287 -0
- package/distribution/runtime/src/core/graph-relations.ts +354 -0
- package/distribution/runtime/src/core/graph-scope.ts +63 -0
- package/distribution/runtime/src/core/graph-sync.ts +228 -0
- package/distribution/runtime/src/core/io.ts +27 -0
- package/distribution/runtime/src/core/kpi-appearance.ts +205 -0
- package/distribution/runtime/src/core/layouts.ts +245 -0
- package/distribution/runtime/src/core/project.ts +851 -0
- package/distribution/runtime/src/core/reconcile.ts +384 -0
- package/distribution/runtime/src/core/recovery.ts +1255 -0
- package/distribution/runtime/src/core/slide-actions.ts +158 -0
- package/distribution/runtime/src/core/source-scope.ts +50 -0
- package/distribution/runtime/src/core/sync.ts +2557 -0
- package/distribution/runtime/src/core/widgets.ts +300 -0
- package/distribution/runtime/src/integration/install.ts +98 -0
- package/distribution/runtime/src/integration/invocation.ts +100 -0
- package/distribution/runtime/src/integration/quarto.ts +457 -0
- package/distribution/runtime/src/render/assets.ts +66 -0
- package/distribution/runtime/src/render/capture.lua +8 -0
- package/distribution/runtime/src/render/chart.ts +165 -0
- package/distribution/runtime/src/render/components.ts +199 -0
- package/distribution/runtime/src/render/design.ts +820 -0
- package/distribution/runtime/src/render/diagnostics.ts +36 -0
- package/distribution/runtime/src/render/graphs.ts +747 -0
- package/distribution/runtime/src/render/html.ts +82 -0
- package/distribution/runtime/src/render/images.ts +268 -0
- package/distribution/runtime/src/render/inline.ts +259 -0
- package/distribution/runtime/src/render/kpi.ts +385 -0
- package/distribution/runtime/src/render/lists.ts +183 -0
- package/distribution/runtime/src/render/math.ts +413 -0
- package/distribution/runtime/src/render/measure.ts +185 -0
- package/distribution/runtime/src/render/native-layout.ts +174 -0
- package/distribution/runtime/src/render/producer-capture.ts +319 -0
- package/distribution/runtime/src/render/producer-environment.R +49 -0
- package/distribution/runtime/src/render/producer-environment.ts +96 -0
- package/distribution/runtime/src/render/producer-profile.R +91 -0
- package/distribution/runtime/src/render/producer-site-profile.R +18 -0
- package/distribution/runtime/src/render/project.ts +1806 -0
- package/distribution/runtime/src/render/quarto.ts +127 -0
- package/distribution/runtime/src/render/registry.ts +233 -0
- package/distribution/runtime/src/render/resources.ts +292 -0
- package/distribution/runtime/src/render/shell.ts +110 -0
- package/distribution/runtime/src/render/source-inline.ts +169 -0
- package/distribution/runtime/src/render/table.ts +220 -0
- package/distribution/runtime/src/render/text-overflow.ts +208 -0
- package/distribution/runtime/src/render/types.ts +71 -0
- package/distribution/runtime/src/render/validate.ts +572 -0
- package/distribution/runtime/src/rich/project.ts +117 -0
- package/distribution/runtime/src/rich/table.ts +374 -0
- package/distribution/runtime/src/source/annotations.ts +490 -0
- package/distribution/runtime/src/source/attributes.ts +415 -0
- package/distribution/runtime/src/source/chart.ts +165 -0
- package/distribution/runtime/src/source/code-language.ts +277 -0
- package/distribution/runtime/src/source/component-insertion.ts +160 -0
- package/distribution/runtime/src/source/deletion.ts +278 -0
- package/distribution/runtime/src/source/graphs.ts +517 -0
- package/distribution/runtime/src/source/identities.ts +256 -0
- package/distribution/runtime/src/source/identity-id.ts +6 -0
- package/distribution/runtime/src/source/image.ts +50 -0
- package/distribution/runtime/src/source/kpi.ts +196 -0
- package/distribution/runtime/src/source/mod.ts +876 -0
- package/distribution/runtime/src/source/notes.ts +308 -0
- package/distribution/runtime/src/source/structure.ts +647 -0
- package/distribution/runtime/src/source/table.ts +290 -0
- package/distribution/runtime/src/source/text-conversion.ts +146 -0
- package/distribution/runtime/src/state/components.ts +281 -0
- package/distribution/runtime/src/state/graphs.ts +200 -0
- package/distribution/runtime/src/state/json.ts +162 -0
- package/distribution/runtime/src/state/merge.ts +1034 -0
- package/distribution/runtime/src/state/mod.ts +5 -0
- package/distribution/runtime/src/state/store.ts +226 -0
- package/distribution/runtime/src/state/types.ts +151 -0
- package/distribution/runtime/src/studio/client.js +892 -0
- package/distribution/runtime/src/studio/components.js +188 -0
- package/distribution/runtime/src/studio/index.html +283 -0
- package/distribution/runtime/src/studio/layouts.js +170 -0
- package/distribution/runtime/src/studio/registry.js +283 -0
- package/distribution/runtime/src/studio/relations.js +169 -0
- package/distribution/runtime/src/studio/server.ts +461 -0
- package/distribution/runtime/src/widgets/deck.ts +39 -0
- package/distribution/runtime/src/widgets/plotly.ts +354 -0
- package/distribution/runtime/src/widgets/presenter.ts +367 -0
- package/distribution/runtime/src/widgets/snapshot.ts +116 -0
- package/distribution/runtime/vendor/bento/FONT_NOTICES.md +49 -0
- package/distribution/runtime/vendor/bento/LICENSE +21 -0
- package/distribution/runtime/vendor/bento/THIRD_PARTY_NOTICES.md +51 -0
- package/distribution/runtime/vendor/bento/font-licenses/fraunces-OFL.txt +93 -0
- package/distribution/runtime/vendor/bento/font-licenses/instrument-sans-OFL.txt +93 -0
- package/distribution/runtime/vendor/bento/pin.json +69 -0
- package/distribution/runtime/vendor/bento/provenance/slides.package-lock.json +1928 -0
- package/distribution/runtime/vendor/bento/provenance/slides.package.json +28 -0
- package/distribution/runtime/vendor/bento/runtime-dependencies.json +244 -0
- package/distribution/runtime/vendor/bento/runtime-licenses.json +4038 -0
- package/distribution/runtime/vendor/bento/shell.html +269 -0
- package/distribution/runtime/vendor/bento/slides.schema.json +2379 -0
- package/package.json +40 -4
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { withProject } from "./project.ts";
|
|
2
|
+
import {
|
|
3
|
+
assertOutputUnchanged,
|
|
4
|
+
captureEdits,
|
|
5
|
+
clone,
|
|
6
|
+
compose,
|
|
7
|
+
equal,
|
|
8
|
+
StateError,
|
|
9
|
+
} from "../state/mod.ts";
|
|
10
|
+
import { scanSource } from "../source/mod.ts";
|
|
11
|
+
import { previewStructure } from "../source/structure.ts";
|
|
12
|
+
import { assertProjectDocument } from "../render/validate.ts";
|
|
13
|
+
|
|
14
|
+
export interface SlideActionRequest {
|
|
15
|
+
action: "hide" | "show" | "delete" | "restore";
|
|
16
|
+
slideId: string;
|
|
17
|
+
revision: number;
|
|
18
|
+
expectedDocId?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Save reversible slide actions; source deletion belongs only to reviewed sync. */
|
|
22
|
+
export async function applySlideAction(
|
|
23
|
+
source: string,
|
|
24
|
+
request: SlideActionRequest,
|
|
25
|
+
) {
|
|
26
|
+
if (
|
|
27
|
+
!request ||
|
|
28
|
+
!["hide", "show", "delete", "restore"].includes(request.action) ||
|
|
29
|
+
typeof request.slideId !== "string" || !request.slideId ||
|
|
30
|
+
!Number.isSafeInteger(request.revision) ||
|
|
31
|
+
(request.expectedDocId !== undefined &&
|
|
32
|
+
(typeof request.expectedDocId !== "string" || !request.expectedDocId))
|
|
33
|
+
) throw new StateError("INVALID_SLIDE_ACTION", "Invalid slide action.");
|
|
34
|
+
return await withProject(source, async (paths, store) => {
|
|
35
|
+
const state = await store.load();
|
|
36
|
+
if (!state) {
|
|
37
|
+
throw new StateError("NO_BASELINE", "Render the project first.");
|
|
38
|
+
}
|
|
39
|
+
if (
|
|
40
|
+
request.expectedDocId !== undefined &&
|
|
41
|
+
state.docId !== request.expectedDocId
|
|
42
|
+
) {
|
|
43
|
+
throw new StateError(
|
|
44
|
+
"UNRELATED_DOCUMENT",
|
|
45
|
+
"The slide action belongs to a different docId. Reload the project.",
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
if (state.revision !== request.revision) {
|
|
49
|
+
throw new StateError("STALE_REVISION", "Reload before changing slides.");
|
|
50
|
+
}
|
|
51
|
+
await assertOutputUnchanged(state, paths.output);
|
|
52
|
+
const doc = clone(state.emitted);
|
|
53
|
+
const slide = doc.slides.find((item) => item.id === request.slideId);
|
|
54
|
+
if (request.action === "restore") {
|
|
55
|
+
const deletion = state.pending.find((change) =>
|
|
56
|
+
change.kind === "delete-slide" &&
|
|
57
|
+
change.target.slideId === request.slideId
|
|
58
|
+
);
|
|
59
|
+
if (slide || !deletion) {
|
|
60
|
+
throw new StateError(
|
|
61
|
+
"SLIDE_NOT_DELETED",
|
|
62
|
+
"No pending deletion to restore.",
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
if (
|
|
66
|
+
!scanSource(await Deno.readTextFile(paths.source)).slides.some((item) =>
|
|
67
|
+
item.id === request.slideId
|
|
68
|
+
)
|
|
69
|
+
) {
|
|
70
|
+
throw new StateError(
|
|
71
|
+
"SLIDE_SOURCE_REMOVED",
|
|
72
|
+
"This slide is already absent from QMD. Recalculate or recover its source from history before restoring it.",
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
// Compose the retained design and semantic journal; restoring the raw
|
|
76
|
+
// baseline alone would lose edits made before the slide was removed.
|
|
77
|
+
const recoverable = clone(state);
|
|
78
|
+
recoverable.pending = recoverable.pending.filter((change) =>
|
|
79
|
+
change.id !== deletion.id
|
|
80
|
+
);
|
|
81
|
+
const restored = compose(
|
|
82
|
+
recoverable,
|
|
83
|
+
state.generatedBaseline,
|
|
84
|
+
undefined,
|
|
85
|
+
undefined,
|
|
86
|
+
{ strict: false },
|
|
87
|
+
).document.slides.find((item) => item.id === request.slideId);
|
|
88
|
+
if (!restored) {
|
|
89
|
+
throw new StateError(
|
|
90
|
+
"SLIDE_NOT_RECOVERABLE",
|
|
91
|
+
"Retained slide is unavailable.",
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
const originalOrder = deletion.restoreOrder ??
|
|
95
|
+
state.generatedBaseline.slides.map((item) => item.id);
|
|
96
|
+
const position = originalOrder.indexOf(restored.id);
|
|
97
|
+
// Prefer the closest surviving successor, then predecessor. Retain the
|
|
98
|
+
// current relative order of survivors if they were reordered meanwhile.
|
|
99
|
+
const following = originalOrder.slice(position + 1).find((id) =>
|
|
100
|
+
doc.slides.some((item) => item.id === id)
|
|
101
|
+
);
|
|
102
|
+
const preceding = originalOrder.slice(0, position).reverse().find((id) =>
|
|
103
|
+
doc.slides.some((item) => item.id === id)
|
|
104
|
+
);
|
|
105
|
+
const index = following
|
|
106
|
+
? doc.slides.findIndex((item) => item.id === following)
|
|
107
|
+
: preceding
|
|
108
|
+
? doc.slides.findIndex((item) => item.id === preceding) + 1
|
|
109
|
+
: doc.slides.length;
|
|
110
|
+
doc.slides.splice(index, 0, restored);
|
|
111
|
+
} else {
|
|
112
|
+
if (!slide) {
|
|
113
|
+
throw new StateError("SLIDE_NOT_FOUND", "Slide is unavailable.");
|
|
114
|
+
}
|
|
115
|
+
if (request.action === "delete") {
|
|
116
|
+
if (doc.slides.length === 1) {
|
|
117
|
+
throw new StateError(
|
|
118
|
+
"LAST_SLIDE_REQUIRED",
|
|
119
|
+
"Keep one slide in the editable deck; hide this slide instead.",
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
if (
|
|
123
|
+
!state.generatedBaseline.slides.some((item) => item.id === slide.id)
|
|
124
|
+
) {
|
|
125
|
+
throw new StateError(
|
|
126
|
+
"SLIDE_NOT_SOURCED",
|
|
127
|
+
"Sync this new slide before marking a QMD deletion; you can hide it now.",
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
if (
|
|
131
|
+
doc.slides.some((item) =>
|
|
132
|
+
item.id !== slide.id && (item.stateOf === slide.id ||
|
|
133
|
+
item.elements.some((element) => element.link === slide.id))
|
|
134
|
+
)
|
|
135
|
+
) {
|
|
136
|
+
throw new StateError(
|
|
137
|
+
"SLIDE_IN_USE",
|
|
138
|
+
"Another slide links to this slide. Resolve the links or hide it.",
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
// Check safety without writing source or granting sync approval.
|
|
142
|
+
// The subsequent source preview/apply repeats these checks independently.
|
|
143
|
+
previewStructure(scanSource(await Deno.readTextFile(paths.source)), [{
|
|
144
|
+
type: "delete-slide",
|
|
145
|
+
id: slide.id,
|
|
146
|
+
confirmed: true,
|
|
147
|
+
}]);
|
|
148
|
+
doc.slides = doc.slides.filter((item) => item.id !== slide.id);
|
|
149
|
+
} else {
|
|
150
|
+
if (request.action === "hide") slide.hidden = true;
|
|
151
|
+
else delete slide.hidden;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
assertProjectDocument(doc);
|
|
155
|
+
if (equal(doc, state.emitted)) return state;
|
|
156
|
+
return await store.save(captureEdits(state, doc));
|
|
157
|
+
});
|
|
158
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/** Verify a component's native scope against its original authored heading receipt. */
|
|
2
|
+
import type { SourceDocument } from "../source/mod.ts";
|
|
3
|
+
import { INLINE_CODEC_VERSION } from "../render/inline.ts";
|
|
4
|
+
import { type ProjectState, StateError } from "../state/types.ts";
|
|
5
|
+
|
|
6
|
+
export function verifiedSourceSlide(
|
|
7
|
+
source: SourceDocument,
|
|
8
|
+
state: ProjectState,
|
|
9
|
+
sourceSlideId: string,
|
|
10
|
+
expectedNativeSlideId: string,
|
|
11
|
+
): string {
|
|
12
|
+
function fail(): never {
|
|
13
|
+
throw new StateError(
|
|
14
|
+
"UNMAPPED_SOURCE_SLIDE",
|
|
15
|
+
"The component needs its unique exact authored heading receipt in the expected native slide. Recalculate and review the source before reusing its appearance.",
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
const slides = source.slides.filter((slide) => slide.id === sourceSlideId);
|
|
19
|
+
if (slides.length !== 1 || !expectedNativeSlideId) fail();
|
|
20
|
+
const slide = slides[0];
|
|
21
|
+
if (slide.stable) {
|
|
22
|
+
if (slide.id !== expectedNativeSlideId) fail();
|
|
23
|
+
return slide.id;
|
|
24
|
+
}
|
|
25
|
+
const heading = source.blocks.find((block) => block.id === slide.headingId);
|
|
26
|
+
if (
|
|
27
|
+
!heading || heading.kind !== "heading" || heading.parentId ||
|
|
28
|
+
heading.origin !== "authored"
|
|
29
|
+
) fail();
|
|
30
|
+
const titles = Object.entries(state.provenance).filter(([, provenance]) =>
|
|
31
|
+
provenance.source?.blockId === heading.id &&
|
|
32
|
+
provenance.source.kind === "heading"
|
|
33
|
+
);
|
|
34
|
+
if (titles.length !== 1) fail();
|
|
35
|
+
const [key, provenance] = titles[0],
|
|
36
|
+
[nativeSlideId, elementId] = JSON.parse(key) as [string, string];
|
|
37
|
+
const baseline = state.generatedBaseline.slides.find((slide) =>
|
|
38
|
+
slide.id === expectedNativeSlideId
|
|
39
|
+
)?.elements.find((element) => element.id === elementId);
|
|
40
|
+
if (
|
|
41
|
+
nativeSlideId !== expectedNativeSlideId || !elementId ||
|
|
42
|
+
provenance.origin !== "authored" || provenance.semanticId !== heading.id ||
|
|
43
|
+
provenance.source?.hash !== source.hash ||
|
|
44
|
+
provenance.source.content !== heading.content ||
|
|
45
|
+
provenance.source.stable !== false ||
|
|
46
|
+
provenance.source.inlineCodec !== INLINE_CODEC_VERSION ||
|
|
47
|
+
baseline?.type !== "text" || typeof baseline.html !== "string"
|
|
48
|
+
) fail();
|
|
49
|
+
return expectedNativeSlideId;
|
|
50
|
+
}
|