@markii/host 0.0.0-stage → 0.17.0-next.2
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 +19 -2
- package/dist/browser-worker.js +639 -0
- package/dist/browser.d.ts +57 -0
- package/dist/browser.js +167 -0
- package/dist/conformance/host/01-plain-open/expected.json +5 -0
- package/dist/conformance/host/01-plain-open/note.mk.md +8 -0
- package/dist/conformance/host/01-plain-open/scenario.json +6 -0
- package/dist/conformance/host/02-run-manual-grant-accepted/expected.json +16 -0
- package/dist/conformance/host/02-run-manual-grant-accepted/note.mk.md +5 -0
- package/dist/conformance/host/02-run-manual-grant-accepted/scenario.json +14 -0
- package/dist/conformance/host/03-run-manual-grant-refused/expected.json +18 -0
- package/dist/conformance/host/03-run-manual-grant-refused/note.mk.md +5 -0
- package/dist/conformance/host/03-run-manual-grant-refused/scenario.json +14 -0
- package/dist/conformance/host/04-run-auto-write-denied/expected.json +16 -0
- package/dist/conformance/host/04-run-auto-write-denied/note.mk.md +10 -0
- package/dist/conformance/host/04-run-auto-write-denied/scenario.json +13 -0
- package/dist/conformance/host/05-export-html-pack-directive/expected.json +14 -0
- package/dist/conformance/host/05-export-html-pack-directive/note.mk.md +8 -0
- package/dist/conformance/host/05-export-html-pack-directive/scenario.json +6 -0
- package/dist/conformance/host/06-install-pack-then-render/expected.json +18 -0
- package/dist/conformance/host/06-install-pack-then-render/files/ana-1.0.0/pack.json +13 -0
- package/dist/conformance/host/06-install-pack-then-render/files/ana-1.0.0/webview.js +1 -0
- package/dist/conformance/host/06-install-pack-then-render/note.mk.md +5 -0
- package/dist/conformance/host/06-install-pack-then-render/scenario.json +15 -0
- package/dist/conformance/host/07-complete-in-directive/expected.json +14 -0
- package/dist/conformance/host/07-complete-in-directive/note.mk.md +3 -0
- package/dist/conformance/host/07-complete-in-directive/scenario.json +6 -0
- package/dist/conformance/host/08-hover-standard-component/expected.json +5 -0
- package/dist/conformance/host/08-hover-standard-component/note.mk.md +5 -0
- package/dist/conformance/host/08-hover-standard-component/scenario.json +6 -0
- package/dist/conformance/host/09-insert-skeleton/expected.json +5 -0
- package/dist/conformance/host/09-insert-skeleton/note.mk.md +4 -0
- package/dist/conformance/host/09-insert-skeleton/scenario.json +6 -0
- package/dist/conformance/host/10-scripts-disabled/expected.json +11 -0
- package/dist/conformance/host/10-scripts-disabled/note.mk.md +5 -0
- package/dist/conformance/host/10-scripts-disabled/scenario.json +9 -0
- package/dist/conformance/host/11-complete-closing-fence/expected.json +5 -0
- package/dist/conformance/host/11-complete-closing-fence/note.mk.md +5 -0
- package/dist/conformance/host/11-complete-closing-fence/scenario.json +12 -0
- package/dist/conformance/host/12-complete-nested-container/expected.json +5 -0
- package/dist/conformance/host/12-complete-nested-container/note.mk.md +5 -0
- package/dist/conformance/host/12-complete-nested-container/scenario.json +12 -0
- package/dist/conformance/host/13-install-pack-declined/expected.json +12 -0
- package/dist/conformance/host/13-install-pack-declined/files/ana-1.0.0/pack.json +13 -0
- package/dist/conformance/host/13-install-pack-declined/files/ana-1.0.0/webview.js +1 -0
- package/dist/conformance/host/13-install-pack-declined/note.mk.md +5 -0
- package/dist/conformance/host/13-install-pack-declined/scenario.json +10 -0
- package/dist/conformance/host/14-remove-pack/expected.json +14 -0
- package/dist/conformance/host/14-remove-pack/files/ana-1.0.0/pack.json +13 -0
- package/dist/conformance/host/14-remove-pack/files/ana-1.0.0/webview.js +1 -0
- package/dist/conformance/host/14-remove-pack/note.mk.md +5 -0
- package/dist/conformance/host/14-remove-pack/scenario.json +11 -0
- package/dist/conformance/host/15-load-packs-engine-views/expected.json +19 -0
- package/dist/conformance/host/15-load-packs-engine-views/files/duo-1.0.0/pack.json +9 -0
- package/dist/conformance/host/15-load-packs-engine-views/files/duo-1.0.0/webview.js +1 -0
- package/dist/conformance/host/15-load-packs-engine-views/note.mk.md +9 -0
- package/dist/conformance/host/15-load-packs-engine-views/scenario.json +10 -0
- package/dist/conformance/host/16-pack-trust/expected.json +37 -0
- package/dist/conformance/host/16-pack-trust/files/ana-1.0.0/pack.json +12 -0
- package/dist/conformance/host/16-pack-trust/files/ana-1.0.0/webview.js +1 -0
- package/dist/conformance/host/16-pack-trust/files/sneaky-1.0.0/pack.json +12 -0
- package/dist/conformance/host/16-pack-trust/files/sneaky-1.0.0/webview.js +1 -0
- package/dist/conformance/host/16-pack-trust/note.mk.md +5 -0
- package/dist/conformance/host/16-pack-trust/scenario.json +26 -0
- package/dist/conformance/host/README.md +221 -0
- package/dist/conformance.d.ts +2 -0
- package/dist/conformance.js +5 -0
- package/dist/diagnostics/render-diagnostics.d.ts +41 -0
- package/dist/diagnostics/render-diagnostics.js +46 -0
- package/dist/export/cascade.d.ts +98 -0
- package/dist/export/cascade.js +159 -0
- package/dist/export/export-zip.d.ts +19 -0
- package/dist/export/export-zip.js +38 -0
- package/dist/export/image-embed.d.ts +126 -0
- package/dist/export/image-embed.js +274 -0
- package/dist/export/note-export.d.ts +332 -0
- package/dist/export/note-export.js +366 -0
- package/dist/export/note-links.d.ts +83 -0
- package/dist/export/note-links.js +241 -0
- package/dist/glue.wasm +0 -0
- package/dist/host/adapter.d.ts +223 -0
- package/dist/host/adapter.js +9 -0
- package/dist/host/cascade-export-behavior.d.ts +120 -0
- package/dist/host/cascade-export-behavior.js +151 -0
- package/dist/host/conformance-runner.d.ts +156 -0
- package/dist/host/conformance-runner.js +634 -0
- package/dist/host/create-host.d.ts +253 -0
- package/dist/host/create-host.js +329 -0
- package/dist/host/editor-behavior.d.ts +157 -0
- package/dist/host/editor-behavior.js +227 -0
- package/dist/host/export/md-plain.d.ts +15 -0
- package/dist/host/export/md-plain.js +125 -0
- package/dist/host/export-behavior.d.ts +113 -0
- package/dist/host/export-behavior.js +257 -0
- package/dist/host/labels.d.ts +24 -0
- package/dist/host/labels.js +32 -0
- package/dist/host/pack-archive.d.ts +30 -0
- package/dist/host/pack-archive.js +68 -0
- package/dist/host/pack-install.d.ts +88 -0
- package/dist/host/pack-install.js +189 -0
- package/dist/host/pack-load.d.ts +72 -0
- package/dist/host/pack-load.js +57 -0
- package/dist/host/pack-remove.d.ts +21 -0
- package/dist/host/pack-remove.js +65 -0
- package/dist/host/pack-trust-behavior.d.ts +79 -0
- package/dist/host/pack-trust-behavior.js +262 -0
- package/dist/host/refresh-interval.d.ts +39 -0
- package/dist/host/refresh-interval.js +61 -0
- package/dist/host/run-behavior.d.ts +89 -0
- package/dist/host/run-behavior.js +168 -0
- package/dist/host/run-diagnostics.d.ts +26 -0
- package/dist/host/run-diagnostics.js +43 -0
- package/dist/host/script-execution.d.ts +72 -0
- package/dist/host/script-execution.js +68 -0
- package/dist/host/terminal-packs.d.ts +39 -0
- package/dist/host/terminal-packs.js +51 -0
- package/dist/html-engine.d.ts +2 -0
- package/dist/html-engine.js +29 -0
- package/dist/index.d.ts +95 -0
- package/dist/index.js +70 -0
- package/dist/insert/component-catalog.d.ts +23 -0
- package/dist/insert/component-catalog.js +96 -0
- package/dist/node-fs.d.ts +14 -0
- package/dist/node-fs.js +70 -0
- package/dist/node.d.ts +5 -0
- package/dist/node.js +13 -0
- package/dist/package.json +3 -0
- package/dist/packs/discover.d.ts +119 -0
- package/dist/packs/discover.js +266 -0
- package/dist/packs/pack-diagnostics.d.ts +81 -0
- package/dist/packs/pack-diagnostics.js +105 -0
- package/dist/packs/pack-paths.d.ts +53 -0
- package/dist/packs/pack-paths.js +106 -0
- package/dist/packs/pack-render-registry.d.ts +83 -0
- package/dist/packs/pack-render-registry.js +182 -0
- package/dist/packs/pack-scripts.d.ts +20 -0
- package/dist/packs/pack-scripts.js +96 -0
- package/dist/packs/pack-trust.d.ts +57 -0
- package/dist/packs/pack-trust.js +111 -0
- package/dist/packs/prebuilt.d.ts +49 -0
- package/dist/packs/prebuilt.js +97 -0
- package/dist/path-util.d.ts +24 -0
- package/dist/path-util.js +82 -0
- package/dist/run/browser-isolate.d.ts +45 -0
- package/dist/run/browser-isolate.js +81 -0
- package/dist/run/bundle-document.d.ts +29 -0
- package/dist/run/bundle-document.js +40 -0
- package/dist/run/bundle-run.d.ts +188 -0
- package/dist/run/bundle-run.js +232 -0
- package/dist/run/grant-flow.d.ts +232 -0
- package/dist/run/grant-flow.js +390 -0
- package/dist/run/ip-address.d.ts +41 -0
- package/dist/run/ip-address.js +255 -0
- package/dist/run/isolate.d.ts +57 -0
- package/dist/run/isolate.js +22 -0
- package/dist/run/lua-resolver.d.ts +21 -0
- package/dist/run/lua-resolver.js +63 -0
- package/dist/run/net-bridge-worker.d.ts +26 -0
- package/dist/run/net-bridge-worker.js +66 -0
- package/dist/run/net-bridge.d.ts +56 -0
- package/dist/run/net-bridge.js +59 -0
- package/dist/run/net-pinning.d.ts +82 -0
- package/dist/run/net-pinning.js +137 -0
- package/dist/run/net-provider.d.ts +70 -0
- package/dist/run/net-provider.js +289 -0
- package/dist/run/node-isolate.d.ts +30 -0
- package/dist/run/node-isolate.js +117 -0
- package/dist/run/run-flow.d.ts +187 -0
- package/dist/run/run-flow.js +323 -0
- package/dist/run/run-host.d.ts +107 -0
- package/dist/run/run-host.js +241 -0
- package/dist/run/run-job.d.ts +130 -0
- package/dist/run/run-job.js +279 -0
- package/dist/run/run-progress.d.ts +55 -0
- package/dist/run/run-progress.js +42 -0
- package/dist/run/run-trace.d.ts +44 -0
- package/dist/run/run-trace.js +36 -0
- package/dist/run/script-requirements.d.ts +98 -0
- package/dist/run/script-requirements.js +212 -0
- package/dist/run/snapshot-storage.d.ts +25 -0
- package/dist/run/snapshot-storage.js +31 -0
- package/dist/run/stale-values.d.ts +25 -0
- package/dist/run/stale-values.js +32 -0
- package/dist/run/worker-entry-browser.d.ts +42 -0
- package/dist/run/worker-entry-browser.js +77 -0
- package/dist/run/worker-entry.d.ts +1 -0
- package/dist/run/worker-entry.js +96 -0
- package/dist/values-failure.d.ts +17 -0
- package/dist/values-failure.js +1 -0
- package/dist/values-merge.d.ts +39 -0
- package/dist/values-merge.js +33 -0
- package/package.json +84 -4
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import type { CompletionContext, CompletionItem, HoverInfo, InsertableComponent, LineColumn } from '@markii/stdlib/editor';
|
|
2
|
+
import type { HostEditor, HostTextEdit } from './adapter.js';
|
|
3
|
+
/** The origin tag every host's completion row shows for a component item: `'standard'`, `'layout'`, or the owning pack's own namespace. Empty string for an attribute or value item, which has no `group`. */
|
|
4
|
+
export declare const STANDARD_ORIGIN_TAG = "standard";
|
|
5
|
+
export declare const LAYOUT_ORIGIN_TAG = "layout";
|
|
6
|
+
/**
|
|
7
|
+
* Survey finding A5: `apps/vscode/src/completion.ts:completionOriginTag`
|
|
8
|
+
* and `apps/obsidian/src/complete-component.ts:completionOriginTag` had
|
|
9
|
+
* IDENTICAL function bodies, differing only in which locally-defined
|
|
10
|
+
* constant stood in for the standard/layout tag string. Both apps'
|
|
11
|
+
* constants held the same lower-case values (`'standard'`/`'layout'`),
|
|
12
|
+
* so there was never a wording difference to preserve.
|
|
13
|
+
*/
|
|
14
|
+
export declare function completionOriginTag(item: CompletionItem): string;
|
|
15
|
+
/** A small cache in front of `buildComponentCatalog` (`../insert/component-catalog.js`), so a completion/hover provider does not re-discover packs from disk on every keystroke. */
|
|
16
|
+
export interface CatalogCache {
|
|
17
|
+
/** The cached catalog, building it via `load` on first use and again after `invalidate()`. Never throws: a rejected `load` degrades to the standard-set-only catalog. */
|
|
18
|
+
get(): Promise<readonly InsertableComponent[]>;
|
|
19
|
+
/** The cached catalog if one is built, else `undefined`. Never starts a build. */
|
|
20
|
+
peek(): readonly InsertableComponent[] | undefined;
|
|
21
|
+
/** Drops the cached catalog (and any in-flight build), so the next `get()` rebuilds from `load`. */
|
|
22
|
+
invalidate(): void;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Builds a `CatalogCache` around `buildCatalog` (typically
|
|
26
|
+
* `../insert/component-catalog.js`'s `buildComponentCatalog` composed with
|
|
27
|
+
* a pack-discovery call). Concurrent `get()` calls made before a build
|
|
28
|
+
* finishes share one in-flight promise rather than each starting their own
|
|
29
|
+
* pack discovery.
|
|
30
|
+
*/
|
|
31
|
+
export declare function createCatalogCache(buildCatalog: () => Promise<readonly InsertableComponent[]>): CatalogCache;
|
|
32
|
+
/**
|
|
33
|
+
* `completionAt` composed over a `HostEditor`'s document text, at the
|
|
34
|
+
* POSITION a completion request names — not `editor.cursor()`. A real
|
|
35
|
+
* editor's completion provider is invoked with an explicit position (the
|
|
36
|
+
* position the user is typing at when the provider fires), which is why a
|
|
37
|
+
* completion/hover request always carries its own `line`/`column` rather
|
|
38
|
+
* than reading the live caret a second time. `editor.cursor()` stays
|
|
39
|
+
* reserved for `insertComponentPlan`, which genuinely inserts at the
|
|
40
|
+
* caret.
|
|
41
|
+
*/
|
|
42
|
+
export declare function completeAtViaEditor(editor: Pick<HostEditor, 'documentText'>, position: LineColumn, catalog: readonly InsertableComponent[]): CompletionContext;
|
|
43
|
+
/**
|
|
44
|
+
* Everything a completion UI needs for one request, decided once in the
|
|
45
|
+
* shared layer: the items, the replace span (`replaceStart`/`replaceEnd`),
|
|
46
|
+
* the token start a filtering popup should score against (`tokenStart`,
|
|
47
|
+
* which equals `replaceStart` unless a colon run precedes the name), and
|
|
48
|
+
* the fence lengthening edits an accepted CONTAINER item needs. Hosts only
|
|
49
|
+
* map this onto their own popup type.
|
|
50
|
+
*/
|
|
51
|
+
export interface CompletionPlan {
|
|
52
|
+
readonly kind: CompletionContext['kind'];
|
|
53
|
+
readonly items: readonly CompletionItem[];
|
|
54
|
+
readonly replaceStart: number;
|
|
55
|
+
readonly replaceEnd: number;
|
|
56
|
+
readonly tokenStart: number;
|
|
57
|
+
/**
|
|
58
|
+
* Fence rewrites to apply in the SAME undo step as an accepted container
|
|
59
|
+
* item, in pre-edit coordinates, never on the insertion line. Empty when
|
|
60
|
+
* no item in the response opens a container or the document's fences do
|
|
61
|
+
* not pair cleanly. Every container item in one context inserts the same
|
|
62
|
+
* fence shape, so one list serves them all; a host applies it only when
|
|
63
|
+
* the accepted item is a container (`isContainerCompletionItem`).
|
|
64
|
+
*/
|
|
65
|
+
readonly fenceEdits: readonly HostTextEdit[];
|
|
66
|
+
}
|
|
67
|
+
/** Whether accepting `item` opens a container, and so needs `CompletionPlan.fenceEdits` applied with it. */
|
|
68
|
+
export declare function isContainerCompletionItem(item: {
|
|
69
|
+
readonly insertText: string;
|
|
70
|
+
}): boolean;
|
|
71
|
+
/**
|
|
72
|
+
* `completionAt` over a whole document at a position, plus the two
|
|
73
|
+
* decisions both editors used to repeat at their own trigger boundary: a
|
|
74
|
+
* bare colon run that CLOSES an open container is not an opener waiting
|
|
75
|
+
* for a name (so no plan), and fence edits are computed once per response.
|
|
76
|
+
* Returns `undefined` when there is nothing to offer. Never throws.
|
|
77
|
+
*/
|
|
78
|
+
export declare function completionPlanAt(documentText: string, position: LineColumn, catalog: readonly InsertableComponent[]): CompletionPlan | undefined;
|
|
79
|
+
/** `hoverAt` composed over a `HostEditor`'s document text at a given position, the same way `completeAtViaEditor` composes `completionAt`. */
|
|
80
|
+
export declare function hoverAtViaEditor(editor: Pick<HostEditor, 'documentText'>, position: LineColumn, catalog: readonly InsertableComponent[]): HoverInfo | undefined;
|
|
81
|
+
/**
|
|
82
|
+
* Plain-text hover documentation for `item`: `@markii/stdlib/editor`'s
|
|
83
|
+
* `formatComponentDocumentation`, the shape every host without a
|
|
84
|
+
* rich-markdown hover surface (or a host doing its own markdown mapping)
|
|
85
|
+
* can use as-is. This is also what `createMarkiiHost`'s `hoverAt` returns
|
|
86
|
+
* (via `hoverAtViaEditor`'s `HoverInfo.documentation`) rather than the
|
|
87
|
+
* bare summary sentence: the summary alone can read as generic prose with
|
|
88
|
+
* no component name in it (a callout's summary is "A colored box for an
|
|
89
|
+
* aside, warning, or danger note.", nowhere mentioning "callout"), while
|
|
90
|
+
* the formatted text's `Example: :::callout{}` line always names the
|
|
91
|
+
* directive.
|
|
92
|
+
*/
|
|
93
|
+
export declare function hoverDocumentationText(item: InsertableComponent): string;
|
|
94
|
+
/** One text edit and the cursor position an insertion leaves behind, both relative to the document `insertionLine`/`insertionColumn` named. */
|
|
95
|
+
export interface InsertComponentEdit {
|
|
96
|
+
readonly line: number;
|
|
97
|
+
readonly startColumn: number;
|
|
98
|
+
readonly endColumn: number;
|
|
99
|
+
readonly text: string;
|
|
100
|
+
}
|
|
101
|
+
export interface InsertComponentPlan {
|
|
102
|
+
/**
|
|
103
|
+
* Every edit `HostEditor.applyEdits` must apply together as one
|
|
104
|
+
* undoable edit: the skeleton insertion plus any fence lines an
|
|
105
|
+
* enclosing container needs lengthened.
|
|
106
|
+
*
|
|
107
|
+
* All edits are expressed in the ORIGINAL document's coordinates (the
|
|
108
|
+
* text `documentText` named, before any of these edits land) — exactly
|
|
109
|
+
* like `fenceExtensionEdits` already promises, and exactly what both
|
|
110
|
+
* apps' `HostTextEdit`-consuming adapters already assume of a
|
|
111
|
+
* multi-edit `applyEdits` call. That is what makes the array safe to
|
|
112
|
+
* apply as one atomic transaction: a host whose `applyEdits` resolves
|
|
113
|
+
* every position against the pre-edit document (VS Code's
|
|
114
|
+
* `WorkspaceEdit`, Obsidian's `Editor.transaction`) can apply them in
|
|
115
|
+
* any order.
|
|
116
|
+
*
|
|
117
|
+
* The array is ALSO returned bottom-to-top (descending by line, ties
|
|
118
|
+
* broken by descending column) as a second-line defense for a simpler
|
|
119
|
+
* host: one that applies this list as a sequence of plain string
|
|
120
|
+
* splices against a single mutable buffer rather than a coordinate-free
|
|
121
|
+
* transaction. Splicing text at a line shifts every line and column
|
|
122
|
+
* number below it; splicing from the bottom of the document upward
|
|
123
|
+
* means each edit is always applied before anything that could move
|
|
124
|
+
* out from under it, and nothing below an edit's line is ever touched
|
|
125
|
+
* after that edit lands. Fence edits never land on the insertion line
|
|
126
|
+
* itself (`fenceExtensionEdits`'s own guarantee, an enclosing pair by
|
|
127
|
+
* definition straddles it), so the insertion edit's own line never
|
|
128
|
+
* collides with a fence edit's line and the ordering rule never has to
|
|
129
|
+
* break a tie between them.
|
|
130
|
+
*/
|
|
131
|
+
readonly edits: readonly InsertComponentEdit[];
|
|
132
|
+
readonly cursor: LineColumn;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Builds the skeleton for `component` (`@markii/stdlib/editor`'s
|
|
136
|
+
* `componentSkeleton`), turns its flat `cursorOffset` into an absolute
|
|
137
|
+
* line/column given where the insertion happens, and folds in the fence
|
|
138
|
+
* edits (`@markii/stdlib/editor`'s `fenceExtensionEdits`) an enclosing
|
|
139
|
+
* container needs lengthened so it still nests legally. This is the pure
|
|
140
|
+
* half of both apps' `insert-component.ts` command handlers: build the
|
|
141
|
+
* skeleton, insert it at the cursor, lengthen any enclosing fences, place
|
|
142
|
+
* the cursor inside it — all in one plan a host applies as one undoable
|
|
143
|
+
* edit (`HostEditor.applyEdits`'s contract).
|
|
144
|
+
*
|
|
145
|
+
* `documentText` is the document as it stands before this insertion (the
|
|
146
|
+
* same text `fenceExtensionEdits` scans); a caller inserting from a
|
|
147
|
+
* completion accept passes the same text it already read to decide what
|
|
148
|
+
* to insert.
|
|
149
|
+
*
|
|
150
|
+
* Never throws: a failure to compute fence edits (hostile input, an
|
|
151
|
+
* unpaired document `fenceExtensionEdits` refuses to touch) degrades to
|
|
152
|
+
* the skeleton insertion alone, exactly as inserting always worked before
|
|
153
|
+
* fence auto-extension existed.
|
|
154
|
+
*/
|
|
155
|
+
export declare function insertComponentPlan(component: Pick<InsertableComponent, 'directiveName' | 'kind'> & {
|
|
156
|
+
readonly requiredAttributes?: readonly string[];
|
|
157
|
+
}, documentText: string, insertionLine: number, insertionColumn: number): InsertComponentPlan;
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `completeAt`/`hoverAt`/`insertComponent` behaviors' shared half,
|
|
3
|
+
* merged from `apps/vscode/src/completion.ts` + `completion-catalog.ts`,
|
|
4
|
+
* `apps/obsidian/src/complete-component.ts`, and both apps'
|
|
5
|
+
* `insert-component.ts` (survey finding A5, plus table rows 31/33).
|
|
6
|
+
*
|
|
7
|
+
* NODE-FREE by design (`tmp/W11-adapter-design.md`, section 1): re-exported
|
|
8
|
+
* from `../browser.ts` as well as `../index.ts`, so VS Code's webview
|
|
9
|
+
* bundle can read the origin-tag vocabulary and the pure completion math
|
|
10
|
+
* directly if a future slice needs to. `CatalogCache.get`'s injected
|
|
11
|
+
* `load` is the one thing that reaches disk, and it is always supplied by
|
|
12
|
+
* the caller (a main-entry-only pack-discovery call), never imported here.
|
|
13
|
+
*
|
|
14
|
+
* What stays app-side (deliberately, per the survey's section D): VS
|
|
15
|
+
* Code's sectioned quick-pick presentation (`Standard`/`Layout`/`<pack>
|
|
16
|
+
* pack` separators) and Obsidian's flat fuzzy list are a genuine product
|
|
17
|
+
* difference, not duplication — this module returns the catalog and the
|
|
18
|
+
* origin tag; it never picks a picker shape. Snippet escaping
|
|
19
|
+
* (`snippetText`) is VS Code's own snippet-syntax concern and stays there
|
|
20
|
+
* too. Hover exists only on VS Code today; a host with no `adapter.editor`
|
|
21
|
+
* simply never calls `hoverAtViaAdapter`.
|
|
22
|
+
*/
|
|
23
|
+
import { closesOpenContainerFence, completionAt, componentDocumentation, fenceExtensionEdits, insertedContainerColonCount, formatComponentDocumentation, hoverAt, offsetToLineColumn, componentSkeleton, } from '@markii/stdlib/editor';
|
|
24
|
+
/** The origin tag every host's completion row shows for a component item: `'standard'`, `'layout'`, or the owning pack's own namespace. Empty string for an attribute or value item, which has no `group`. */
|
|
25
|
+
export const STANDARD_ORIGIN_TAG = 'standard';
|
|
26
|
+
export const LAYOUT_ORIGIN_TAG = 'layout';
|
|
27
|
+
/**
|
|
28
|
+
* Survey finding A5: `apps/vscode/src/completion.ts:completionOriginTag`
|
|
29
|
+
* and `apps/obsidian/src/complete-component.ts:completionOriginTag` had
|
|
30
|
+
* IDENTICAL function bodies, differing only in which locally-defined
|
|
31
|
+
* constant stood in for the standard/layout tag string. Both apps'
|
|
32
|
+
* constants held the same lower-case values (`'standard'`/`'layout'`),
|
|
33
|
+
* so there was never a wording difference to preserve.
|
|
34
|
+
*/
|
|
35
|
+
export function completionOriginTag(item) {
|
|
36
|
+
if (item.kind !== 'component')
|
|
37
|
+
return '';
|
|
38
|
+
if (item.group === 'standard')
|
|
39
|
+
return STANDARD_ORIGIN_TAG;
|
|
40
|
+
if (item.group === 'layout')
|
|
41
|
+
return LAYOUT_ORIGIN_TAG;
|
|
42
|
+
return item.packName ?? '';
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Builds a `CatalogCache` around `buildCatalog` (typically
|
|
46
|
+
* `../insert/component-catalog.js`'s `buildComponentCatalog` composed with
|
|
47
|
+
* a pack-discovery call). Concurrent `get()` calls made before a build
|
|
48
|
+
* finishes share one in-flight promise rather than each starting their own
|
|
49
|
+
* pack discovery.
|
|
50
|
+
*/
|
|
51
|
+
export function createCatalogCache(buildCatalog) {
|
|
52
|
+
let cached;
|
|
53
|
+
let pending;
|
|
54
|
+
async function build() {
|
|
55
|
+
try {
|
|
56
|
+
return await buildCatalog();
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return [];
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
async get() {
|
|
64
|
+
if (cached !== undefined)
|
|
65
|
+
return cached;
|
|
66
|
+
if (pending === undefined) {
|
|
67
|
+
pending = build().then((catalog) => {
|
|
68
|
+
cached = catalog;
|
|
69
|
+
pending = undefined;
|
|
70
|
+
return catalog;
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
return pending;
|
|
74
|
+
},
|
|
75
|
+
peek() {
|
|
76
|
+
return cached;
|
|
77
|
+
},
|
|
78
|
+
invalidate() {
|
|
79
|
+
cached = undefined;
|
|
80
|
+
pending = undefined;
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
/** The line of `documentText` that `line` (zero-based) names, or `''` past the end of the document. */
|
|
85
|
+
function lineTextAt(documentText, line) {
|
|
86
|
+
return documentText.split('\n')[line] ?? '';
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* `completionAt` composed over a `HostEditor`'s document text, at the
|
|
90
|
+
* POSITION a completion request names — not `editor.cursor()`. A real
|
|
91
|
+
* editor's completion provider is invoked with an explicit position (the
|
|
92
|
+
* position the user is typing at when the provider fires), which is why a
|
|
93
|
+
* completion/hover request always carries its own `line`/`column` rather
|
|
94
|
+
* than reading the live caret a second time. `editor.cursor()` stays
|
|
95
|
+
* reserved for `insertComponentPlan`, which genuinely inserts at the
|
|
96
|
+
* caret.
|
|
97
|
+
*/
|
|
98
|
+
export function completeAtViaEditor(editor, position, catalog) {
|
|
99
|
+
const line = lineTextAt(editor.documentText(), position.line);
|
|
100
|
+
return completionAt(line, position.column, catalog);
|
|
101
|
+
}
|
|
102
|
+
/** Whether accepting `item` opens a container, and so needs `CompletionPlan.fenceEdits` applied with it. */
|
|
103
|
+
export function isContainerCompletionItem(item) {
|
|
104
|
+
try {
|
|
105
|
+
return insertedContainerColonCount(item.insertText) !== undefined;
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* `completionAt` over a whole document at a position, plus the two
|
|
113
|
+
* decisions both editors used to repeat at their own trigger boundary: a
|
|
114
|
+
* bare colon run that CLOSES an open container is not an opener waiting
|
|
115
|
+
* for a name (so no plan), and fence edits are computed once per response.
|
|
116
|
+
* Returns `undefined` when there is nothing to offer. Never throws.
|
|
117
|
+
*/
|
|
118
|
+
export function completionPlanAt(documentText, position, catalog) {
|
|
119
|
+
try {
|
|
120
|
+
const line = lineTextAt(documentText, position.line);
|
|
121
|
+
if (closesOpenContainerFence(documentText, position.line, line)) {
|
|
122
|
+
return undefined;
|
|
123
|
+
}
|
|
124
|
+
const context = completionAt(line, position.column, catalog);
|
|
125
|
+
if (context.kind === 'none' || context.items.length === 0) {
|
|
126
|
+
return undefined;
|
|
127
|
+
}
|
|
128
|
+
const container = context.items.find(isContainerCompletionItem);
|
|
129
|
+
let fenceEdits = [];
|
|
130
|
+
if (container !== undefined) {
|
|
131
|
+
try {
|
|
132
|
+
fenceEdits = fenceExtensionEdits(documentText, position.line, container.insertText)
|
|
133
|
+
.filter((edit) => edit.line !== position.line)
|
|
134
|
+
.map((edit) => ({
|
|
135
|
+
line: edit.line,
|
|
136
|
+
startColumn: edit.column,
|
|
137
|
+
endColumn: edit.column + edit.oldText.length,
|
|
138
|
+
text: edit.newText,
|
|
139
|
+
}));
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
fenceEdits = [];
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return {
|
|
146
|
+
kind: context.kind,
|
|
147
|
+
items: context.items,
|
|
148
|
+
replaceStart: context.replaceStart,
|
|
149
|
+
replaceEnd: context.replaceEnd,
|
|
150
|
+
tokenStart: context.tokenStart ?? context.replaceStart,
|
|
151
|
+
fenceEdits,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
return undefined;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
/** `hoverAt` composed over a `HostEditor`'s document text at a given position, the same way `completeAtViaEditor` composes `completionAt`. */
|
|
159
|
+
export function hoverAtViaEditor(editor, position, catalog) {
|
|
160
|
+
const line = lineTextAt(editor.documentText(), position.line);
|
|
161
|
+
return hoverAt(line, position.column, catalog);
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Plain-text hover documentation for `item`: `@markii/stdlib/editor`'s
|
|
165
|
+
* `formatComponentDocumentation`, the shape every host without a
|
|
166
|
+
* rich-markdown hover surface (or a host doing its own markdown mapping)
|
|
167
|
+
* can use as-is. This is also what `createMarkiiHost`'s `hoverAt` returns
|
|
168
|
+
* (via `hoverAtViaEditor`'s `HoverInfo.documentation`) rather than the
|
|
169
|
+
* bare summary sentence: the summary alone can read as generic prose with
|
|
170
|
+
* no component name in it (a callout's summary is "A colored box for an
|
|
171
|
+
* aside, warning, or danger note.", nowhere mentioning "callout"), while
|
|
172
|
+
* the formatted text's `Example: :::callout{}` line always names the
|
|
173
|
+
* directive.
|
|
174
|
+
*/
|
|
175
|
+
export function hoverDocumentationText(item) {
|
|
176
|
+
return formatComponentDocumentation(componentDocumentation(item));
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Builds the skeleton for `component` (`@markii/stdlib/editor`'s
|
|
180
|
+
* `componentSkeleton`), turns its flat `cursorOffset` into an absolute
|
|
181
|
+
* line/column given where the insertion happens, and folds in the fence
|
|
182
|
+
* edits (`@markii/stdlib/editor`'s `fenceExtensionEdits`) an enclosing
|
|
183
|
+
* container needs lengthened so it still nests legally. This is the pure
|
|
184
|
+
* half of both apps' `insert-component.ts` command handlers: build the
|
|
185
|
+
* skeleton, insert it at the cursor, lengthen any enclosing fences, place
|
|
186
|
+
* the cursor inside it — all in one plan a host applies as one undoable
|
|
187
|
+
* edit (`HostEditor.applyEdits`'s contract).
|
|
188
|
+
*
|
|
189
|
+
* `documentText` is the document as it stands before this insertion (the
|
|
190
|
+
* same text `fenceExtensionEdits` scans); a caller inserting from a
|
|
191
|
+
* completion accept passes the same text it already read to decide what
|
|
192
|
+
* to insert.
|
|
193
|
+
*
|
|
194
|
+
* Never throws: a failure to compute fence edits (hostile input, an
|
|
195
|
+
* unpaired document `fenceExtensionEdits` refuses to touch) degrades to
|
|
196
|
+
* the skeleton insertion alone, exactly as inserting always worked before
|
|
197
|
+
* fence auto-extension existed.
|
|
198
|
+
*/
|
|
199
|
+
export function insertComponentPlan(component, documentText, insertionLine, insertionColumn) {
|
|
200
|
+
const skeleton = componentSkeleton(component.directiveName, component.kind, component.requiredAttributes ?? []);
|
|
201
|
+
const relative = offsetToLineColumn(skeleton.text, skeleton.cursorOffset);
|
|
202
|
+
const cursor = relative.line === 0
|
|
203
|
+
? { line: insertionLine, column: insertionColumn + relative.column }
|
|
204
|
+
: { line: insertionLine + relative.line, column: relative.column };
|
|
205
|
+
const insertionEdit = {
|
|
206
|
+
line: insertionLine,
|
|
207
|
+
startColumn: insertionColumn,
|
|
208
|
+
endColumn: insertionColumn,
|
|
209
|
+
text: skeleton.text,
|
|
210
|
+
};
|
|
211
|
+
let fenceEdits;
|
|
212
|
+
try {
|
|
213
|
+
fenceEdits = fenceExtensionEdits(documentText, insertionLine, skeleton.text)
|
|
214
|
+
.filter((edit) => edit.line !== insertionLine)
|
|
215
|
+
.map((edit) => ({
|
|
216
|
+
line: edit.line,
|
|
217
|
+
startColumn: edit.column,
|
|
218
|
+
endColumn: edit.column + edit.oldText.length,
|
|
219
|
+
text: edit.newText,
|
|
220
|
+
}));
|
|
221
|
+
}
|
|
222
|
+
catch {
|
|
223
|
+
fenceEdits = [];
|
|
224
|
+
}
|
|
225
|
+
const edits = [...fenceEdits, insertionEdit].sort((a, b) => a.line !== b.line ? b.line - a.line : b.startColumn - a.startColumn);
|
|
226
|
+
return { edits, cursor };
|
|
227
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Downgrades `text` to plain CommonMark: container directives lose their
|
|
3
|
+
* opening/closing fence lines and keep their inner markdown, leaf
|
|
4
|
+
* directives are removed, a text directive is replaced by its own text
|
|
5
|
+
* content, and named script fences are removed entirely. A component
|
|
6
|
+
* becomes its inner content only — this is a readability floor, not a
|
|
7
|
+
* faithful render.
|
|
8
|
+
*
|
|
9
|
+
* Every edit is a pure deletion (the inner text a directive wraps is
|
|
10
|
+
* already the correct, final text — nothing is rewritten, only the
|
|
11
|
+
* surrounding directive syntax is cut away), applied from the END of the
|
|
12
|
+
* source backwards so earlier offsets stay valid as later ones are
|
|
13
|
+
* consumed.
|
|
14
|
+
*/
|
|
15
|
+
export declare function mdPlainFromSource(text: string): string;
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `md-plain` export format: a SOURCE-LEVEL rewrite of a note down to
|
|
3
|
+
* plain CommonMark. Moved out of `apps/cli/src/export-note.ts` (the only
|
|
4
|
+
* host that ever offered this format): pure logic over `@markii/core`'s
|
|
5
|
+
* parse tree that never imports `node:*`, so it never belonged behind an
|
|
6
|
+
* app's main entry in the first place.
|
|
7
|
+
*
|
|
8
|
+
* No markdown serializer, no new dependency: every directive's own
|
|
9
|
+
* fence/bracket syntax is deleted from the original text using the
|
|
10
|
+
* offsets `@markii/core`'s `parse` already puts on every node, leaving the
|
|
11
|
+
* author's own inner markdown untouched.
|
|
12
|
+
*/
|
|
13
|
+
import { parse, extractScripts } from '@markii/core';
|
|
14
|
+
import { visit, SKIP } from 'unist-util-visit';
|
|
15
|
+
function offsetsOf(position) {
|
|
16
|
+
if (position === undefined ||
|
|
17
|
+
position.start.offset === undefined ||
|
|
18
|
+
position.end.offset === undefined) {
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
return { start: position.start.offset, end: position.end.offset };
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* The shared logic for a container directive or a text directive: keep
|
|
25
|
+
* exactly the children's own source range, trimming the fence lines
|
|
26
|
+
* (container) or `:name[`/`]{...}` (text) away on either side. An empty
|
|
27
|
+
* directive (no children — attributes only, or no label) has nothing to
|
|
28
|
+
* preserve, so its whole range is removed instead, and visiting does not
|
|
29
|
+
* need to descend into it.
|
|
30
|
+
*/
|
|
31
|
+
function wrapperEdits(node, edits) {
|
|
32
|
+
const outer = offsetsOf(node.position);
|
|
33
|
+
if (outer === undefined)
|
|
34
|
+
return undefined;
|
|
35
|
+
const { children } = node;
|
|
36
|
+
if (children.length === 0) {
|
|
37
|
+
edits.push(outer);
|
|
38
|
+
return SKIP;
|
|
39
|
+
}
|
|
40
|
+
const first = children[0];
|
|
41
|
+
const last = children[children.length - 1];
|
|
42
|
+
const firstOffsets = offsetsOf(first?.position);
|
|
43
|
+
const lastOffsets = offsetsOf(last?.position);
|
|
44
|
+
if (firstOffsets !== undefined && firstOffsets.start > outer.start) {
|
|
45
|
+
edits.push({ start: outer.start, end: firstOffsets.start });
|
|
46
|
+
}
|
|
47
|
+
if (lastOffsets !== undefined && lastOffsets.end < outer.end) {
|
|
48
|
+
edits.push({ start: lastOffsets.end, end: outer.end });
|
|
49
|
+
}
|
|
50
|
+
// No SKIP: keep visiting into `children` so a nested directive inside
|
|
51
|
+
// this one's content still gets its own wrapper stripped.
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Every directive's wrapper-syntax edit: a leaf directive (`::name[...]`)
|
|
56
|
+
* is removed in full (there is no "inner markdown" to keep — a leaf
|
|
57
|
+
* directive's label is inline attributes-shaped content, not a nested
|
|
58
|
+
* document); a container or text directive keeps its inner markdown and
|
|
59
|
+
* loses only its own fence/bracket syntax (`wrapperEdits`, above).
|
|
60
|
+
*/
|
|
61
|
+
function collectDirectiveEdits(tree) {
|
|
62
|
+
const edits = [];
|
|
63
|
+
visit(tree, 'containerDirective', (node) => wrapperEdits(node, edits));
|
|
64
|
+
visit(tree, 'textDirective', (node) => wrapperEdits(node, edits));
|
|
65
|
+
visit(tree, 'leafDirective', (node) => {
|
|
66
|
+
const outer = offsetsOf(node.position);
|
|
67
|
+
if (outer !== undefined)
|
|
68
|
+
edits.push(outer);
|
|
69
|
+
return SKIP;
|
|
70
|
+
});
|
|
71
|
+
return edits;
|
|
72
|
+
}
|
|
73
|
+
/** Script fences are removed entirely — their own `position` (the whole fence, per `@markii/core`'s `extractScripts`) is the edit. */
|
|
74
|
+
function collectScriptEdits(tree) {
|
|
75
|
+
const edits = [];
|
|
76
|
+
const blocks = extractScripts(tree);
|
|
77
|
+
for (const block of blocks) {
|
|
78
|
+
const offsets = offsetsOf(block.position);
|
|
79
|
+
if (offsets !== undefined)
|
|
80
|
+
edits.push(offsets);
|
|
81
|
+
}
|
|
82
|
+
return edits;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Downgrades `text` to plain CommonMark: container directives lose their
|
|
86
|
+
* opening/closing fence lines and keep their inner markdown, leaf
|
|
87
|
+
* directives are removed, a text directive is replaced by its own text
|
|
88
|
+
* content, and named script fences are removed entirely. A component
|
|
89
|
+
* becomes its inner content only — this is a readability floor, not a
|
|
90
|
+
* faithful render.
|
|
91
|
+
*
|
|
92
|
+
* Every edit is a pure deletion (the inner text a directive wraps is
|
|
93
|
+
* already the correct, final text — nothing is rewritten, only the
|
|
94
|
+
* surrounding directive syntax is cut away), applied from the END of the
|
|
95
|
+
* source backwards so earlier offsets stay valid as later ones are
|
|
96
|
+
* consumed.
|
|
97
|
+
*/
|
|
98
|
+
export function mdPlainFromSource(text) {
|
|
99
|
+
const tree = parse(text);
|
|
100
|
+
const edits = [
|
|
101
|
+
...collectDirectiveEdits(tree),
|
|
102
|
+
...collectScriptEdits(tree),
|
|
103
|
+
].sort((a, b) => b.start - a.start);
|
|
104
|
+
let result = text;
|
|
105
|
+
for (const edit of edits) {
|
|
106
|
+
result = result.slice(0, edit.start) + result.slice(edit.end);
|
|
107
|
+
}
|
|
108
|
+
return collapseBlankRuns(result);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Collapses a run of three or more consecutive newlines down to two, and
|
|
112
|
+
* trims the leading and trailing blank lines off the whole file.
|
|
113
|
+
*
|
|
114
|
+
* Cutting a directive out of the source leaves the blank lines that stood
|
|
115
|
+
* on either side of it behind, so a note with a few leaf directives comes
|
|
116
|
+
* out pocked with three and four line gaps. CommonMark treats any run of
|
|
117
|
+
* blank lines as one paragraph break, so closing them up changes nothing
|
|
118
|
+
* about how the file parses and a great deal about how it reads.
|
|
119
|
+
*/
|
|
120
|
+
function collapseBlankRuns(text) {
|
|
121
|
+
return `${text
|
|
122
|
+
.replace(/\n{3,}/g, '\n\n')
|
|
123
|
+
.replace(/^\n+/, '')
|
|
124
|
+
.replace(/\n+$/, '')}\n`;
|
|
125
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `exportNote` behavior: outcome shape, wording, and format gating,
|
|
3
|
+
* merged from `apps/vscode/src/export-html.ts`, the host-neutral half of
|
|
4
|
+
* `apps/obsidian/src/export-note.ts`, and `apps/cli/src/export-note.ts`'s
|
|
5
|
+
* `exportHtml` (survey finding A10, table rows 22/26).
|
|
6
|
+
*
|
|
7
|
+
* `html` and `md-plain` are fully implemented here, over `../export/note-export.js`'s
|
|
8
|
+
* `buildNoteExport` (already host-neutral, already shared) and
|
|
9
|
+
* `./export/md-plain.js`'s `mdPlainFromSource` (moved in this same phase).
|
|
10
|
+
* `ansi` needs an engine this package does not depend on
|
|
11
|
+
* (`@markii/ansi` is deliberately never a `@markii/host` dependency — see
|
|
12
|
+
* AGENTS.md's package boundaries), so it is rendered through the injected
|
|
13
|
+
* `renderAnsi` option; a host that omits it gets `Unsupported`.
|
|
14
|
+
*
|
|
15
|
+
* PDF stays a seam, never an implementation here, exactly as
|
|
16
|
+
* `tmp/W11-adapter-design.md` specifies: this module builds the same HTML
|
|
17
|
+
* document `html` would have written and hands it to
|
|
18
|
+
* `adapter.exports.htmlToPdf`, but the DEGRADE-TO-HTML-ON-FAILURE product
|
|
19
|
+
* behavior Obsidian's own `exportNoteAsPdf` layers on top (falling back to
|
|
20
|
+
* writing the HTML file when printing is unavailable or fails, and
|
|
21
|
+
* reporting which of the two happened) is Obsidian-specific policy about
|
|
22
|
+
* what to do with a seam's failure, not the seam itself — it stays
|
|
23
|
+
* app-side. See `tmp/W11-phase1b.md` for this call.
|
|
24
|
+
*
|
|
25
|
+
* Cascade orchestration (multi-note export, `walkNoteCascade` +
|
|
26
|
+
* `assignCascadeFileNames` + `rewriteCascadeLinks` + `zipExportArchive`)
|
|
27
|
+
* is NOT moved in this pass — see `tmp/W11-phase1b.md`'s deviations
|
|
28
|
+
* section for why, and what Phase 2 (or a follow-up) still needs to do.
|
|
29
|
+
*/
|
|
30
|
+
import type { StoredValue } from '@markii/runtime';
|
|
31
|
+
import type { OnDiagnostic, UnavailableComponent } from '@markii/stdlib';
|
|
32
|
+
import type { HtmlExportEngine, ExportBodyRenderer, ExportPackStylesheet, ExportRenderInfo, StaticExportReason } from '../export/note-export.js';
|
|
33
|
+
import type { EmbeddedImageReport, ExportImageReader } from '../export/image-embed.js';
|
|
34
|
+
import type { HostAdapter, HostExportFormat, HostLabels } from './adapter.js';
|
|
35
|
+
/** The file name an export lands under, by format: the note's own base name with the format's own extension, so the export lands beside the note unless the host's picker says otherwise. */
|
|
36
|
+
export declare function exportDefaultFileName(notePath: string, format: HostExportFormat): string;
|
|
37
|
+
/** What a `'html'` or `'pdf'` export attempt produced. Every shape except `failed` means the host got a file, and every shape but `failed` carries the same diagnostics-facing detail so the two hosts' wording can share it. */
|
|
38
|
+
export type NoteFileExportOutcome = {
|
|
39
|
+
readonly kind: 'written';
|
|
40
|
+
readonly path: string;
|
|
41
|
+
readonly bytes: number;
|
|
42
|
+
/** How many last-run values were baked into the file. */
|
|
43
|
+
readonly valueCount: number;
|
|
44
|
+
/** `true` when the note contains a script fence. Absent or `false` means no scripts, so the message never explains an empty state the note cannot have. */
|
|
45
|
+
readonly hasScripts?: boolean;
|
|
46
|
+
/** Which engine rendered the body, and why when it was the static one. Diagnostics-facing only. */
|
|
47
|
+
readonly render: ExportRenderInfo;
|
|
48
|
+
/** What image embedding did. The empty report when the note has no images or none was offered a reader. */
|
|
49
|
+
readonly images: EmbeddedImageReport;
|
|
50
|
+
} | {
|
|
51
|
+
readonly kind: 'failed';
|
|
52
|
+
readonly path?: string;
|
|
53
|
+
readonly reason: string;
|
|
54
|
+
};
|
|
55
|
+
/** The last path segment of a `/`-separated or `\`-separated path, for naming a file in a message. */
|
|
56
|
+
export declare function fileNameOf(path: string): string;
|
|
57
|
+
/**
|
|
58
|
+
* The short result message for a written or failed html/pdf export. A
|
|
59
|
+
* success names the file. A failure says what failed and points at this
|
|
60
|
+
* host's own diagnostics surface (`labels.diagnosticsSurface`), never at
|
|
61
|
+
* a stack trace — the verbatim reason goes there instead.
|
|
62
|
+
*/
|
|
63
|
+
export declare function exportResultMessage(outcome: NoteFileExportOutcome, labels: HostLabels): string;
|
|
64
|
+
/** One diagnostic line describing HOW an export's body was rendered: the pack-vs-static distinction, kept off the result message and put here instead. */
|
|
65
|
+
export declare function renderEngineDiagnosticLine(render: ExportRenderInfo): string;
|
|
66
|
+
/** `bytes`, formatted as a human-readable size (`"1.2 KB"`, `"512 bytes"`). */
|
|
67
|
+
export declare function formatByteSize(bytes: number): string;
|
|
68
|
+
/** The image lines for `exportDiagnosticLines`: how many images were embedded and how many bytes they added, one line per skipped image, and a remote-source count when any sources still reach the network. Empty when a note has no images at all. */
|
|
69
|
+
export declare function imageEmbedDiagnosticLines(images: EmbeddedImageReport): string[];
|
|
70
|
+
/** The full diagnostics-surface detail for one html/pdf export attempt. */
|
|
71
|
+
export declare function exportDiagnosticLines(outcome: NoteFileExportOutcome): string[];
|
|
72
|
+
/** What `exportHtmlDocumentViaAdapter` needs beyond the note's own text and path. */
|
|
73
|
+
export interface ExportHtmlDocumentRequest {
|
|
74
|
+
/** The static HTML engine. Required for the `html` and `pdf` formats; `exportNoteFileViaAdapter` declines those without it. */
|
|
75
|
+
readonly engine?: HtmlExportEngine;
|
|
76
|
+
readonly notePath: string;
|
|
77
|
+
readonly text: string;
|
|
78
|
+
readonly values?: Record<string, StoredValue>;
|
|
79
|
+
readonly renderBody?: ExportBodyRenderer;
|
|
80
|
+
readonly staticReason?: StaticExportReason;
|
|
81
|
+
readonly packStylesheets?: readonly ExportPackStylesheet[];
|
|
82
|
+
readonly packCount?: number;
|
|
83
|
+
readonly embedImages?: ExportImageReader;
|
|
84
|
+
readonly hideScriptBlocks?: boolean;
|
|
85
|
+
/** Installed pack components the static engine cannot draw; see `NoteExportBuildRequest`. */
|
|
86
|
+
readonly unavailableComponents?: readonly UnavailableComponent[];
|
|
87
|
+
/** Receives the static engine's render diagnostics. */
|
|
88
|
+
readonly onDiagnostic?: OnDiagnostic;
|
|
89
|
+
}
|
|
90
|
+
/** Builds the standalone HTML document `html` and `pdf` both export, via the shared, already-host-neutral `buildNoteExport`. Never throws: a failing `renderBody` degrades to the static engine rather than propagating. */
|
|
91
|
+
export declare function buildExportDocument(request: ExportHtmlDocumentRequest & {
|
|
92
|
+
readonly engine: HtmlExportEngine;
|
|
93
|
+
}): Promise<{
|
|
94
|
+
html: string;
|
|
95
|
+
valueCount: number;
|
|
96
|
+
hasScripts: boolean;
|
|
97
|
+
render: ExportRenderInfo;
|
|
98
|
+
images: EmbeddedImageReport;
|
|
99
|
+
}>;
|
|
100
|
+
/**
|
|
101
|
+
* Exports one note as `format`, gated on `adapter.exports?.formats`. A
|
|
102
|
+
* format the host has not declared returns `undefined` immediately
|
|
103
|
+
* (`createMarkiiHost`'s `exportNote` turns that into the shared
|
|
104
|
+
* `Unsupported` outcome and writes its own diagnostics line, matching
|
|
105
|
+
* every other capability-gated behavior).
|
|
106
|
+
*
|
|
107
|
+
* `renderAnsi` is required to actually produce an `'ansi'` outcome — a
|
|
108
|
+
* host that offers the `'ansi'` FORMAT capability but passes no renderer
|
|
109
|
+
* is a configuration bug on that host's part, not a Markii user's problem
|
|
110
|
+
* to see, so it also resolves to `undefined` here (never a thrown error)
|
|
111
|
+
* and the caller's `Unsupported` fallback covers it.
|
|
112
|
+
*/
|
|
113
|
+
export declare function exportNoteFileViaAdapter(adapter: HostAdapter, format: HostExportFormat, request: ExportHtmlDocumentRequest, renderAnsi?: (text: string) => Promise<string>): Promise<NoteFileExportOutcome | undefined>;
|