okf 1.10.0 → 1.12.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +294 -0
- data/README.md +310 -445
- data/lib/okf/bundle/folder.rb +24 -5
- data/lib/okf/bundle/linter.rb +1 -1
- data/lib/okf/bundle/search/index.rb +13 -3
- data/lib/okf/bundle/search.rb +93 -13
- data/lib/okf/bundle/skeleton.rb +241 -0
- data/lib/okf/bundle.rb +19 -14
- data/lib/okf/cli/catalog.rb +6 -6
- data/lib/okf/cli/command.rb +241 -14
- data/lib/okf/cli/dirs.rb +118 -0
- data/lib/okf/cli/files.rb +2 -2
- data/lib/okf/cli/graph.rb +115 -9
- data/lib/okf/cli/index.rb +67 -25
- data/lib/okf/cli/loose.rb +2 -2
- data/lib/okf/cli/registry.rb +152 -15
- data/lib/okf/cli/render.rb +3 -2
- data/lib/okf/cli/search.rb +33 -2
- data/lib/okf/cli/server.rb +16 -5
- data/lib/okf/cli/stats.rb +36 -11
- data/lib/okf/cli/tags.rb +29 -7
- data/lib/okf/cli/types.rb +1 -1
- data/lib/okf/cli.rb +10 -6
- data/lib/okf/registry.rb +351 -20
- data/lib/okf/render/graph/template.html.erb +504 -103
- data/lib/okf/render/graph.rb +27 -3
- data/lib/okf/server/app.rb +74 -3
- data/lib/okf/server/hub.rb +40 -30
- data/lib/okf/skill/SKILL.md +17 -10
- data/lib/okf/skill/playbooks/consume.md +3 -3
- data/lib/okf/skill/playbooks/maintain.md +4 -3
- data/lib/okf/skill/playbooks/menu.md +3 -2
- data/lib/okf/skill/playbooks/refine.md +30 -3
- data/lib/okf/skill/playbooks/search.md +7 -7
- data/lib/okf/skill/reference/cli.md +171 -32
- data/lib/okf/version.rb +1 -1
- data/lib/okf.rb +10 -0
- metadata +21 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9a83101e62e3b3bc97d7351580d50ecc017be391a5edbde2e8f52388de529111
|
|
4
|
+
data.tar.gz: 040be5295642da56cbeed342424872469d1c2b15efcb1b477f10076d7c90fafc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 47fb84d8dffc04eee30eb65adfaca756ae43315e2d152e62692084a5bd04c6a7992529d243b2375412a8339a55db891c3ae18d679e171d5709e9426b93686047
|
|
7
|
+
data.tar.gz: 30390e1eecc1570b28531c374b21200309630aad87f821a7c3f25b631ca5285e03b91baab9048f69127931a09368da1ab38a089fc9bc25473c05da3c0fadfe2b
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,299 @@ All notable changes to this project are documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.12.0] - 2026-07-24
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **`okf graph --traffic`** — the link graph read one grain coarser. `--hubs`
|
|
13
|
+
measures concepts, but the refine playbook's directory judgements ("does this
|
|
14
|
+
directory prune? a concern, or a container?") had nothing measured at their
|
|
15
|
+
grain. `--traffic` collapses each concept into its directory and the links
|
|
16
|
+
between two directories into one weighted arc, so every row carries its
|
|
17
|
+
internal / out / in traffic and a **cohesion** — its internal share of that
|
|
18
|
+
total. That is cohesion versus coupling on a knowledge tree: near-zero cohesion
|
|
19
|
+
under heavy inbound is a shared vocabulary doing its job, heavy outbound with
|
|
20
|
+
nothing back is a projection wearing a directory. The rows sort by cohesion
|
|
21
|
+
ascending, so the directories with a case to answer come first, and a directory
|
|
22
|
+
with no traffic at all prints `—` rather than a `0%` it did not earn. The arc
|
|
23
|
+
**cut** is fitted to the bundle, not fixed — at weight 3, ten bundles ranged
|
|
24
|
+
from 2 arcs to 136 — and `--cut N` overrides it; cohesion is computed over
|
|
25
|
+
*every* arc regardless, so narrowing the drawn picture never moves the
|
|
26
|
+
evidence. JSON: `{ bundle, cut, fitted, dirs, arcs, total_arcs }`. The pure
|
|
27
|
+
model underneath is `Bundle::Skeleton` — concepts folded to directories, links
|
|
28
|
+
to weighted arcs, every edge tagged with the cut it survives (a local-degree
|
|
29
|
+
sparsifier, union rule, so no linked concept is ever stranded) — and it does no
|
|
30
|
+
I/O and draws nothing: it names a cut rather than taking one, so both the graph
|
|
31
|
+
page and the CLI narrow the same emission their own way.
|
|
32
|
+
- **The graph page draws links in three amounts, and opens dense on its spine.**
|
|
33
|
+
227 links over 47 concepts at degree 9.7 is unreadable because of its arrows,
|
|
34
|
+
not its dots — so links become a layer: **every** link, the **spine** (each
|
|
35
|
+
concept's strongest edge — the Skeleton's `keep_at===0` set, chosen so it
|
|
36
|
+
touches every linked concept), or **none**, with a selected concept's own links
|
|
37
|
+
always shown in full. A dense bundle now opens on its spine rather than greeting
|
|
38
|
+
the reader with the thicket; the trigger is undirected degree above a floor set
|
|
39
|
+
between a tree's ~2 and the 9.7 that drove it, and `--map` overrides to none
|
|
40
|
+
with the directories boxed. Above 800 edges the first layout runs on the spine
|
|
41
|
+
alone and the rest arrive a frame later with no re-layout — **6.25 s → 3.29 s**
|
|
42
|
+
on 414 concepts, nothing on screen moving. Proven in both render modes on a
|
|
43
|
+
committed 110-concept / 880-link fixture, the only one over both floors.
|
|
44
|
+
- **Registry groups — a named, recursive set of bundles.** A group is a registry
|
|
45
|
+
slug that names a list of members (bundle *or* group slugs, so they nest) and
|
|
46
|
+
resolves recursively, path-deduped, to its bundle leaves. `okf registry group
|
|
47
|
+
<slug> <@member…>` creates one or adds to it, `ungroup` removes members (and
|
|
48
|
+
emptying a group deletes it), and `del`/`rename` now span a group slug too —
|
|
49
|
+
one name cascades across every member list, one `del` cascade-drops the slug and
|
|
50
|
+
deletes any group it empties. Groups live in their own list in the registry
|
|
51
|
+
JSON, so the first-is-default rule and the `File.directory?` guards never meet a
|
|
52
|
+
pathless entry. `okf search @backend` merges the members into one ranking and
|
|
53
|
+
`okf server @backend` mounts each (the first at `/`), both skipping a vanished
|
|
54
|
+
member with a note, exactly as `@all` does. Every single-bundle verb **refuses**
|
|
55
|
+
a `@group` (exit 2) — the same rule that refuses a second bundle, through the
|
|
56
|
+
same `resolve_registered` seam.
|
|
57
|
+
- **`okf registry init`** — create a project-local `.okf-registry.json` in the
|
|
58
|
+
current directory. Once it exists, okf discovers it by walking up from the
|
|
59
|
+
working directory, and every registry operation — and every `@slug` — resolves
|
|
60
|
+
through it in place of the global `$OKF_HOME` registry, so a bare `okf server`
|
|
61
|
+
inside a repo serves that repo's bundles with no global setup. The nearest one
|
|
62
|
+
on the path wins (nested registries resolve nearest-first), `okf registry list`
|
|
63
|
+
names the local file it found, and `OKF_NO_DISCOVERY=1` forces the global one —
|
|
64
|
+
the escape hatch for a fixed-cwd caller (CI, a tool).
|
|
65
|
+
- **A local registry stores portable, relative paths.** A bundle inside the
|
|
66
|
+
registry's own tree is written relative to the `.okf-registry.json`, so the file
|
|
67
|
+
can be committed and travels with the repo — a checkout on another machine, or a
|
|
68
|
+
container that mounts it, resolves the same bundles unchanged. A bundle outside
|
|
69
|
+
the tree keeps an absolute path (it cannot travel). Paths still read back
|
|
70
|
+
absolute everywhere the CLI reports them; the relative form lives only on disk,
|
|
71
|
+
and an existing absolute local entry migrates to relative on its next write. The
|
|
72
|
+
global `$OKF_HOME` registry is unchanged — it stores absolute paths as before.
|
|
73
|
+
|
|
74
|
+
### Changed
|
|
75
|
+
|
|
76
|
+
- **The derived `area` field is renamed `top_dir`** — the first-path-segment
|
|
77
|
+
rollup the catalog, search, `stats`, and `graph --hubs` carry. `area` was never
|
|
78
|
+
the OKF spec's word (the spec speaks only of `dir`), so the rollup now names
|
|
79
|
+
itself in the spec's vocabulary: it is the `dir` at the top level. The `--json`
|
|
80
|
+
keys move with it — catalog/search rows carry `top_dir` in place of `area`,
|
|
81
|
+
`stats` emits `top_dirs`/`by_top_dir`, and `graph --hubs` emits
|
|
82
|
+
`top_dir`/`by_top_dir`. The **deprecated `--area`/`--by area` input flags are
|
|
83
|
+
unchanged** — they still warn and map to `--dir`/`--by dir`, and now source the
|
|
84
|
+
renamed field internally. No behavior changes; the well-homed-hub numbers are
|
|
85
|
+
identical.
|
|
86
|
+
|
|
87
|
+
### Fixed
|
|
88
|
+
|
|
89
|
+
- **A local-registry server preserves its relative-path anchor across a
|
|
90
|
+
re-open.** The hub re-read its boot registry with `OKF::Registry.new(path)`,
|
|
91
|
+
which drops the `relative_base` a discovered `.okf-registry.json` carries — so
|
|
92
|
+
on a project-local server the Bundles panel matched each mounted bundle's
|
|
93
|
+
absolute root against the re-read entry's *relative* path, missed, and drew
|
|
94
|
+
every in-tree bundle as "folder is gone", while a browser add flattened the new
|
|
95
|
+
bundle to an absolute path, undoing the portability relative storage exists for.
|
|
96
|
+
`Registry#reopen` re-reads the same file anchored the same way, and both hub
|
|
97
|
+
re-open sites use it.
|
|
98
|
+
|
|
99
|
+
## [1.11.0] - 2026-07-22
|
|
100
|
+
|
|
101
|
+
### Added
|
|
102
|
+
|
|
103
|
+
- **`okf dirs <dir|@slug> [--json]`** — the bundle's directories (its clusters)
|
|
104
|
+
with the number of concepts living **directly** in each, root first and the
|
|
105
|
+
total last. Every dir the tree has, including the empty intermediates that
|
|
106
|
+
exist only to connect it — a dir holding nothing but sub-directories reads
|
|
107
|
+
`0`, not a hidden rollup, so the column sums to the bundle's concept count.
|
|
108
|
+
JSON: `{ bundle, total, count, dirs: [{ dir, count, subdirs }] }`.
|
|
109
|
+
- **`--depth N` on `index` and `dirs`** — how many directory levels below the
|
|
110
|
+
starting point to keep, where the starting point is the `--dir` when one is
|
|
111
|
+
given and the bundle root otherwise. Relative rather than absolute, so
|
|
112
|
+
`--dir a/b --depth 1` reads "a/b and one level under it" without first working
|
|
113
|
+
out how deep `a/b` is, and the two flags walk a tree a level at a time.
|
|
114
|
+
`--depth 0` is the starting point alone; anything but a whole number is a
|
|
115
|
+
usage error (exit 2). This is what makes `index` usable at scale — every
|
|
116
|
+
directory in it is a section, so a few hundred concepts is a map nobody reads
|
|
117
|
+
whole. On one 414-concept bundle `index --no-body` went 12.5 KB → 1.3 KB at
|
|
118
|
+
`--depth 1`, and `index --json` 313 KB → 2.8 KB with `--depth 1 --except
|
|
119
|
+
body,listing`.
|
|
120
|
+
- **`--dir` on `index` and `dirs` brings the chain up to the root with it**, so a
|
|
121
|
+
branch is never shown adrift of the authored context that says what it is —
|
|
122
|
+
the root `index.md`'s prose first among it. Those rows print with a leading
|
|
123
|
+
`↑`, carry `ancestor: true`, and stay out of `total`; `--no-ancestors` drops
|
|
124
|
+
them. Ascent and descent are separate axes, so `--depth` never bounds the
|
|
125
|
+
chain: `--dir X --depth 0` is X alone, plus how you get to X. A `--dir` that
|
|
126
|
+
names nothing gains no chain, since a lone root row would read as a partial
|
|
127
|
+
answer to a query that in fact matched nothing.
|
|
128
|
+
- **`dirs` gains `--dir` (repeatable) and a `subtree` count** per row: the
|
|
129
|
+
concepts at or below that directory, defined as exactly what `--dir` on the
|
|
130
|
+
row returns, so the number and the flag can never disagree. Without it a
|
|
131
|
+
truncated listing is all zeroes at the top of a deep tree — which is where
|
|
132
|
+
"where is the mass?" is actually asked. The human table shows the column only
|
|
133
|
+
where some directory nests; `--json` always carries it.
|
|
134
|
+
- **`--dir PATH`** joins the shared filter set on `search`, `catalog`, `files`,
|
|
135
|
+
`types` and `tags`, and `index` gains it as a repeatable selector. One rule:
|
|
136
|
+
a concept matches when its dir *is* the path or sits below it — so `--dir
|
|
137
|
+
platform` reaches `platform/services/api`, `--dir platform/services` narrows,
|
|
138
|
+
and `--dir .` means the root alone with no special case. `root` is the
|
|
139
|
+
unquoted spelling of `.`; matching folds case.
|
|
140
|
+
- **`tags --by dir`** cuts the tag index by the whole directory path, where
|
|
141
|
+
`--by area` only ever saw the first segment.
|
|
142
|
+
- **`stats` gains `dirs` and `by_dir`** (the full-path cut, direct counts), and
|
|
143
|
+
its human breakdown now reads **By dir**. Both are read off the same map `okf
|
|
144
|
+
dirs` lists and `--dir` is answered against, so the two verbs cannot report a
|
|
145
|
+
different number of directories — a directory holding nothing directly appears
|
|
146
|
+
at `0` rather than being dropped, since it is still one `--dir` addresses.
|
|
147
|
+
- **Search rows carry `dir`** — the full path, `.` at the root — beside the
|
|
148
|
+
first-segment `area` they already had.
|
|
149
|
+
- **`dirs` takes `--fields`/`--except`** like the other list views, over the row
|
|
150
|
+
shape it already declared.
|
|
151
|
+
- **A single-bundle `okf server` answers `GET /search?q=`.** The route was the
|
|
152
|
+
hub's alone — conceived as the *cross-bundle* one — which left `okf server
|
|
153
|
+
./docs` with a ⌘K palette that could find nothing, though one bundle is a legal
|
|
154
|
+
one-element set. `App` owns the payload and the hub calls it, so the shape is
|
|
155
|
+
defined once; a row from a single-bundle server carries no `slug`, which is how
|
|
156
|
+
it avoids answering as if it were a set. A static `okf render` still advertises
|
|
157
|
+
no endpoint: there is no server behind it to ask.
|
|
158
|
+
- **The route always answers; advertising it is the caller's call.** The page
|
|
159
|
+
resolves the endpoint *relative to the URL the reader is on*, so only whoever
|
|
160
|
+
mounted the app knows what to call it: `okf server` mounts at the root and
|
|
161
|
+
passes `search`, while `App.new(folder)` on its own advertises nothing, since
|
|
162
|
+
a default would point an app mounted at `/knowledge` back at its host's root.
|
|
163
|
+
- **The search index is built once and held.** Every request used to rebuild the
|
|
164
|
+
whole corpus — measured 1.45 s per search on a 414-concept bundle, flat across
|
|
165
|
+
repeats, with the build ~95% of it. `Search.prepare` holds a corpus (documents,
|
|
166
|
+
the key→concept map, the built index) and `Search.with` queries it: **0.016 –
|
|
167
|
+
0.052 s** per search, with the 1.39 s build moved into boot, where `okf server`
|
|
168
|
+
warms it deliberately. An engine opts in by exposing `prepare`; the scan
|
|
169
|
+
declares none and is handed none, so no engine or addon had to change. The
|
|
170
|
+
trade is staleness — a corpus is a snapshot, like the graph — and the hub drops
|
|
171
|
+
its corpus on any registry write, since a held index outliving the set it was
|
|
172
|
+
built from is a wrong answer rather than a slow one.
|
|
173
|
+
- **`⌥ drag` moves a cluster box**, and is listed in the `?` sheet.
|
|
174
|
+
|
|
175
|
+
### Changed
|
|
176
|
+
|
|
177
|
+
- **A bundle is named by its slug, everywhere it is chosen.** The ⌘K switcher,
|
|
178
|
+
the Bundles panel and the hub's `/b/` page all led with the derived
|
|
179
|
+
`parent/dir` label and left the slug in muted grey beside it — the address in
|
|
180
|
+
the name's place, when a bundle is addressed by `@okf-gem` and `/b/okf-gem/`.
|
|
181
|
+
Rows now carry `@slug` as the name and the folder as the fact under it, shown
|
|
182
|
+
only where it is not the name repeated. `/b/` drops the short label outright:
|
|
183
|
+
it only ever stood in for the full path, which is on the row already.
|
|
184
|
+
- **A `.okf` directory is labelled by the project that holds it.**
|
|
185
|
+
`Bundle::Folder.label` reads `repo/.okf` as `repo`. The `parent/dir` pair
|
|
186
|
+
exists because a bundle directory's own name is rarely unique — except when it
|
|
187
|
+
is `.okf`, the conventional container, and then a registry of eight projects
|
|
188
|
+
was eight rows all saying `.okf`. This also reaches `okf registry list` and the
|
|
189
|
+
default `okf server` title. (A `.okf` with no parent to borrow keeps its own
|
|
190
|
+
name; that case used to compose into `//.okf`.)
|
|
191
|
+
- **A force layout settles, then moves once.** `animate:true` reads to these
|
|
192
|
+
engines as "render every tick of the simulation" — the visible bounce, and
|
|
193
|
+
hundreds of full re-renders for one settle. It is `'end'` now: the same
|
|
194
|
+
simulation run headless, the nodes moved once into the same final positions.
|
|
195
|
+
Past 250 nodes even that transition is dropped, because at that size the move
|
|
196
|
+
itself is the jank.
|
|
197
|
+
- **A cluster box is scenery, not a handle.** Its empty interior is the largest
|
|
198
|
+
drag target on the canvas, so dragging to look around dragged the *directory*
|
|
199
|
+
instead of the view — worse the bigger the cluster. It takes `grabbable:false`
|
|
200
|
+
**and** `pannable:true`: ungrabbable alone stops the box moving, but the node
|
|
201
|
+
still swallows the drag, and it is `pannable` that hands the gesture to the
|
|
202
|
+
canvas the way empty background does. `Alt`+drag gives the handle back, since
|
|
203
|
+
moving a box is a real gesture, just not the constant one — `Alt` rather than
|
|
204
|
+
`Ctrl`, which on macOS is the system secondary click. A tap still opens the
|
|
205
|
+
directory's map.
|
|
206
|
+
- **`f` is no longer a shortcut.** A bare letter bound globally fires on every
|
|
207
|
+
keystroke the page did not route into an input, and fullscreen is not a mode to
|
|
208
|
+
enter by accident. The button stays and is now the only way in; the shortcut
|
|
209
|
+
sheet no longer advertises a key nothing is bound to.
|
|
210
|
+
- **The skill names one first move.** It had prescribed three different ones
|
|
211
|
+
across seven places — SKILL.md, four playbooks and the CLI reference — and that
|
|
212
|
+
disagreement is the deliberation an agent pays for on every retrieval. Every
|
|
213
|
+
site now says `okf dirs` first, then `okf index --dir <branch>` to descend,
|
|
214
|
+
chosen structurally: `dirs` emits one row per *directory* where `index` emits
|
|
215
|
+
one listing row per *concept* even under `--no-body`, so the two scale with
|
|
216
|
+
different things.
|
|
217
|
+
|
|
218
|
+
- **Cluster mode nests.** The graph page grouped concepts into one flat row of
|
|
219
|
+
boxes, one per *first path segment* — the same lossy projection `--area` was.
|
|
220
|
+
A cluster is a directory now, and the boxes nest as the directories do, to a
|
|
221
|
+
depth picked from a select beside the layout one. Depth **1** is the default
|
|
222
|
+
and draws exactly the old view; a flat bundle is offered no control at all.
|
|
223
|
+
At depth N every directory of depth ≤ N gets a box (intermediates that hold no
|
|
224
|
+
concepts of their own included, since they hold sub-boxes), and a concept
|
|
225
|
+
attaches to its own directory's box truncated to N. The root box still holds
|
|
226
|
+
direct-root concepts and never nests another. Box ids carry the directory
|
|
227
|
+
verbatim (`box::platform/services`, `box::.`), so a tap opens that directory's
|
|
228
|
+
map with no label to unmangle.
|
|
229
|
+
- **The page speaks `dir` too**: the filter group is **Dirs**, listing every
|
|
230
|
+
directory (not just first segments) and filtering by the same
|
|
231
|
+
directory-and-below rule `--dir` uses — in the graph, catalog and tags views —
|
|
232
|
+
and the Stats panel's breakdown is **By dir**, keyed by the whole path.
|
|
233
|
+
|
|
234
|
+
### Fixed
|
|
235
|
+
|
|
236
|
+
- **The rail marks Index while the root map is open.** Index is a shortcut into
|
|
237
|
+
Files, so the two share one `data-view` — and the rail read only that, lighting
|
|
238
|
+
**Files** on the one screen a reader reached by clicking **Index**. The open
|
|
239
|
+
file is what distinguishes them, so it is what the rail reads; a *nested*
|
|
240
|
+
`index.md` is still Files.
|
|
241
|
+
- **`--dir` accepts the label the views print.** `fold_dir` never stripped a
|
|
242
|
+
trailing slash, while `okf index` labels a row `tables/` — so pasting a printed
|
|
243
|
+
row back into the flag matched nothing and exited 0, an empty result under a
|
|
244
|
+
count that agreed with it.
|
|
245
|
+
- **The `--dir` chain keeps its case.** It was walked over case-folded paths and
|
|
246
|
+
then matched against the map with `include?`, which does not fold, so every
|
|
247
|
+
ancestor of a directory spelled with a capital vanished from the chain that
|
|
248
|
+
exists to place the branch.
|
|
249
|
+
- **`--area` with `--depth` or `--dir` is refused (exit 2)** instead of unioning
|
|
250
|
+
the area with what the other flag selects. The deprecated flag is exact: with
|
|
251
|
+
`--depth` it names no starting point to be relative to, and with `--dir` one
|
|
252
|
+
side is exact where the other is a prefix, so the map came back with the area
|
|
253
|
+
*and* the subtree — an answer to neither question. A deprecated flag that
|
|
254
|
+
quietly widens is worse than one that is merely old.
|
|
255
|
+
- **A cleared filter no longer leaves a cluster unlaid.** The tiling runs over
|
|
256
|
+
the visible elements only (fcose throws on a node whose label went
|
|
257
|
+
`display:none` mid-run), but nothing re-tiled when a filter was later loosened —
|
|
258
|
+
so concepts hidden when clustering began came back at their pre-cluster
|
|
259
|
+
coordinates and stretched their box across the canvas. Worst case the filter
|
|
260
|
+
matched nothing, the layout returned early, and clearing it showed a view
|
|
261
|
+
nothing had laid out at all.
|
|
262
|
+
- **A palette hit in a single-bundle server no longer 404s or reloads the page.**
|
|
263
|
+
A row with no `slug` was read as naming a *foreign* bundle, so the href became
|
|
264
|
+
`../undefined/`, the row rendered an "undefined" chip, and the click took the
|
|
265
|
+
page-load branch — reloading the whole index to reach a node already on screen.
|
|
266
|
+
Three sites, one absent field.
|
|
267
|
+
- **A focused form field no longer zooms the page on iOS.** Safari zooms whenever
|
|
268
|
+
a focused control is under 16px and never zooms back out, so on a phone every
|
|
269
|
+
`/` left the reader pinching to recover. Keyed on `(max-width:768px)` *or*
|
|
270
|
+
`(pointer:coarse)`, because neither covers the other — a phone is narrow, a
|
|
271
|
+
tablet in landscape is not and zooms just the same.
|
|
272
|
+
- **A nested cluster no longer throws when a filter empties it mid-layout.**
|
|
273
|
+
fcose measures every node it is handed, so hiding nodes while its tiling
|
|
274
|
+
animation ran threw on a label it could no longer measure. The layout is
|
|
275
|
+
handed the visible elements only — which is also the right answer, since a
|
|
276
|
+
hidden concept has no business influencing where the visible ones land.
|
|
277
|
+
- **An intermediate directory box no longer takes its branch off the canvas.**
|
|
278
|
+
The empty-box rule read a compound's direct children, and a box holding only
|
|
279
|
+
sub-boxes has none, so it always counted as empty. It reads leaf descendants
|
|
280
|
+
now.
|
|
281
|
+
|
|
282
|
+
### Removed
|
|
283
|
+
|
|
284
|
+
- **`OKF::Server::Hub::SEARCH_LIMIT` and `Hub::SEARCH_ENGINE`.** Both moved to
|
|
285
|
+
`OKF::Server::App`, which now defines the `/search` payload both hosts answer
|
|
286
|
+
with (`App.search_payload`). The hub's copies were left behind unreferenced —
|
|
287
|
+
two constants for one cap is two places to raise it and one of them silently
|
|
288
|
+
losing. Use `OKF::Server::App::SEARCH_LIMIT` / `App::SEARCH_ENGINE`.
|
|
289
|
+
|
|
290
|
+
### Deprecated
|
|
291
|
+
|
|
292
|
+
- **`--area`, and `tags --by area`.** OKF's own vocabulary for grouping is
|
|
293
|
+
*directories* (`grep -ci area SPEC.md` → 0); "area" was this gem's invention,
|
|
294
|
+
and defining it as a concept id's first path segment threw away every level
|
|
295
|
+
below it. `dir` is now the only machine word — full path, `.` at the root,
|
|
296
|
+
rendered `(root)` for humans — and "cluster" stays prose for what a dir
|
|
297
|
+
groups. Both deprecated spellings keep their **old behavior exactly** and warn
|
|
298
|
+
once per run on stderr (`--json` on stdout is unaffected); they go in a later
|
|
299
|
+
release.
|
|
300
|
+
|
|
8
301
|
## [1.10.0] - 2026-07-21
|
|
9
302
|
|
|
10
303
|
### Added
|
|
@@ -930,6 +1223,7 @@ Initial release.
|
|
|
930
1223
|
|
|
931
1224
|
- Runs on Ruby >= 2.4 with two runtime dependencies: rack and webrick.
|
|
932
1225
|
|
|
1226
|
+
[1.11.0]: https://github.com/serradura/okf-gem/compare/v1.10.0...v1.11.0
|
|
933
1227
|
[1.10.0]: https://github.com/serradura/okf-gem/compare/v1.9.0...v1.10.0
|
|
934
1228
|
[1.9.0]: https://github.com/serradura/okf-gem/compare/v1.8.0...v1.9.0
|
|
935
1229
|
[1.8.0]: https://github.com/serradura/okf-gem/compare/v1.7.0...v1.8.0
|