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.
Files changed (33) hide show
  1. package/README.md +186 -143
  2. package/bin/yamlover.js +51 -2
  3. package/dist/agent-docs/AGENTS.md +267 -0
  4. package/dist/agent-docs/CLAUDE.md +7 -0
  5. package/dist/builtin-taxonomy/$defs/.yamlover/meta.yamlover +25 -0
  6. package/dist/builtin-taxonomy/$defs/annotation +18 -0
  7. package/dist/builtin-taxonomy/$defs/board +15 -0
  8. package/dist/builtin-taxonomy/$defs/chapter +19 -0
  9. package/dist/builtin-taxonomy/$defs/chunk +6 -0
  10. package/dist/builtin-taxonomy/$defs/config +20 -0
  11. package/dist/builtin-taxonomy/$defs/fragment +44 -0
  12. package/dist/builtin-taxonomy/$defs/tag +17 -0
  13. package/dist/builtin-taxonomy/$defs/task +33 -0
  14. package/dist/builtin-taxonomy/$defs/workflow +18 -0
  15. package/dist/builtin-taxonomy/tags/.yamlover/body.yamlover +46 -0
  16. package/dist/client/assets/{decoded-M-9MjrR3.js → decoded-DVR13gJ5.js} +1 -1
  17. package/dist/client/assets/{djvu-M6ksPMNz.js → djvu-CZbDJKwH.js} +1 -1
  18. package/dist/client/assets/{docx-D4h81KWC.js → docx-BWH54ddd.js} +1 -1
  19. package/dist/client/assets/{heic-BoSpW_tk.js → heic-CV3oZ6BF.js} +1 -1
  20. package/dist/client/assets/{imagemap-CIJFPvXv.js → imagemap-DRJrF_aD.js} +1 -1
  21. package/dist/client/assets/index-CCUPHCB4.js +620 -0
  22. package/dist/client/assets/{index-DHqicFJy.css → index-D5xvCkZE.css} +1 -1
  23. package/dist/client/assets/{map-MATRMboW.js → map-Y_zmEjLV.js} +1 -1
  24. package/dist/client/assets/{paged-BQLUFBor.js → paged-DKLVDozm.js} +1 -1
  25. package/dist/client/assets/{panzoom-Do4jskMZ.js → panzoom-NbtFbptF.js} +1 -1
  26. package/dist/client/assets/{pdf-BShpl2D5.js → pdf-DM1MQkYa.js} +1 -1
  27. package/dist/client/assets/{psd-DIxfuhrV.js → psd-PXeZB_nC.js} +1 -1
  28. package/dist/client/assets/{spreadsheet-DL0NoDcm.js → spreadsheet-B-3t2XCi.js} +1 -1
  29. package/dist/client/assets/{tiff-C7zPNKoD.js → tiff-BQq-nYyI.js} +1 -1
  30. package/dist/client/index.html +2 -2
  31. package/dist/server.js +545 -339
  32. package/package.json +1 -1
  33. 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,6 @@
1
+ # $defs/chunk — one renderable content block.
2
+ #
3
+ # A chunk is a typed value; its (type, format) selects the renderer. The default is
4
+ # markdown prose; a chunk overrides type/format to be a diagram, image, LaTeX, CSV, etc.
5
+ type: string
6
+ format: text/marklower
@@ -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-DZ1FuhId.js";import{PanZoomImage as p}from"./imagemap-CIJFPvXv.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
+ 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-DZ1FuhId.js";import{u as ae}from"./paged-BQLUFBor.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};
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};