yamlover 0.3.10 → 0.3.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +186 -143
- package/bin/yamlover.js +51 -2
- package/dist/agent-docs/AGENTS.md +267 -0
- package/dist/agent-docs/CLAUDE.md +7 -0
- package/dist/builtin-taxonomy/$defs/.yamlover/meta.yamlover +25 -0
- package/dist/builtin-taxonomy/$defs/annotation +18 -0
- package/dist/builtin-taxonomy/$defs/board +15 -0
- package/dist/builtin-taxonomy/$defs/chapter +19 -0
- package/dist/builtin-taxonomy/$defs/chunk +6 -0
- package/dist/builtin-taxonomy/$defs/config +20 -0
- package/dist/builtin-taxonomy/$defs/fragment +44 -0
- package/dist/builtin-taxonomy/$defs/tag +17 -0
- package/dist/builtin-taxonomy/$defs/task +33 -0
- package/dist/builtin-taxonomy/$defs/workflow +18 -0
- package/dist/builtin-taxonomy/tags/.yamlover/body.yamlover +46 -0
- package/dist/client/assets/{decoded-M-9MjrR3.js → decoded-DVR13gJ5.js} +1 -1
- package/dist/client/assets/{djvu-M6ksPMNz.js → djvu-CZbDJKwH.js} +1 -1
- package/dist/client/assets/{docx-D4h81KWC.js → docx-BWH54ddd.js} +1 -1
- package/dist/client/assets/{heic-BoSpW_tk.js → heic-CV3oZ6BF.js} +1 -1
- package/dist/client/assets/{imagemap-CIJFPvXv.js → imagemap-DRJrF_aD.js} +1 -1
- package/dist/client/assets/index-CCUPHCB4.js +620 -0
- package/dist/client/assets/{index-DHqicFJy.css → index-D5xvCkZE.css} +1 -1
- package/dist/client/assets/{map-MATRMboW.js → map-Y_zmEjLV.js} +1 -1
- package/dist/client/assets/{paged-BQLUFBor.js → paged-DKLVDozm.js} +1 -1
- package/dist/client/assets/{panzoom-Do4jskMZ.js → panzoom-NbtFbptF.js} +1 -1
- package/dist/client/assets/{pdf-BShpl2D5.js → pdf-DM1MQkYa.js} +1 -1
- package/dist/client/assets/{psd-DIxfuhrV.js → psd-PXeZB_nC.js} +1 -1
- package/dist/client/assets/{spreadsheet-DL0NoDcm.js → spreadsheet-B-3t2XCi.js} +1 -1
- package/dist/client/assets/{tiff-C7zPNKoD.js → tiff-BQq-nYyI.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/server.js +545 -339
- package/package.json +1 -1
- package/dist/client/assets/index-DZ1FuhId.js +0 -620
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
# Working with this yamlover project (guide for AI agents)
|
|
2
|
+
|
|
3
|
+
This project stores structured knowledge as **yamlover** files. You (an AI coding agent) and a
|
|
4
|
+
human are editing the **same directory** at the same time: the human browses the tree in the
|
|
5
|
+
yamlover web UI (`npx yamlover .`) while you read and edit the files on disk. This document
|
|
6
|
+
tells you how to manipulate these files **correctly** so you don't corrupt the data or break the
|
|
7
|
+
human's live view.
|
|
8
|
+
|
|
9
|
+
> This file is self-contained. You do **not** need any other spec to follow it. If the project
|
|
10
|
+
> also contains files like `YAMLOVER.md`, `SEPARATOR.md`, `QUERY.md`, or `ANNOTATIONS.md`, those
|
|
11
|
+
> are the authoritative, deeper specs — consult them for edge cases.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 1. What yamlover is
|
|
16
|
+
|
|
17
|
+
yamlover is a **strict superset of YAML**. Every valid YAML document is valid yamlover. On top
|
|
18
|
+
of YAML it adds a small **pointer layer** so that data forms a graph, not just a tree:
|
|
19
|
+
|
|
20
|
+
- **`*` pointers** — a value that *refers to* another node (a shared edge, not a copy).
|
|
21
|
+
- **`&` path anchors** — declare that "this node also lives over there" (the push side of `*`).
|
|
22
|
+
- **`!!` tags** — type/schema markers, including inline schema references `!!<…>`.
|
|
23
|
+
|
|
24
|
+
There is a sibling brace surface called **json5p** (`.json5p` files) — the same pointer layer
|
|
25
|
+
expressed in JSON5 syntax. Most projects use `.yamlover`; treat `.json5p` as the JSON-flavored
|
|
26
|
+
twin (pointers are written as quoted strings, e.g. `*": pets[1]"`).
|
|
27
|
+
|
|
28
|
+
**Important:** because `*` and `&` mean something different than in stock YAML (a `*` is a path
|
|
29
|
+
pointer, **not** a YAML alias), these files require the yamlover parser. Do not "fix" them with
|
|
30
|
+
a generic YAML formatter — you will destroy the pointers and anchors.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## 2. The one-ordered-container model
|
|
35
|
+
|
|
36
|
+
Plain YAML forces a node to be **either** a sequence (all `- item`) **or** a mapping (all
|
|
37
|
+
`key: value`). yamlover unifies them: there is **one ordered container**. Every entry has an
|
|
38
|
+
integer **position** (`[0]`, `[1]`, …) and **may also** carry a string key. Keyless (positional)
|
|
39
|
+
and keyed entries coexist in one node — this is the default ("omni"):
|
|
40
|
+
|
|
41
|
+
```yamlover
|
|
42
|
+
playlist:
|
|
43
|
+
- Intro # [0] keyless / positional
|
|
44
|
+
- Verse # [1] keyless
|
|
45
|
+
title: Greatest Hits # [2], key=title keyed — AND still positioned
|
|
46
|
+
- Chorus # [3] keyless
|
|
47
|
+
encore: *: pets[0] # [4], key=encore a keyed pointer, still in order
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
A node can even carry a **scalar value AND fields at once**:
|
|
51
|
+
|
|
52
|
+
```yamlover
|
|
53
|
+
rating: 5 # the node's own scalar value …
|
|
54
|
+
- solid # [0] positional field
|
|
55
|
+
scale: 10 # [2] keyed field
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
(You may see optional `!!mix` / `!!var` tags marking these shapes. They are **no-op readability
|
|
59
|
+
markers** — mixing and scalar-plus-fields are the default. Don't add or remove them to change
|
|
60
|
+
meaning; they don't carry any.)
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## 3. Paths use COLONS, not slashes
|
|
65
|
+
|
|
66
|
+
A path is `key: value` chained. The separator is the **colon**: `a: b: c` means "key a → key b →
|
|
67
|
+
key c". Canonical style writes `: ` (colon + space) after each step; a colon without the space
|
|
68
|
+
also parses.
|
|
69
|
+
|
|
70
|
+
`/` is an **ordinary character** now — MIME-type keys (`text/html`), date keys (`01/02/2026`)
|
|
71
|
+
and URL-ish keys ride bare in a path. (Legacy files may still use `/` as a separator during a
|
|
72
|
+
migration window; **new content you author should use `:`**.)
|
|
73
|
+
|
|
74
|
+
### The scope ladder — more colons, wider scope
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
current: object: path # bare — current scope (siblings)
|
|
78
|
+
: document: rooted: path # : — this document's root
|
|
79
|
+
:: project: rooted: path # :: — this project's root
|
|
80
|
+
::: yamlover.inthemoon.net: $defs: tag # ::: — the world (an external project)
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
- `*pets[1]` — bare: a **sibling** named `pets`, position 1.
|
|
84
|
+
- `*: pets[0]` — `:` document root.
|
|
85
|
+
- `*:: tags: genre` — `::` this project's root.
|
|
86
|
+
- `*::: host.example: $defs: tag` — `:::` a world/external reference.
|
|
87
|
+
|
|
88
|
+
`[n]` addresses by **position** (integer key); a bare word addresses by **string key**. They
|
|
89
|
+
chain: `*: pets[1]: name` = root → position 1 → key `name`.
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## 4. Pointers `*` (the pull side)
|
|
94
|
+
|
|
95
|
+
A `*` value dereferences a path to another node and creates a **shared edge** (not a copy):
|
|
96
|
+
|
|
97
|
+
```yamlover
|
|
98
|
+
humans:
|
|
99
|
+
- name: Alice
|
|
100
|
+
manager: *: pets[1] # Alice.manager IS the node at root → pets → position 1
|
|
101
|
+
feline: *pets[1] # bare → a sibling
|
|
102
|
+
topDog: *: pets[0] # : → document root
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Pointers are **lazy** and **cycle-safe** — pointing two nodes at each other is fine. Editing the
|
|
106
|
+
target changes everything that points at it.
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## 5. Path anchors `&` (the push side)
|
|
111
|
+
|
|
112
|
+
`&: path` on a node declares "this node **also lives** at that path" — it grafts a **real key**
|
|
113
|
+
(there is no separate anchor namespace), so any plain pointer can then reach it:
|
|
114
|
+
|
|
115
|
+
```yamlover
|
|
116
|
+
boss: &: chief # this node is ALSO reachable as the document-root key `chief`
|
|
117
|
+
name: Rex
|
|
118
|
+
team:
|
|
119
|
+
lead: *: chief # same node as `boss` — a shared edge, not a copy
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Reverse relations are spelled as anchors. `&: parent: child` means "parent holds me as child":
|
|
123
|
+
|
|
124
|
+
```yamlover
|
|
125
|
+
adam:
|
|
126
|
+
cain:
|
|
127
|
+
&: eve: cain # "eve holds me as cain" — the reverse of eve's cain-edge
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
`&: container[]` (trailing `[]`, no index) means **positional membership**: "that container also
|
|
131
|
+
holds me", appended after the container's own entries:
|
|
132
|
+
|
|
133
|
+
```yamlover
|
|
134
|
+
fan:
|
|
135
|
+
name: Bob
|
|
136
|
+
&: favorites[] # Bob appends himself to `favorites`
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Anchor paths must be **unambiguous** (no wildcards, no trailing `[n]` position claim) — they
|
|
140
|
+
create real keys, so they must resolve to exactly one place.
|
|
141
|
+
|
|
142
|
+
> You may encounter the older `~key: *path` back-edge syntax in legacy files. It still parses but
|
|
143
|
+
> is deprecated; author new reverse edges as `&` anchors.
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## 6. Tags `!!` and `$defs` schemas
|
|
148
|
+
|
|
149
|
+
- `!!type` — a YAML-style tag. Common no-op markers: `!!mix`, `!!var` (see §2). `!!set` marks a
|
|
150
|
+
container whose membership is by identity (duplicates collapse).
|
|
151
|
+
- **Inline schema reference** `!!<…>` binds a node to a reusable schema definition:
|
|
152
|
+
```yamlover
|
|
153
|
+
mychapter: !!<*:: yamlover: $defs: chapter>
|
|
154
|
+
```
|
|
155
|
+
Reusable schemas live at the project root under **`$defs`** (e.g. `$defs: chapter`,
|
|
156
|
+
`$defs: tag`, `$defs: annotation`, `$defs: fragment`). They are referenced project-scoped
|
|
157
|
+
(`*:: $defs: name`) or via the self-import (`*:: yamlover: $defs: name` — synonyms inside this
|
|
158
|
+
project). Schemas are **metadata** (typing/format/presentation), not data storage.
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## 7. Concretes: how a node is stored on disk
|
|
163
|
+
|
|
164
|
+
A node can be materialized two ways:
|
|
165
|
+
|
|
166
|
+
1. **Single-file concrete** — a whole document in one `.yamlover` (or `.json5p`) file.
|
|
167
|
+
2. **Directory concrete** — a directory **is** the node; its files/subdirs are its entries, and
|
|
168
|
+
two optional overlay files inside a hidden `.yamlover/` subdir add data and schema:
|
|
169
|
+
- `.yamlover/body.yamlover` — **instance** overlay: scalar values, ordering, pointers,
|
|
170
|
+
extra keyed/keyless entries layered onto the directory's contents.
|
|
171
|
+
- `.yamlover/meta.yamlover` — **schema** overlay: typing, format, validation.
|
|
172
|
+
|
|
173
|
+
So to add a pointer or a value "to a folder", you edit (or create) that folder's
|
|
174
|
+
`.yamlover/body.yamlover`. Plain files inside the directory are its members; a `.yamlover/`
|
|
175
|
+
subdir does not appear as a member — it's the overlay.
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
## 8. Annotations & fragments (tags applied to content)
|
|
180
|
+
|
|
181
|
+
The human marks up documents in the UI. These live **on the target node**, not in side files:
|
|
182
|
+
|
|
183
|
+
- **`yamlover-fragments`** — a mapping of slug → selector (a text span, image/PDF rectangle, or
|
|
184
|
+
map box) identifying a region within the node.
|
|
185
|
+
- **`yamlover-annotations`** — a sequence; each element applies a tag to the node (or a
|
|
186
|
+
fragment). An element is either a bare **tag pointer** or an object with a `tag:` field plus
|
|
187
|
+
parameters:
|
|
188
|
+
```yamlover
|
|
189
|
+
yamlover-annotations:
|
|
190
|
+
- *:: tags: genre: brevity # parameterless
|
|
191
|
+
- {description: A math block, tag: *:: tags: topic: math} # parametrized
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Prefer letting the human create these through the UI. If you must touch them by hand, keep the
|
|
195
|
+
exact key names (`yamlover-fragments`, `yamlover-annotations`) and the tag-pointer form, and do
|
|
196
|
+
not renumber or reorder fragment slugs.
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## 9. Escaping (critical when editing keys)
|
|
201
|
+
|
|
202
|
+
A literal metacharacter inside a key is **backslash-escaped** (this is per-character, not
|
|
203
|
+
quote-based):
|
|
204
|
+
|
|
205
|
+
```yamlover
|
|
206
|
+
weird:
|
|
207
|
+
cat\:dog: 1 # the literal key "cat:dog" (\: suppresses the separator)
|
|
208
|
+
cat/dog: 2 # `/` is ordinary now — no escape needed
|
|
209
|
+
ref: *weird: cat\:dog # the second portion is the literal key "cat:dog"
|
|
210
|
+
dots: *\.\. # the literal key ".." (NOT the parent scope)
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
- Metachars that need escaping in a key include: `:` `[` `]` `*` `&` `~` `#` `\` `?` `!`
|
|
214
|
+
`(` `)` `<` `>` `=` `|`.
|
|
215
|
+
- The literal keys `..` and `...` are written `\.\.` and `\.\.\.` (bare `..` means "parent").
|
|
216
|
+
- **A key containing a space MUST be quoted**: `: tags: 'дорожный знак'`. Use the host surface's
|
|
217
|
+
string quoting (`'…'` / `"…"`).
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## 10. Co-editing workflow & safety rules
|
|
222
|
+
|
|
223
|
+
The yamlover server **watches the filesystem**. Every time you save a file, it reindexes and
|
|
224
|
+
pushes the change to the human's browser over a live event stream — so your edits appear in their
|
|
225
|
+
UI within a moment, and theirs appear to you on disk. Work with that, not against it:
|
|
226
|
+
|
|
227
|
+
- **Make small, valid saves.** A half-written file will reindex as broken. Prefer complete edits.
|
|
228
|
+
- **NEVER touch `.yamlover/index.db`** (nor its `-wal` / `-shm` companions). It is the server's
|
|
229
|
+
generated SQLite index — it regenerates itself from the source files. Editing or deleting it
|
|
230
|
+
does nothing useful and can confuse a running server. It is the *only* thing in `.yamlover/`
|
|
231
|
+
you must not edit; `body.yamlover` / `meta.yamlover` overlays (§7) are normal editable data.
|
|
232
|
+
- **Renames and moves break inbound pointers.** Other files may point at a node by its path
|
|
233
|
+
(`*: some: node`). If you move or rename it with a plain `mv`, those pointers dangle. The
|
|
234
|
+
running server exposes a **mediated move** (`POST /api/mv`) that surgically rewrites inbound
|
|
235
|
+
references at their source. Prefer it for moves/renames; if you must move by hand, search the
|
|
236
|
+
project for pointers to the old path (`*` followed by the path) and update them too.
|
|
237
|
+
- **Don't reformat with a generic YAML/JSON tool.** It will mangle `*`, `&`, `!!<…>`, the
|
|
238
|
+
colon paths, and the mixed keyed/keyless ordering. Edit the text directly and preserve style.
|
|
239
|
+
- **Settings live in `.yamlover/settings.yamlover`** at the project root (e.g. where new tags and
|
|
240
|
+
annotations are written). Treat it as configuration; change it only when asked.
|
|
241
|
+
- **When unsure of a path, query it.** The server answers `GET /api/query?...` using the path
|
|
242
|
+
grammar above, and serves the tree at `GET /api/tree` / a node at `GET /api/json?path=:a:b`.
|
|
243
|
+
Use these to confirm a path resolves before you author a pointer to it.
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## 11. Quick reference
|
|
248
|
+
|
|
249
|
+
```yamlover
|
|
250
|
+
# pointers (pull) — colon paths, the scope ladder
|
|
251
|
+
sibling: *pets[1] # current scope, by position
|
|
252
|
+
rooted: *: humans[0]: name # document root → position 0 → key name
|
|
253
|
+
projscope: *:: tags: genre # this project's root
|
|
254
|
+
world: *::: host.example: $defs: tag
|
|
255
|
+
|
|
256
|
+
# anchors (push) — "I also live there"; real keys; unambiguous only
|
|
257
|
+
here: &: chief # also at document-root key `chief`
|
|
258
|
+
rev: &: parent: child # parent holds me as `child`
|
|
259
|
+
mem: &: favorites[] # appended member of `favorites`
|
|
260
|
+
|
|
261
|
+
# tags / schema
|
|
262
|
+
node: !!<*:: $defs: chapter> # bind a reusable schema
|
|
263
|
+
|
|
264
|
+
# escaping
|
|
265
|
+
lit: *weird: cat\:dog # literal colon in a key
|
|
266
|
+
space: : tags: 'two words' # spacey key must be quoted
|
|
267
|
+
```
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Project guidance for AI agents
|
|
2
|
+
|
|
3
|
+
This is a **yamlover** project. The authoring rules, the pointer/anchor syntax, and the rules for
|
|
4
|
+
safely co-editing the tree while the yamlover web UI is running are in **`AGENTS.md`** — read it
|
|
5
|
+
before editing any `.yamlover` / `.json5p` file.
|
|
6
|
+
|
|
7
|
+
@AGENTS.md
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
properties:
|
|
2
|
+
chapter:
|
|
3
|
+
type: string
|
|
4
|
+
format: yamlover/meta
|
|
5
|
+
chunk:
|
|
6
|
+
type: string
|
|
7
|
+
format: yamlover/meta
|
|
8
|
+
tag:
|
|
9
|
+
type: string
|
|
10
|
+
format: yamlover/meta
|
|
11
|
+
annotation:
|
|
12
|
+
type: string
|
|
13
|
+
format: yamlover/meta
|
|
14
|
+
fragment:
|
|
15
|
+
type: string
|
|
16
|
+
format: yamlover/meta
|
|
17
|
+
task:
|
|
18
|
+
type: string
|
|
19
|
+
format: yamlover/meta
|
|
20
|
+
workflow:
|
|
21
|
+
type: string
|
|
22
|
+
format: yamlover/meta
|
|
23
|
+
board:
|
|
24
|
+
type: string
|
|
25
|
+
format: yamlover/meta
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# $defs/annotation — ONE TAG APPLICATION on the host node (a whole document, or a fragment within
|
|
2
|
+
# it). It lives as an element of the host's `yamlover-annotations` array. Two forms:
|
|
3
|
+
# • parameterless — a bare tag pointer (`- *::tags:…`): the host simply HAS this tag.
|
|
4
|
+
# • parametrized — an object carrying `tag: *::tags:…` plus parameters (e.g. `description`).
|
|
5
|
+
# The applied tag drives the display color (its `color`, else a name-derived hue). The region, if
|
|
6
|
+
# any, is the host fragment's selector — an annotation carries no selector of its own. This
|
|
7
|
+
# replaces the old separate-file annotation (a `target` pointer + `selector` + reverse tag
|
|
8
|
+
# membership). The tag pointer IS the data (a forward ref edge into the tag); the schema tag
|
|
9
|
+
# !!<*::yamlover/$defs/annotation> is optional, for the parametrized object form.
|
|
10
|
+
type: variant
|
|
11
|
+
format: x-yamlover-annotation
|
|
12
|
+
value: *:: yamlover: $defs: tag # the parameterless case: the self-value IS a tag (by ref)
|
|
13
|
+
properties:
|
|
14
|
+
tag: *:: yamlover: $defs: tag # the parametrized case: the applied tag, by pointer
|
|
15
|
+
description:
|
|
16
|
+
type: string
|
|
17
|
+
format: text/marklower
|
|
18
|
+
additionalProperties: true # any other field is a free-form parameter
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# $defs/board — marks a DIRECTORY of tasks as an agile/kanban board. Attach it to the directory's
|
|
2
|
+
# `.yamlover/body.yamlover` overlay; the `workflow:` ref names which $defs/workflow supplies the
|
|
3
|
+
# columns (its states, in order). The board renderer then groups the directory's task members into
|
|
4
|
+
# those columns by each task's current state annotation, and a drag between columns rewrites that
|
|
5
|
+
# annotation (re-tags the state). format `x-yamlover-board`. Attach with !!<*yamlover:$defs:board>.
|
|
6
|
+
# Spec: ../TICKETS.md §3.
|
|
7
|
+
type: object
|
|
8
|
+
format: x-yamlover-board
|
|
9
|
+
properties:
|
|
10
|
+
workflow: *:: yamlover: $defs: tag # ref → the workflow that SEEDS the default lanes
|
|
11
|
+
columns: # optional saved lanes (the explorer board view's config):
|
|
12
|
+
type: array # a sequence of lanes, each a list of tag refs; multiple
|
|
13
|
+
items: # tags in a lane split it vertically. Edited via the lane
|
|
14
|
+
type: array # headers, persisted by POST /api/board.
|
|
15
|
+
items: *:: yamlover: $defs: tag
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# $defs/chapter — a document node: a title, a body of chunks, and recursive subchapters.
|
|
2
|
+
#
|
|
3
|
+
# This replaces the old split (title from the schema concrete, chunks from the instance):
|
|
4
|
+
# it is a normal schema with `title`, `chunks`, and `children` fields. Attach it to a
|
|
5
|
+
# yamlover node with the tag !!<*yamlover/$defs/chapter>.
|
|
6
|
+
type: object
|
|
7
|
+
properties:
|
|
8
|
+
title:
|
|
9
|
+
type: string
|
|
10
|
+
format: text/marklower
|
|
11
|
+
description: # optional subtitle / heading annotation
|
|
12
|
+
type: string
|
|
13
|
+
format: text/marklower
|
|
14
|
+
chunks: # the body — a sequence of chunks, read top to bottom
|
|
15
|
+
type: array
|
|
16
|
+
items: *:: yamlover: $defs: chunk
|
|
17
|
+
children: # subchapters — the recursion
|
|
18
|
+
type: array
|
|
19
|
+
items: *:: yamlover: $defs: chapter
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# $defs/config — the project configuration schema for `<root>/.yamlover/settings.yamlover`
|
|
2
|
+
# (settings.ts, IMPORTS.md). Settings are DEFAULTS, never constraints; reading is location-
|
|
3
|
+
# independent. The format `x-yamlover-config` lets the server render the (hidden but accessible)
|
|
4
|
+
# config file with the SETTINGS EDITOR — opened by the gear button, in the main pane. Locations are
|
|
5
|
+
# PROJECT-SCOPE pointers (`*:: name`) naming the object at the project root. Attach with
|
|
6
|
+
# !!<*yamlover:$defs:config>.
|
|
7
|
+
type: object
|
|
8
|
+
format: x-yamlover-config
|
|
9
|
+
properties:
|
|
10
|
+
uri: # this project's identity (IMPORTS.md §1) — an authority,
|
|
11
|
+
type: string # authored as `::: host`; identity, not transport
|
|
12
|
+
exports: # the paths exported to importers (IMPORTS.md §2):
|
|
13
|
+
type: array # a list of pointer/query texts
|
|
14
|
+
items:
|
|
15
|
+
type: string
|
|
16
|
+
annotations: *:: yamlover: $defs: tag # where new annotations are created (`*:: annotations`)
|
|
17
|
+
tags: *:: yamlover: $defs: tag # where new tags are created (`*:: tags`)
|
|
18
|
+
sidecars: # where derived sidecar blobs go: per-directory | project
|
|
19
|
+
type: string
|
|
20
|
+
annotation-tag: *:: yamlover: $defs: tag # the last-used annotation tag (the picker default)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# $defs/fragment — a user-marked REGION inside a document (the locate/highlight data only; no
|
|
2
|
+
# description or color). Stored under the host node's `yamlover-fragments` mapping, keyed by a
|
|
3
|
+
# slug. `type` selects the locator: a text quote {type:text, exact, prefix, suffix}, an image /
|
|
4
|
+
# pdf / djvu rect {type:rect|pdf|djvu, x,y,w,h (pdf/djvu add page)}, or a map box
|
|
5
|
+
# {type:map, n,s,e,w (geographic edges)}. Image-like fragments also carry an `image` — a PNG crop
|
|
6
|
+
# of the region (a sidecar blob, referenced by a `*` pointer). A fragment may itself be tagged via
|
|
7
|
+
# its own `yamlover-annotations`. Attach with the tag !!<*::yamlover/$defs/fragment>.
|
|
8
|
+
type: object
|
|
9
|
+
format: x-yamlover-fragment
|
|
10
|
+
properties:
|
|
11
|
+
type:
|
|
12
|
+
type: string
|
|
13
|
+
description: The locator kind — text | rect | pdf | djvu | map
|
|
14
|
+
exact:
|
|
15
|
+
type: string
|
|
16
|
+
prefix:
|
|
17
|
+
type: string
|
|
18
|
+
suffix:
|
|
19
|
+
type: string
|
|
20
|
+
page:
|
|
21
|
+
type: integer
|
|
22
|
+
x:
|
|
23
|
+
type: number
|
|
24
|
+
y:
|
|
25
|
+
type: number
|
|
26
|
+
w:
|
|
27
|
+
type: number # rect/pdf/djvu width — OR the map's west edge
|
|
28
|
+
h:
|
|
29
|
+
type: number
|
|
30
|
+
n:
|
|
31
|
+
type: number # map north edge
|
|
32
|
+
s:
|
|
33
|
+
type: number # map south edge
|
|
34
|
+
e:
|
|
35
|
+
type: number # map east edge
|
|
36
|
+
image:
|
|
37
|
+
type: binary
|
|
38
|
+
format: image/png
|
|
39
|
+
description: A PNG crop of the region (image-like fragments only); a sidecar blob by pointer
|
|
40
|
+
created:
|
|
41
|
+
type: string
|
|
42
|
+
yamlover-annotations:
|
|
43
|
+
type: array
|
|
44
|
+
items: *:: yamlover: $defs: annotation
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# $defs/tag — a node in a tag taxonomy. A tag's description is its BODY (the node's own scalar
|
|
2
|
+
# value — author `name: !!omni The description` when the tag also has fields, or a plain
|
|
3
|
+
# `name: The description` for a leaf); an optional explicit `color` ("#rrggbb" — a "pure color
|
|
4
|
+
# tag"; absent ⇒ the UI derives a stable hue from the name); every OTHER key is a sub-tag, so
|
|
5
|
+
# the taxonomy recurses to any depth (`additionalProperties` → tag). The format `x-yamlover-tag`
|
|
6
|
+
# makes the server render each one as a tag (a badge wired to its super/sub-tags). Attach to the
|
|
7
|
+
# taxonomy root with the tag !!<*yamlover/$defs/tag> — it propagates down.
|
|
8
|
+
type: variant
|
|
9
|
+
format: x-yamlover-tag
|
|
10
|
+
value:
|
|
11
|
+
type: string
|
|
12
|
+
format: text/marklower
|
|
13
|
+
properties:
|
|
14
|
+
color:
|
|
15
|
+
type: string
|
|
16
|
+
description: An explicit display color ("#rrggbb"); absent ⇒ derived from the tag's name
|
|
17
|
+
additionalProperties: *:: yamlover: $defs: tag
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# $defs/task — a unit of work (a ticket), or a study card. A task IS a chapter (it has a
|
|
2
|
+
# `title`, an optional `description`, a body of `chunks`, and recursive `children` — which here
|
|
3
|
+
# mean SUBTASKS, a task tree) PLUS a few OPTIONAL structured fields for planning and automation.
|
|
4
|
+
# It carries NO `state` field: a task's lifecycle position is a TAG APPLICATION
|
|
5
|
+
# (`yamlover-annotations`) into a workflow taxonomy ($defs/workflow), so the whole tag / board
|
|
6
|
+
# machinery is reused. Every planning field is optional — defaults-never-constraints: an
|
|
7
|
+
# untouched task is just a titled note. Attach with !!<*yamlover:$defs:task>. Spec: ../TICKETS.md.
|
|
8
|
+
type: object
|
|
9
|
+
properties:
|
|
10
|
+
title:
|
|
11
|
+
type: string
|
|
12
|
+
format: text/marklower
|
|
13
|
+
description: # optional subtitle / brief
|
|
14
|
+
type: string
|
|
15
|
+
format: text/marklower
|
|
16
|
+
chunks: # the body — a sequence of chunks, read top to bottom
|
|
17
|
+
type: array
|
|
18
|
+
items: *:: yamlover: $defs: chunk
|
|
19
|
+
children: # SUBTASKS — the task tree (the recursion)
|
|
20
|
+
type: array
|
|
21
|
+
items: *:: yamlover: $defs: task
|
|
22
|
+
priority: # free ordinal: low | normal | high | urgent (advisory)
|
|
23
|
+
type: string
|
|
24
|
+
due:
|
|
25
|
+
type: string
|
|
26
|
+
format: date-time
|
|
27
|
+
assignee: # a name, or a pointer to a person / agent node
|
|
28
|
+
type: string
|
|
29
|
+
depends: # "blocked-by" edges → other tasks (ref; the dependency DAG)
|
|
30
|
+
type: array
|
|
31
|
+
items: *:: yamlover: $defs: task
|
|
32
|
+
estimate: # free: "2h", "3pt"
|
|
33
|
+
type: string
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# $defs/workflow — a state machine, expressed as a TAG whose CONTAINED sub-tags are its STATES
|
|
2
|
+
# (each an ordinary $defs/tag: a description body, an optional `color`). Transitions are REF
|
|
3
|
+
# edges, not containment: a state's `next:` holds pointer(s) to its successor state(s) elsewhere
|
|
4
|
+
# in the SAME workflow — a cross-link in the or-graph, so a successor is NOT nested under its
|
|
5
|
+
# predecessor (the contain-vs-ref distinction; a pointer-valued entry is a ref member, never a
|
|
6
|
+
# contained sub-tag). The workflow's `initial:` ref points at the start state; a TERMINAL state
|
|
7
|
+
# is DERIVED (it has no outgoing `next`). Transitions are ADVISORY — the engine permits any state
|
|
8
|
+
# change; the board UI merely offers `next` states as the obvious moves. The format
|
|
9
|
+
# `x-yamlover-workflow` lets the board renderer recognize this node and order its columns. Attach
|
|
10
|
+
# with !!<*yamlover:$defs:workflow>. Spec: ../TICKETS.md §2.
|
|
11
|
+
type: variant
|
|
12
|
+
format: x-yamlover-workflow
|
|
13
|
+
value: # the workflow's description (its body)
|
|
14
|
+
type: string
|
|
15
|
+
format: text/marklower
|
|
16
|
+
properties:
|
|
17
|
+
initial: *:: yamlover: $defs: tag # ref → the start state
|
|
18
|
+
additionalProperties: *:: yamlover: $defs: tag # every other key is a STATE (a plain tag)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Tag taxonomy: the built-in PALETTE (colors — pure color tags, each with an explicit
|
|
2
|
+
# color) plus named tags created from the annotation picker (settings.yamlover:
|
|
3
|
+
# tags.location). The palette ships with the yamlover project (::: yamlover.inthemoon.net);
|
|
4
|
+
# the engine grafts {$defs, tags} as the `yamlover` self-import key into every served
|
|
5
|
+
# root, so *//yamlover/tags/colors/<name> resolves from any project. Spec: ../$defs/tag.
|
|
6
|
+
!!<*yamlover: $defs: tag>
|
|
7
|
+
!!var Tags
|
|
8
|
+
colors: Pure color tags — the annotation palette; each carries an explicit color
|
|
9
|
+
yellow:
|
|
10
|
+
color: "#f9e2af"
|
|
11
|
+
green:
|
|
12
|
+
color: "#a6e3a1"
|
|
13
|
+
sky:
|
|
14
|
+
color: "#89dceb"
|
|
15
|
+
mauve:
|
|
16
|
+
color: "#cba6f7"
|
|
17
|
+
pink:
|
|
18
|
+
color: "#f5c2e7"
|
|
19
|
+
peach:
|
|
20
|
+
color: "#fab387"
|
|
21
|
+
first tag: !!<*::yamlover:$defs:tag>
|
|
22
|
+
second tag: !!<*::yamlover:$defs:tag>
|
|
23
|
+
third tag: !!<*::yamlover:$defs:tag>
|
|
24
|
+
forth tag: !!<*::yamlover:$defs:tag>
|
|
25
|
+
fifth tag: !!<*::yamlover:$defs:tag>
|
|
26
|
+
# Workflows — state machines (TICKETS.md §2). Each `next:` is a REF edge to a successor STATE in
|
|
27
|
+
# the SAME workflow (a cross-link, not a contained sub-tag); a terminal state has no `next`.
|
|
28
|
+
workflow: Lifecycles — task/ticket state machines
|
|
29
|
+
dev: !!<*yamlover:$defs:workflow> Software task lifecycle
|
|
30
|
+
initial: *::yamlover:tags:workflow:dev:backlog
|
|
31
|
+
backlog: Captured, not yet refined
|
|
32
|
+
color: "#9399b2"
|
|
33
|
+
next: *::yamlover:tags:workflow:dev:ready
|
|
34
|
+
ready: Refined, ready to pick up
|
|
35
|
+
color: "#89b4fa"
|
|
36
|
+
next: *::yamlover:tags:workflow:dev:in-progress
|
|
37
|
+
in-progress: Being worked
|
|
38
|
+
color: "#fab387"
|
|
39
|
+
next:
|
|
40
|
+
- *::yamlover:tags:workflow:dev:done
|
|
41
|
+
- *::yamlover:tags:workflow:dev:cancelled
|
|
42
|
+
done: Completed
|
|
43
|
+
color: "#a6e3a1"
|
|
44
|
+
cancelled: Dropped
|
|
45
|
+
color: "#f38ba8"
|
|
46
|
+
верхушка: !!<*::yamlover:$defs:tag>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{r as f,b as d,j as i}from"./index-
|
|
1
|
+
import{r as f,b as d,j as i}from"./index-CCUPHCB4.js";import{PanZoomImage as p}from"./imagemap-DRJrF_aD.js";function j({node:r,label:t,decode:a}){const[e,n]=f.useState([]),[c,u]=f.useState(null);return f.useEffect(()=>{let s=!1;const o=[];return n([]),u(null),(async()=>{const l=await fetch(d(r.path)).then(m=>m.arrayBuffer()),g=await a(l);if(!s){for(const m of g)o.push(URL.createObjectURL(m));s||n(o)}})().catch(l=>!s&&u(String(l.message||l))),()=>{s=!0,o.forEach(URL.revokeObjectURL)}},[r.path]),c?i.jsxs("div",{className:"error",children:[t,": ",c]}):e.length?i.jsx(i.Fragment,{children:e.map((s,o)=>i.jsx(p,{src:s,className:"filemap fileimagemap"},o))}):i.jsxs("div",{className:"loading",children:["decoding ",t,"…"]})}async function E(r,t,a){const e=document.createElement("canvas");e.width=t,e.height=a;const n=e.getContext("2d");if(!n)throw new Error("no 2d context");const c=n.createImageData(t,a);return c.data.set(r),n.putImageData(c,0,0),h(e)}function h(r){return new Promise((t,a)=>r.toBlob(e=>e?t(e):a(new Error("canvas export failed")),"image/png"))}export{j as D,h as c,E as r};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{r as u,f as te,d as ne,e as oe,c as re,D as W,b as se,j as d}from"./index-
|
|
1
|
+
import{r as u,f as te,d as ne,e as oe,c as re,D as W,b as se,j as d}from"./index-CCUPHCB4.js";import{u as ae}from"./paged-DKLVDozm.js";const ce="/assets/djvu-Ci0lFKxE.js";let X=null;function ie(){return window.DjVu?Promise.resolve(window.DjVu):(X??=new Promise((s,c)=>{const i=document.createElement("script");i.src=ce,i.onload=()=>window.DjVu?s(window.DjVu):c(new Error("DjVu failed to load")),i.onerror=()=>c(new Error("could not load djvu.js")),document.head.appendChild(i)}),X)}let m=null,Y=null,_=null;const p=new Map,S=new Map,le=6,F=1500;async function ue(s,c){const i=await ie();return m??=new i.Worker,c!==Y&&(Y=c,p.clear(),S.clear(),_=(async()=>(await m.createDocument(s),Number(await m.doc.getPagesQuantity().run())||0))()),_}async function de(s,c){const i=Math.max(1,Math.round(s.height*c/s.width)),w=await createImageBitmap(s,{resizeWidth:c,resizeHeight:i,resizeQuality:"medium"}),g=document.createElement("canvas");g.width=c,g.height=i;const v=g.getContext("2d");return v.drawImage(w,0,0),w.close(),v.getImageData(0,0,c,i)}function he(s){const c=p.get(s);if(c)return p.delete(s),p.set(s,c),Promise.resolve(c);const i=S.get(s);if(i)return i;const w=(async()=>{const[g,v,x,P]=await m.run(m.doc.getPage(s).getImageData(),m.doc.getPage(s).getNormalizedTextZones(),m.doc.getPage(s).getWidth(),m.doc.getPage(s).getHeight()),M=Number(x)||g.width,N=Number(P)||g.height,y={image:M>F?await de(g,F):g,zones:Array.isArray(v)?v:[],w:M,h:N};for(S.delete(s),p.set(s,y);p.size>le;)p.delete(p.keys().next().value);return y})();return S.set(s,w),w}const C=s=>Number(s)||0;function ge({image:s}){const c=u.useRef(null);return u.useLayoutEffect(()=>{const i=c.current;i&&(i.width=s.width,i.height=s.height,i.getContext("2d")?.putImageData(s,0,0))},[s]),d.jsx("canvas",{className:"djvu-page",ref:c})}function ve({node:s}){const c=u.useRef(null),[i,w]=u.useState(0),[g,v]=u.useState(1),[x,P]=u.useState(0),[M,N]=u.useState(null),[f,y]=u.useState(null),[b,V]=u.useState(()=>new Set),[D,T]=u.useState(()=>new Map),A=u.useRef(new Map),z=te(s.path),{openCreate:H,openEdit:Q,palette:Z,preview:R}=ne(z),q=(R?[...z.annotations,{path:"(preview)",selector:R.selector,tag:R.tag}]:z.annotations).filter(t=>t.selector?.type==="djvu").map(t=>({page:C(t.selector.page)||1,x:C(t.selector.x),y:C(t.selector.y),w:C(t.selector.w),h:C(t.selector.h),title:t.description,color:re(t),ann:oe(t)?t:void 0})),O=R?.color??W,E=u.useRef(new Map),k=ae(c,()=>{const t=[];for(let n=1;n<=i;n++){const e=E.current.get(n);e&&t.push(e)}return t},i>0&&x>0),K=u.useRef(k);K.current=k,u.useLayoutEffect(()=>{k.restoreAnchor()},[g]),u.useEffect(()=>{const t=c.current;t&&!t.closest(".chunk-body")&&t.focus({preventScroll:!0})},[]),u.useLayoutEffect(()=>{const t=c.current;if(!t)return;const n=new ResizeObserver(([e])=>P(e.contentRect.width));return n.observe(t),()=>n.disconnect()},[]);const B=Math.min(x,1e3)*g;u.useEffect(()=>{const t=c.current;if(!t)return;let n=!1,e=0;const o=a=>{(a.ctrlKey||a.altKey||a.metaKey)&&(a.preventDefault(),n||(K.current.captureAnchor(),n=!0),clearTimeout(e),e=window.setTimeout(()=>n=!1,250),v(h=>Math.min(5,Math.max(.4,h*(a.deltaY<0?1.1:1/1.1)))))};return t.addEventListener("wheel",o,{passive:!1}),()=>{t.removeEventListener("wheel",o),clearTimeout(e)}},[]),u.useEffect(()=>{let t=!1;return w(0),N(null),V(new Set),T(new Map),E.current.clear(),A.current.clear(),(async()=>{const n=await fetch(se(s.path)).then(o=>o.arrayBuffer()),e=await ue(n,s.path);t||w(e)})().catch(n=>!t&&N(String(n.message||n))),()=>{t=!0}},[s.path]),u.useEffect(()=>{if(!i)return;const t=new IntersectionObserver(n=>{V(e=>{const o=new Set(e);for(const a of n){const h=Number(a.target.dataset.page);a.isIntersecting?o.add(h):o.delete(h)}return o.size===e.size&&[...o].every(a=>e.has(a))?e:o})},{root:c.current,rootMargin:"2000px 0px"});for(const n of E.current.values())t.observe(n);return()=>t.disconnect()},[i,x>0]),u.useEffect(()=>{let t=!0;return b.forEach(n=>{D.has(n)||he(n).then(e=>{A.current.set(n,{w:e.w,h:e.h}),t&&T(o=>new Map(o).set(n,e))}).catch(()=>{})}),T(n=>{let e=!1;const o=new Map(n);for(const a of o.keys())b.has(a)||(o.delete(a),e=!0);return e?o:n}),()=>{t=!1}},[b]);const G=()=>{const t=window.getSelection();if(!t||t.isCollapsed||!t.anchorNode)return;const e=(t.anchorNode.nodeType===1?t.anchorNode:t.anchorNode.parentElement)?.closest(".djvu-page-wrap");if(!e||!c.current?.contains(e))return;const o=Number(e.dataset.page),a=e.getBoundingClientRect(),h=a.width/(D.get(o)?.w||1);if(!h)return;const l=t.getRangeAt(0).getBoundingClientRect();l.width<2||l.height<2||H({type:"djvu",page:o,x:Math.round((l.left-a.left)/h),y:Math.round((l.top-a.top)/h),w:Math.round(l.width/h),h:Math.round(l.height/h)},{x:l.left,y:l.bottom+6})},I=(t,n)=>n.getBoundingClientRect().width/(D.get(t)?.w||1),J=(t,n)=>{const e=n.currentTarget.getBoundingClientRect(),o=I(t,n.currentTarget);y({page:t,x0:(n.clientX-e.left)/o,y0:(n.clientY-e.top)/o,x1:(n.clientX-e.left)/o,y1:(n.clientY-e.top)/o})},$=(t,n)=>{const e=n.currentTarget.getBoundingClientRect(),o=I(t,n.currentTarget);y(a=>a&&{...a,x1:(n.clientX-e.left)/o,y1:(n.clientY-e.top)/o})},ee=(t,n)=>{const e=f;if(y(null),!e||e.page!==t)return;const o=I(t,n.currentTarget),a=Math.min(e.x0,e.x1),h=Math.min(e.y0,e.y1),l=Math.abs(e.x1-e.x0),r=Math.abs(e.y1-e.y0);if(l*o<3||r*o<3)return;const j=n.currentTarget.getBoundingClientRect();H({type:"djvu",page:t,x:Math.round(a),y:Math.round(h),w:Math.round(l),h:Math.round(r)},{x:j.left+a*o,y:j.top+(h+r)*o+6})};return M?d.jsxs("div",{className:"error",children:["djvu: ",M]}):d.jsxs(d.Fragment,{children:[d.jsxs("div",{className:"filedjvu yo-zoomable",ref:c,tabIndex:0,onMouseUp:G,children:[i===0&&d.jsx("div",{className:"loading",children:"opening djvu…"}),x>0&&Array.from({length:i},(t,n)=>{const e=n+1,o=b.has(e)?D.get(e):void 0,a=A.current.get(e),h=B*(a?a.h/a.w:Math.SQRT2),l=o?B/o.w:0;return d.jsx("div",{className:"djvu-page-wrap","data-page":e,ref:r=>{r?E.current.set(e,r):E.current.delete(e)},style:{width:B,height:o?void 0:h},children:o?d.jsxs(d.Fragment,{children:[d.jsx(ge,{image:o.image}),o.zones.length>0?d.jsx("div",{className:"djvu-textlayer",children:o.zones.map((r,j)=>d.jsx("span",{style:{left:r.x*l,top:r.y*l,width:r.width*l,height:r.height*l,fontSize:r.height*l},children:r.text},j))}):d.jsx("div",{className:"djvu-marquee",onMouseDown:r=>J(e,r),onMouseMove:r=>$(e,r),onMouseUp:r=>ee(e,r),children:f?.page===e&&d.jsx("div",{className:"djvu-region",style:{left:Math.min(f.x0,f.x1)*l,top:Math.min(f.y0,f.y1)*l,width:Math.abs(f.x1-f.x0)*l,height:Math.abs(f.y1-f.y0)*l,borderColor:O,background:O+"2e"}})}),q.filter(r=>r.page===e).map((r,j)=>{const U=r.color||W;return d.jsx("div",{className:"djvu-region"+(r.ann?" editable":""),title:r.ann?r.title||"click to recolor or delete":r.title,onClick:r.ann?L=>{L.stopPropagation(),Q(r.ann,{x:L.clientX,y:L.clientY})}:void 0,style:{left:r.x*l,top:r.y*l,width:r.w*l,height:r.h*l,borderColor:U,background:U+"2e"}},j)})]}):d.jsx("div",{className:"djvu-placeholder",children:b.has(e)?"decoding…":""})},n)})]}),Z]})}export{ve as DjvuView};
|