yamlover 0.3.4 → 0.3.6
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/bin/yamlover.js +199 -112
- package/dist/client/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- package/dist/client/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- package/dist/client/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- package/dist/client/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- package/dist/client/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- package/dist/client/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- package/dist/client/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- package/dist/client/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- package/dist/client/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- package/dist/client/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- package/dist/client/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- package/dist/client/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- package/dist/client/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- package/dist/client/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- package/dist/client/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- package/dist/client/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- package/dist/client/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- package/dist/client/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- package/dist/client/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- package/dist/client/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- package/dist/client/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- package/dist/client/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- package/dist/client/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- package/dist/client/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- package/dist/client/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- package/dist/client/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- package/dist/client/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- package/dist/client/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- package/dist/client/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- package/dist/client/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- package/dist/client/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- package/dist/client/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- package/dist/client/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- package/dist/client/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- package/dist/client/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- package/dist/client/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- package/dist/client/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- package/dist/client/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- package/dist/client/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- package/dist/client/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- package/dist/client/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- package/dist/client/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- package/dist/client/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- package/dist/client/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- package/dist/client/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- package/dist/client/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- package/dist/client/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- package/dist/client/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- package/dist/client/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- package/dist/client/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- package/dist/client/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- package/dist/client/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- package/dist/client/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- package/dist/client/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- package/dist/client/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- package/dist/client/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- package/dist/client/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- package/dist/client/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- package/dist/client/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- package/dist/client/assets/_commonjs-dynamic-modules-TDtrdbi3.js +1 -0
- package/dist/client/assets/decoded-CFkcuQ1f.js +1 -0
- package/dist/client/assets/djvu-B3wuFWwI.js +1 -0
- package/dist/client/assets/docx-putHYXzO.js +230 -0
- package/dist/client/assets/heic-oIxhIbIW.js +66 -0
- package/dist/client/assets/imagemap-CUKUIcGe.js +1 -0
- package/dist/client/assets/index-CQ3TGVSx.js +617 -0
- package/dist/client/assets/index-wTjOzmSY.css +1 -0
- package/dist/client/assets/map-CZS083CS.js +1 -0
- package/dist/client/assets/paged-CjxSuuik.js +1 -0
- package/dist/client/assets/panzoom-C7OuIIda.js +4 -0
- package/dist/client/assets/panzoom-CIGW-MKW.css +1 -0
- package/dist/client/assets/pdf-Dz3gUx0B.js +12 -0
- package/dist/client/assets/pdf-m5e3jsV4.css +1 -0
- package/dist/client/assets/pdf.worker.min-qwK7q_zL.mjs +28 -0
- package/dist/client/assets/psd-C8yNwHFw.js +11 -0
- package/dist/client/assets/spreadsheet-DBv7aqoN.js +38 -0
- package/dist/client/assets/tiff-73RKtIux.js +1 -0
- package/{index.html → dist/client/index.html} +2 -1
- package/package.json +18 -21
- package/src/client/App.tsx +0 -424
- package/src/client/NodeView.tsx +0 -422
- package/src/client/TaskStrip.tsx +0 -34
- package/src/client/Tree.tsx +0 -97
- package/src/client/api.ts +0 -213
- package/src/client/icons.ts +0 -91
- package/src/client/links.tsx +0 -108
- package/src/client/live.ts +0 -42
- package/src/client/main.tsx +0 -10
- package/src/client/paste-html.ts +0 -228
- package/src/client/paste-links.ts +0 -42
- package/src/client/paths.ts +0 -139
- package/src/client/render.tsx +0 -339
- package/src/client/renderers/annotate.tsx +0 -641
- package/src/client/renderers/asciidoc.tsx +0 -36
- package/src/client/renderers/chapter.tsx +0 -142
- package/src/client/renderers/csv.tsx +0 -234
- package/src/client/renderers/decoded.tsx +0 -72
- package/src/client/renderers/djvu.tsx +0 -271
- package/src/client/renderers/djvuWorker.ts +0 -99
- package/src/client/renderers/doc.tsx +0 -40
- package/src/client/renderers/docx.tsx +0 -49
- package/src/client/renderers/epub.tsx +0 -147
- package/src/client/renderers/explorer.tsx +0 -294
- package/src/client/renderers/fb2.tsx +0 -149
- package/src/client/renderers/headings.ts +0 -69
- package/src/client/renderers/heic.tsx +0 -23
- package/src/client/renderers/imagemap.tsx +0 -170
- package/src/client/renderers/kml.ts +0 -46
- package/src/client/renderers/latex.tsx +0 -37
- package/src/client/renderers/map.tsx +0 -205
- package/src/client/renderers/marklower.tsx +0 -120
- package/src/client/renderers/markup.tsx +0 -64
- package/src/client/renderers/media.tsx +0 -19
- package/src/client/renderers/paged.ts +0 -137
- package/src/client/renderers/panzoom.ts +0 -101
- package/src/client/renderers/pdf.tsx +0 -316
- package/src/client/renderers/plaintext.tsx +0 -120
- package/src/client/renderers/plantuml.tsx +0 -83
- package/src/client/renderers/psd.tsx +0 -25
- package/src/client/renderers/registry.tsx +0 -411
- package/src/client/renderers/rtf.tsx +0 -210
- package/src/client/renderers/spreadsheet.tsx +0 -105
- package/src/client/renderers/tag.tsx +0 -113
- package/src/client/renderers/text.tsx +0 -42
- package/src/client/renderers/tiff.tsx +0 -33
- package/src/client/styles.css +0 -1212
- package/src/client/vendor/README.md +0 -30
- package/src/client/vite-env.d.ts +0 -31
- package/src/server/api.ts +0 -147
- package/src/server/embed.ts +0 -187
- package/src/server/engine-api.ts +0 -1563
- package/src/server/gitignore.ts +0 -81
- package/src/server/node-kind.ts +0 -61
- package/src/server/tasks.ts +0 -83
- package/src/server/yamlover.ts +0 -1133
- /package/{src/client/vendor/djvu.js → dist/client/assets/djvu-Ci0lFKxE.js} +0 -0
package/src/client/api.ts
DELETED
|
@@ -1,213 +0,0 @@
|
|
|
1
|
-
// Typed wrappers over the server's JSON API.
|
|
2
|
-
|
|
3
|
-
export interface TreeNode {
|
|
4
|
-
path: string;
|
|
5
|
-
label: string;
|
|
6
|
-
type: string;
|
|
7
|
-
format: string | null;
|
|
8
|
-
valueType?: string | null; // renderer dispatch facets (TYPES.md §9)
|
|
9
|
-
hasKeyed?: boolean;
|
|
10
|
-
hasOrdinal?: boolean;
|
|
11
|
-
concrete: string | null; // how it is stored; `dir` → a plain-folder icon
|
|
12
|
-
hasChildren: boolean;
|
|
13
|
-
children: TreeNode[];
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export interface NodeJson {
|
|
17
|
-
path: string;
|
|
18
|
-
type: string;
|
|
19
|
-
format?: string | null; // schema `format`; with the facets it keys the renderer (TYPES.md §9)
|
|
20
|
-
valueType?: string | null; // the scalar self-VALUE's type (null|boolean|integer|number|string|binary), or null
|
|
21
|
-
hasKeyed?: boolean; // owns ≥1 keyed element
|
|
22
|
-
hasOrdinal?: boolean; // owns ≥1 ordinal (keyless) element
|
|
23
|
-
concrete: string | null;
|
|
24
|
-
documentPath?: string; // the document (nearest yamlover entity) this node is in —
|
|
25
|
-
// the anchor a document-relative (`/…`) link resolves against
|
|
26
|
-
title: string | null;
|
|
27
|
-
description: string | null;
|
|
28
|
-
value: unknown;
|
|
29
|
-
relations?: Record<string, unknown>; // named up-edges (+ `..`) as ref markers
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
async function getJson<T>(url: string): Promise<T> {
|
|
33
|
-
const res = await fetch(url);
|
|
34
|
-
const body = await res.json();
|
|
35
|
-
if (!res.ok) throw new Error((body && body.error) || `HTTP ${res.status}`);
|
|
36
|
-
return body as T;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/** Server info: the ROOT path as given on the CLI (breadcrumb head; "" if omitted). */
|
|
40
|
-
export function fetchInfo(): Promise<{ root: string }> {
|
|
41
|
-
return getJson<{ root: string }>("/api/info");
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/** A long-running server task (indexing, hashing, …) — mirrors server/tasks.ts. Updates ride
|
|
45
|
-
* /api/events as `{type:"task", task}` frames; this shape is also what GET /api/tasks lists. */
|
|
46
|
-
export interface TaskInfo {
|
|
47
|
-
id: string;
|
|
48
|
-
label: string;
|
|
49
|
-
state: "running" | "done" | "error";
|
|
50
|
-
progress: { done: number; total?: number; message?: string };
|
|
51
|
-
startedAt: number;
|
|
52
|
-
finishedAt?: number;
|
|
53
|
-
error?: string;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/** Server tasks in flight (or just finished) — the snapshot a freshly loaded page needs. */
|
|
57
|
-
export function fetchTasks(): Promise<TaskInfo[]> {
|
|
58
|
-
return getJson<TaskInfo[]>("/api/tasks");
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/** The TOC subtree rooted at `path`, `depth` levels deep (server default 3). */
|
|
62
|
-
export function fetchTree(path = ":", depth?: number): Promise<TreeNode> {
|
|
63
|
-
const q = new URLSearchParams({ path });
|
|
64
|
-
if (depth != null) q.set("depth", String(depth));
|
|
65
|
-
return getJson<TreeNode>(`/api/tree?${q}`);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export function fetchNode(
|
|
69
|
-
path: string,
|
|
70
|
-
depth?: number,
|
|
71
|
-
opts?: { binary?: boolean },
|
|
72
|
-
): Promise<NodeJson> {
|
|
73
|
-
const q = new URLSearchParams({ path });
|
|
74
|
-
if (depth != null) q.set("depth", String(depth));
|
|
75
|
-
if (opts?.binary) q.set("binary", "1"); // request a binary leaf's base64 bytes
|
|
76
|
-
return getJson<NodeJson>(`/api/json?${q}`);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/** URL of a file-backed node's raw bytes (image / pdf / html / djvu source). */
|
|
80
|
-
export function blobUrl(path: string): string {
|
|
81
|
-
return `/api/blob?path=${encodeURIComponent(path)}`;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/** The node's instance schema, one level deep (nested containers as link markers). */
|
|
85
|
-
export function fetchSchema(path: string, depth?: number): Promise<unknown> {
|
|
86
|
-
const q = new URLSearchParams({ path });
|
|
87
|
-
if (depth != null) q.set("depth", String(depth));
|
|
88
|
-
return getJson<unknown>(`/api/schema?${q}`);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/** A tag as the annotation API hands it around: its node path, display name (the taxonomy key),
|
|
92
|
-
* and explicit color — null for a named tag, whose hue the client derives from the name. */
|
|
93
|
-
export interface TagRef {
|
|
94
|
-
path: string;
|
|
95
|
-
name: string;
|
|
96
|
-
color: string | null;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/** An annotation of a material — ONE TAG APPLICATION (ANNOTATIONS.md): the whole node, or a
|
|
100
|
-
* fragment within it (then `selector` carries that fragment's region and `fragmentSlug` its
|
|
101
|
-
* key, and `imageUrl` its crop), tagged by `tag` with optional `description` / `params`. */
|
|
102
|
-
export interface Annotation {
|
|
103
|
-
tag?: TagRef | null;
|
|
104
|
-
selector?: { type?: string; exact?: string; prefix?: string; suffix?: string; [k: string]: unknown };
|
|
105
|
-
fragmentSlug?: string; // set when the tag is on a fragment (the region) rather than the whole node
|
|
106
|
-
imageUrl?: string; // an image-like fragment's crop (a /api/blob URL)
|
|
107
|
-
description?: string;
|
|
108
|
-
params?: Record<string, unknown>;
|
|
109
|
-
created?: string;
|
|
110
|
-
path?: string; // a transient client marker only ("(preview)"/"(pending)"); annotations have no node path
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/** The annotations whose `target` is the material at `path` (the engine's reverse link). */
|
|
114
|
-
export function fetchAnnotations(path: string): Promise<Annotation[]> {
|
|
115
|
-
return getJson<Annotation[]>(`/api/annotations?path=${encodeURIComponent(path)}`);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/** The materials filed under the tag at `path` — `$yamloverLink` markers, annotations already
|
|
119
|
-
* resolved to their `target` and deduped (the explorer's member list for a tag page). */
|
|
120
|
-
export function fetchTagged(path: string): Promise<unknown[]> {
|
|
121
|
-
return getJson<unknown[]>(`/api/tagged?path=${encodeURIComponent(path)}`);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/** Evaluate a colon-grammar QUERY (QUERY.md / engine `query` op) at `at` (default: the root `:`),
|
|
125
|
-
* returning the matched node paths in canonical colon form. A malformed query rejects (the
|
|
126
|
-
* server answers 400). Reused by the tag-picker typeahead and, later, by find-usages. */
|
|
127
|
-
export function query(q: string, at = ":"): Promise<string[]> {
|
|
128
|
-
const params = new URLSearchParams({ q, path: at });
|
|
129
|
-
return getJson<{ results: string[] }>(`/api/query?${params}`).then((r) => r.results);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
async function postJson<T>(url: string, body: unknown): Promise<T> {
|
|
133
|
-
const res = await fetch(url, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body) });
|
|
134
|
-
const json = await res.json();
|
|
135
|
-
if (!res.ok) throw new Error((json && json.error) || `HTTP ${res.status}`);
|
|
136
|
-
return json as T;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/** Create a FRAGMENT — a marked region in the node at `target` (ANNOTATIONS.md). Returns its slug
|
|
140
|
-
* and full node path, which is then the `target` for {@link annotate}. `imageBase64` is an
|
|
141
|
-
* optional PNG crop for image-like selections. */
|
|
142
|
-
export function createFragment(target: string, selector: Record<string, unknown>, imageBase64?: string): Promise<{ slug: string; fragmentPath: string }> {
|
|
143
|
-
return postJson("/api/fragment", { target, selector, ...(imageBase64 ? { imageBase64 } : {}) });
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/** Apply the tag at `tag` to the node at `target` (a whole node OR a fragment path) — appends to
|
|
147
|
-
* the target's `yamlover-annotations`. `description`/`params` make it a parametrized annotation. */
|
|
148
|
-
export function annotate(a: { target: string; tag: string; description?: string; params?: Record<string, unknown> }): Promise<{ ok: true }> {
|
|
149
|
-
return postJson("/api/annotate", a);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/** Create a named tag at the project's default tags location (settings.yamlover; `/tags` by
|
|
153
|
-
* default) — the picker's create-on-miss. Idempotent: an existing tag at that path is returned
|
|
154
|
-
* as-is; a non-tag node already occupying the path is an error. */
|
|
155
|
-
export function createTag(name: string): Promise<TagRef> {
|
|
156
|
-
return fetch("/api/tag", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name }) }).then(
|
|
157
|
-
async (res) => {
|
|
158
|
-
const body = await res.json();
|
|
159
|
-
if (!res.ok) throw new Error((body && body.error) || `HTTP ${res.status}`);
|
|
160
|
-
return body as TagRef;
|
|
161
|
-
},
|
|
162
|
-
);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/** The result of pasting/uploading a file or text: the new file's node path (for a text chunk,
|
|
166
|
-
* the chapter it joined), and (for a chapter) the chapter path plus any chunk pointer appended. */
|
|
167
|
-
export interface PasteResult {
|
|
168
|
-
path: string; // the new file's node path (a text chunk: the chapter's own path)
|
|
169
|
-
chapter?: string; // the chapter the chunk was appended to (chapter paste only)
|
|
170
|
-
pointer?: string; // the `*…` chunk pointer appended (chapter FILE paste only)
|
|
171
|
-
dir?: string; // the enclosing directory the file landed in (directory/member paste)
|
|
172
|
-
open?: boolean; // true when the page was a MEMBER of a directory → open the new file
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
function postPaste(body: Record<string, unknown>): Promise<PasteResult> {
|
|
176
|
-
return fetch("/api/paste", {
|
|
177
|
-
method: "POST",
|
|
178
|
-
headers: { "Content-Type": "application/json" },
|
|
179
|
-
body: JSON.stringify(body),
|
|
180
|
-
}).then(async (res) => {
|
|
181
|
-
const json = await res.json();
|
|
182
|
-
if (!res.ok) throw new Error((json && json.error) || `HTTP ${res.status}`);
|
|
183
|
-
return json as PasteResult;
|
|
184
|
-
});
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
/** Upload a pasted file onto the page at `target` (a directory or a chapter). */
|
|
188
|
-
export function pasteFile(target: string, filename: string, contentBase64: string): Promise<PasteResult> {
|
|
189
|
-
return postPaste({ path: target, filename, contentBase64 });
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
/** Paste plain TEXT onto the page at `target`: a chapter gains it as a new chunk; anywhere else
|
|
193
|
-
* it becomes a new chapter .yamlover file in the nearest enclosing directory. */
|
|
194
|
-
export function pasteText(target: string, text: string): Promise<PasteResult> {
|
|
195
|
-
return postPaste({ path: target, text });
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/** Paste RICH content (an HTML selection: text + images + heading-nested subchapters) onto the
|
|
199
|
-
* page at `target`. A chapter appends the chunks and subchapters; anywhere else a new chapter
|
|
200
|
-
* is created — directory-backed when files are present, a standalone .yamlover file otherwise.
|
|
201
|
-
* `rich` is the RichNode tree from paste-html.ts (images already inline as base64 files). */
|
|
202
|
-
export function pasteRich(target: string, rich: unknown): Promise<PasteResult> {
|
|
203
|
-
return postPaste({ path: target, rich });
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
/** Remove the application of `tag` from the node at `target` (a whole node OR a fragment path) —
|
|
207
|
-
* splices the matching element out of its `yamlover-annotations`. */
|
|
208
|
-
export function deleteAnnotation(target: string, tag: string): Promise<void> {
|
|
209
|
-
const q = new URLSearchParams({ target, tag });
|
|
210
|
-
return fetch(`/api/annotate?${q}`, { method: "DELETE" }).then(async (res) => {
|
|
211
|
-
if (!res.ok) throw new Error(((await res.json().catch(() => null))?.error) || `HTTP ${res.status}`);
|
|
212
|
-
});
|
|
213
|
-
}
|
package/src/client/icons.ts
DELETED
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
// Type/format icon for a TOC node — chosen by the schema `format`, falling back
|
|
2
|
-
// to `type`. One exception to being concrete-agnostic: a node stored as an
|
|
3
|
-
// on-disk directory gets a folder icon, since it really is a filesystem folder —
|
|
4
|
-
// plain (`dir`, no `.yamlover/` marker) or a yamlover entity (`yamlover`).
|
|
5
|
-
|
|
6
|
-
export interface Glyph {
|
|
7
|
-
glyph: string;
|
|
8
|
-
cls: string; // CSS class (color)
|
|
9
|
-
title: string; // tooltip
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
// Type → a monochrome glyph, colored by category (matches the value highlighting).
|
|
13
|
-
const TYPE: Record<string, { glyph: string; cls: string }> = {
|
|
14
|
-
object: { glyph: "{}", cls: "t-struct" },
|
|
15
|
-
array: { glyph: "[]", cls: "t-struct" },
|
|
16
|
-
string: { glyph: "“”", cls: "t-str" },
|
|
17
|
-
integer: { glyph: "#", cls: "t-num" },
|
|
18
|
-
number: { glyph: "½", cls: "t-num" },
|
|
19
|
-
boolean: { glyph: "◧", cls: "t-bool" },
|
|
20
|
-
null: { glyph: "∅", cls: "t-null" },
|
|
21
|
-
binary: { glyph: "0110", cls: "t-bin binsq" }, // bits in a little square
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
// Exact-match formats → an icon.
|
|
25
|
-
const FORMAT: Record<string, string> = {
|
|
26
|
-
"date-time": "🕑",
|
|
27
|
-
date: "📅",
|
|
28
|
-
time: "🕑",
|
|
29
|
-
duration: "⏳",
|
|
30
|
-
email: "✉️",
|
|
31
|
-
"idn-email": "✉️",
|
|
32
|
-
hostname: "🖥️",
|
|
33
|
-
"idn-hostname": "🖥️",
|
|
34
|
-
ipv4: "🌐",
|
|
35
|
-
ipv6: "🌐",
|
|
36
|
-
uri: "🔗",
|
|
37
|
-
iri: "🔗",
|
|
38
|
-
"uri-reference": "🔗",
|
|
39
|
-
"iri-reference": "🔗",
|
|
40
|
-
"uri-template": "🔗",
|
|
41
|
-
url: "🔗",
|
|
42
|
-
uuid: "🆔",
|
|
43
|
-
regex: "🔣",
|
|
44
|
-
"json-pointer": "📍",
|
|
45
|
-
"relative-json-pointer": "📍",
|
|
46
|
-
password: "🔑",
|
|
47
|
-
color: "🎨",
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
// Media-type / binary-encoding / custom formats → an icon, chosen by prefix.
|
|
51
|
-
function mediaIcon(format: string): string | null {
|
|
52
|
-
if (format === "x-yamlover-chapter") return "§"; // a chapter — the section sign
|
|
53
|
-
if (format === "x-yamlover-tag") return "🏷️";
|
|
54
|
-
if (format.startsWith("x-yamlover-")) return "🧩"; // a custom yamlover renderer
|
|
55
|
-
if (format === "application/pdf") return "📕";
|
|
56
|
-
if (format === "application/x-fictionbook+xml") return "📘";
|
|
57
|
-
if (format === "application/epub+zip") return "📗";
|
|
58
|
-
if (format === "image/vnd.djvu") return "📓";
|
|
59
|
-
if (format.startsWith("image/")) return "🖼️";
|
|
60
|
-
if (format === "text/markdown") return "📝";
|
|
61
|
-
if (format === "text/asciidoc") return "📃";
|
|
62
|
-
if (format === "text/csv" || format === "text/tab-separated-values") return "▦"; // a table
|
|
63
|
-
if (format === "text/x-plantuml") return "📊"; // source that compiles to a diagram
|
|
64
|
-
if (format === "application/vnd.ms-excel") return "▦"; // legacy .xls workbook
|
|
65
|
-
if (format === "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") return "▦"; // .xlsx
|
|
66
|
-
if (format === "application/rtf") return "📄";
|
|
67
|
-
if (format === "application/msword") return "📄"; // legacy .doc
|
|
68
|
-
if (format === "application/vnd.openxmlformats-officedocument.wordprocessingml.document") return "📄"; // .docx
|
|
69
|
-
if (format === "application/vnd.google-earth.kml+xml" || format === "application/vnd.google-earth.kmz") return "🗺️"; // map overlay
|
|
70
|
-
if (format.startsWith("text/")) return "📄";
|
|
71
|
-
if (format.startsWith("audio/")) return "🔊";
|
|
72
|
-
if (format.startsWith("video/")) return "🎬";
|
|
73
|
-
if (/^(u?int|float)\d/.test(format)) return "💾"; // int32/le, float64, …
|
|
74
|
-
return null;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/** The type/format icon for a node — `format` wins, then a directory concrete
|
|
78
|
-
* (`dir`/`yamlover`) shows a folder, else `type`. */
|
|
79
|
-
export function typeIcon(type: string, format: string | null, concrete?: string | null): Glyph {
|
|
80
|
-
if (format) {
|
|
81
|
-
const g = FORMAT[format] ?? mediaIcon(format);
|
|
82
|
-
if (g) return { glyph: g, cls: "t-fmt", title: format };
|
|
83
|
-
}
|
|
84
|
-
// a plain directory (no `.yamlover/`) — a real OS folder
|
|
85
|
-
if (concrete === "dir") return { glyph: "📁", cls: "t-struct", title: "folder" };
|
|
86
|
-
// a yamlover entity stored as a directory (a folder with a `.yamlover/` marker)
|
|
87
|
-
if (concrete === "yamlover") return { glyph: "🗂️", cls: "t-struct", title: "yamlover folder" };
|
|
88
|
-
const t = TYPE[type];
|
|
89
|
-
if (t) return { glyph: t.glyph, cls: t.cls, title: type };
|
|
90
|
-
return { glyph: "•", cls: "t-bin", title: type || "unknown" };
|
|
91
|
-
}
|
package/src/client/links.tsx
DELETED
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
import { ReactNode } from "react";
|
|
2
|
-
import { segsToStr, strToSegs } from "./paths";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* The shared **link** concept: one place that decides what a link *target* means
|
|
6
|
-
* and how a link is made clickable. Every renderer that emits links routes through
|
|
7
|
-
* here, so link behaviour is defined once.
|
|
8
|
-
*
|
|
9
|
-
* A target is addressed in the app's JSON instance space — the same space the
|
|
10
|
-
* tree, breadcrumbs, and the URL all navigate — with two anchors, mirroring how an
|
|
11
|
-
* `x-yamlover` `rel` pointer is written:
|
|
12
|
-
*
|
|
13
|
-
* - **`/some/path`** — relative to the *document* the link appears in (the
|
|
14
|
-
* nearest yamlover entity; "document" meaning the literal file/entity, overlays
|
|
15
|
-
* and all). Resolved against the `documentPath` the server reports for the node.
|
|
16
|
-
* - **`//some/path`** — relative to the *project root* (the location given at
|
|
17
|
-
* yamlover startup), i.e. the served root → browser path `/some/path`.
|
|
18
|
-
* - **`scheme://…` / `mailto:…`** — an ordinary external link.
|
|
19
|
-
*
|
|
20
|
-
* `resolveLink` is deliberately the single seam for interpretation: it is where
|
|
21
|
-
* refs and rels are expected to plug in later (gaining the full pointer grammar —
|
|
22
|
-
* `..`, `^name`, virtual children), rather than each renderer re-deciding what a
|
|
23
|
-
* target points at. (Refs/rels keep their own server-side interpretation for now;
|
|
24
|
-
* this powers marklower links.)
|
|
25
|
-
*/
|
|
26
|
-
|
|
27
|
-
/** A link's resolved destination. Exactly one of `path` (an in-app JSON-space path
|
|
28
|
-
* for SPA navigation) or `href` (an external URL) is set; both null means the
|
|
29
|
-
* target did not resolve and the link renders as plain text. */
|
|
30
|
-
export interface ResolvedLink {
|
|
31
|
-
path: string | null;
|
|
32
|
-
href: string | null;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const UNRESOLVED: ResolvedLink = { path: null, href: null };
|
|
36
|
-
|
|
37
|
-
/** True for an external target carrying a URI scheme (`http:`, `https:`, `mailto:`,
|
|
38
|
-
* …). A `//`-rooted project path is *not* a scheme (no leading `scheme:`). */
|
|
39
|
-
const hasScheme = (s: string) => /^[a-z][a-z0-9+.-]*:/i.test(s);
|
|
40
|
-
|
|
41
|
-
/** Join a document base path with a document-relative path (both JSON-space),
|
|
42
|
-
* canonicalizing the result. */
|
|
43
|
-
function joinDoc(documentPath: string, rel: string): string {
|
|
44
|
-
return segsToStr([...strToSegs(documentPath), ...strToSegs(rel)]);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/** Tokenize a LEGACY slash-spelled link target (`/a/b[0]`) into segments. */
|
|
48
|
-
function slashSegs(str: string): (string | number)[] {
|
|
49
|
-
const out: (string | number)[] = [];
|
|
50
|
-
for (const tok of str.match(/\[\d+\]|[^/\[\]]+/g) || []) {
|
|
51
|
-
out.push(/^\[\d+\]$/.test(tok) ? Number(tok.slice(1, -1)) : tok);
|
|
52
|
-
}
|
|
53
|
-
return out;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/** Interpret a link `target` against the `documentPath` it appears in (the JSON-space
|
|
57
|
-
* path of its document; defaults to root). Colon spellings (`:a:b`, `::a:b` —
|
|
58
|
-
* SEPARATOR.md) are canonical; legacy slash spellings (`/a/b`, `//a/b`) still parse. */
|
|
59
|
-
export function resolveLink(target: string, documentPath = ":"): ResolvedLink {
|
|
60
|
-
const raw = target.trim();
|
|
61
|
-
if (!raw) return UNRESOLVED;
|
|
62
|
-
if (raw.startsWith("::")) return { path: segsToStr(strToSegs(raw.slice(2))), href: null }; // project root
|
|
63
|
-
if (raw.startsWith(":")) return { path: joinDoc(documentPath, raw), href: null }; // document-relative
|
|
64
|
-
if (raw.startsWith("//")) return { path: segsToStr(slashSegs(raw)), href: null }; // legacy project root
|
|
65
|
-
if (hasScheme(raw)) return { path: null, href: raw }; // external (http(s)/mailto/…)
|
|
66
|
-
if (raw.startsWith("/")) return { path: segsToStr([...strToSegs(documentPath), ...slashSegs(raw)]), href: null }; // legacy doc-relative
|
|
67
|
-
return UNRESOLVED; // anything else is not (yet) a recognized link target
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/** Render a link as the right kind of anchor: an in-app `.descend` link that calls
|
|
71
|
-
* `onNavigate` for an internal target, an ordinary external `.extlink` for a URL,
|
|
72
|
-
* or plain children when the target doesn't resolve. The single place a link
|
|
73
|
-
* becomes clickable — shared by every renderer that emits links. */
|
|
74
|
-
export function NavLink({
|
|
75
|
-
target,
|
|
76
|
-
documentPath,
|
|
77
|
-
onNavigate,
|
|
78
|
-
children,
|
|
79
|
-
}: {
|
|
80
|
-
target: string;
|
|
81
|
-
documentPath?: string;
|
|
82
|
-
onNavigate: (path: string) => void;
|
|
83
|
-
children: ReactNode;
|
|
84
|
-
}) {
|
|
85
|
-
const { path, href } = resolveLink(target, documentPath);
|
|
86
|
-
if (href) {
|
|
87
|
-
return (
|
|
88
|
-
<a className="extlink" href={href} target="_blank" rel="noopener noreferrer">
|
|
89
|
-
{children}
|
|
90
|
-
</a>
|
|
91
|
-
);
|
|
92
|
-
}
|
|
93
|
-
if (path) {
|
|
94
|
-
return (
|
|
95
|
-
<a
|
|
96
|
-
className="descend"
|
|
97
|
-
href={path}
|
|
98
|
-
onClick={(e) => {
|
|
99
|
-
e.preventDefault();
|
|
100
|
-
onNavigate(path);
|
|
101
|
-
}}
|
|
102
|
-
>
|
|
103
|
-
{children}
|
|
104
|
-
</a>
|
|
105
|
-
);
|
|
106
|
-
}
|
|
107
|
-
return <>{children}</>;
|
|
108
|
-
}
|
package/src/client/live.ts
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
// live.ts — the client side of the UNIFIED change flow. Every change to the served tree —
|
|
2
|
-
// mediated writes (annotate, tag, paste, mv) and external edits (the FS watcher) — reaches the
|
|
3
|
-
// client as ONE currency: a file-level IndexDiff over /api/events, which App re-broadcasts as a
|
|
4
|
-
// `yamlover:diff` window event. Hooks that hold server-derived state subscribe HERE instead of
|
|
5
|
-
// inventing per-feature push paths; a new surface gets live refresh by adding one useDiffBump.
|
|
6
|
-
|
|
7
|
-
import { useEffect, useState } from "react";
|
|
8
|
-
|
|
9
|
-
/** A reindex/write diff as App re-broadcasts it: client JSON paths of the touched FILES
|
|
10
|
-
* (added + changed + removed + both ends of moves), with the removals also listed alone. */
|
|
11
|
-
export interface DiffDetail {
|
|
12
|
-
paths: string[];
|
|
13
|
-
removed: string[];
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export const DIFF_EVENT = "yamlover:diff";
|
|
17
|
-
|
|
18
|
-
/** Re-broadcast a server diff to the window (App's SSE handler is the only caller). */
|
|
19
|
-
export function broadcastDiff(detail: DiffDetail): void {
|
|
20
|
-
window.dispatchEvent(new CustomEvent(DIFF_EVENT, { detail }));
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/** Diffs that can affect graph-derived state (annotations, tags, document bodies) — any touched
|
|
24
|
-
* `.yamlover` source. A binary-only diff (a photo import, say) passes nothing here. */
|
|
25
|
-
export const touchesYamlover = (d: DiffDetail): boolean => d.paths.some((p) => p.endsWith(".yamlover"));
|
|
26
|
-
|
|
27
|
-
/** A counter that bumps whenever a diff matches `match` (default: every diff) — put it in a
|
|
28
|
-
* fetch effect's dependency list and the data refetches on relevant changes. `match` must be
|
|
29
|
-
* a stable (module-level) predicate; it is deliberately not a re-subscribe dependency. */
|
|
30
|
-
export function useDiffBump(match: (d: DiffDetail) => boolean = () => true): number {
|
|
31
|
-
const [bump, setBump] = useState(0);
|
|
32
|
-
useEffect(() => {
|
|
33
|
-
const on = (e: Event) => {
|
|
34
|
-
const det = (e as CustomEvent).detail as DiffDetail | undefined;
|
|
35
|
-
if (det && match(det)) setBump((b) => b + 1);
|
|
36
|
-
};
|
|
37
|
-
window.addEventListener(DIFF_EVENT, on);
|
|
38
|
-
return () => window.removeEventListener(DIFF_EVENT, on);
|
|
39
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
40
|
-
}, []);
|
|
41
|
-
return bump;
|
|
42
|
-
}
|
package/src/client/main.tsx
DELETED