@kolkrabbi/kol-workshop 0.24.1 → 0.25.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.
- package/package.json +6 -6
- package/src/tags/TagModeOverlay.jsx +57 -14
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kolkrabbi/kol-workshop",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.25.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "KOL workshop/docs system — a handrolled markdown engine, search, tag system (incl. a d3 tag graph), docs viewer, and docs shell. Sits above @kolkrabbi/kol-{theme,icons,component,framework}; content is consumer-injected (the package never globs docs).",
|
|
6
6
|
"license": "MIT",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
15
|
"d3": "^7.9.0",
|
|
16
|
-
"@kolkrabbi/kol-brand": "^0.1.
|
|
16
|
+
"@kolkrabbi/kol-brand": "^0.1.3"
|
|
17
17
|
},
|
|
18
18
|
"peerDependencies": {
|
|
19
19
|
"@kolkrabbi/kol-component": ">=0.68.1",
|
|
@@ -25,10 +25,10 @@
|
|
|
25
25
|
"react-router-dom": "^6.0.0 || ^7.0.0"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
|
-
"@kolkrabbi/kol-
|
|
29
|
-
"@kolkrabbi/kol-
|
|
30
|
-
"@kolkrabbi/kol-framework": "^0.
|
|
31
|
-
"@kolkrabbi/kol-theme": "^0.
|
|
28
|
+
"@kolkrabbi/kol-component": "^0.145.0",
|
|
29
|
+
"@kolkrabbi/kol-icons": "^0.25.0",
|
|
30
|
+
"@kolkrabbi/kol-framework": "^0.36.0",
|
|
31
|
+
"@kolkrabbi/kol-theme": "^0.112.0"
|
|
32
32
|
},
|
|
33
33
|
"files": [
|
|
34
34
|
"src",
|
|
@@ -5,11 +5,12 @@ import TagPath from './TagPath.jsx'
|
|
|
5
5
|
import { useTagMode } from './TagModeContext.jsx'
|
|
6
6
|
import TagGraph from './TagGraph.jsx'
|
|
7
7
|
import RailRow from '../shell/RailRow.jsx'
|
|
8
|
-
import { extractDocNumber, cleanTitle } from '../engine/index.js'
|
|
8
|
+
import { extractDocNumber, cleanTitle, matchSearchItems } from '../engine/index.js'
|
|
9
9
|
import { buildTagCounts } from '../engine/tags.js'
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
|
-
* TagModeOverlay - the
|
|
12
|
+
* TagModeOverlay - the committed RESULTS view: documents that match, with tags
|
|
13
|
+
* as a facet over them. It was a tag browser that forgot the query.
|
|
13
14
|
*
|
|
14
15
|
* ONE SEARCH (user ruling 2026-08-01: "search SHOULD be ONE system not 2").
|
|
15
16
|
* This component owned a second one - a raw `Input` plus
|
|
@@ -21,27 +22,62 @@ import { buildTagCounts } from '../engine/tags.js'
|
|
|
21
22
|
*
|
|
22
23
|
* `view` comes from the CONTEXT, not local state - the rail's "Graph view" row
|
|
23
24
|
* has to say which mode it wants before this component exists.
|
|
25
|
+
*
|
|
26
|
+
* IT NOW READS `text` (TagModeOverlayIgnoresQuery, kol-website 2026-08-31).
|
|
27
|
+
* Pressing return set `expanded`, ShellLayout swapped its body for this
|
|
28
|
+
* component, and this component consulted `activeTags` and nothing else — so a
|
|
29
|
+
* typed query was replaced by the COMPLETE UNFILTERED TAG CENSUS and zero
|
|
30
|
+
* document rows, by construction. Searching `rf` listed
|
|
31
|
+
* `project/kol-monorepo 85`, `domain/design-system 13` … not one of which
|
|
32
|
+
* contains `rf`. The engine was never the problem: `matchSearchItems` already
|
|
33
|
+
* spans label / tags / headings / keywords, and ShellLayout was computing
|
|
34
|
+
* exactly that. The expanded view simply threw it away.
|
|
35
|
+
*
|
|
36
|
+
* ONE MATCHER (the 2026-08-01 ruling, still): this reuses `matchSearchItems`
|
|
37
|
+
* rather than adding a second predicate, so committed and uncommitted results
|
|
38
|
+
* rank identically. `text` and `activeTags` are two facets of one query.
|
|
24
39
|
*/
|
|
25
40
|
const TagModeOverlay = () => {
|
|
26
|
-
const { activeTags, activeTag, toggleTag, clearTags, closeTagMode, inventory, docHref, tagHref, view, setView } = useTagMode()
|
|
41
|
+
const { activeTags, activeTag, toggleTag, clearTags, closeTagMode, inventory, docHref, tagHref, view, setView, text } = useTagMode()
|
|
42
|
+
|
|
43
|
+
const query = (text || '').trim()
|
|
27
44
|
|
|
28
|
-
|
|
45
|
+
/* THE TWO FACETS, IN ORDER: chips narrow first, then the query matches over
|
|
46
|
+
* what is left. Either alone is a filter; neither is required. */
|
|
47
|
+
const filteredDocs = useMemo(() => {
|
|
48
|
+
const tagged = activeTags.length === 0
|
|
49
|
+
? inventory
|
|
50
|
+
: inventory.filter((d) => Array.isArray(d.metadata?.tags) && activeTags.every((t) => d.metadata.tags.includes(t)))
|
|
51
|
+
if (!query) return activeTags.length === 0 ? [] : tagged
|
|
52
|
+
/* the engine's shape, not a second matcher */
|
|
53
|
+
const matched = matchSearchItems(
|
|
54
|
+
tagged.map((d) => ({
|
|
55
|
+
...d,
|
|
56
|
+
label: cleanTitle(d.title, d.id),
|
|
57
|
+
tags: d.metadata?.tags ?? [],
|
|
58
|
+
headings: d.headings ?? d.metadata?.headings ?? [],
|
|
59
|
+
keywords: d.keywords ?? d.metadata?.keywords ?? [],
|
|
60
|
+
})),
|
|
61
|
+
query,
|
|
62
|
+
)
|
|
63
|
+
return matched
|
|
64
|
+
}, [inventory, activeTags, query])
|
|
65
|
+
|
|
66
|
+
const hasFilters = activeTags.length > 0 || query.length > 0
|
|
67
|
+
|
|
68
|
+
/* THE CLOUD NARROWS TO THE RESULT SET, with counts recomputed over it. An
|
|
69
|
+
* empty query keeps the full census — that is a good browse state, and it is
|
|
70
|
+
* only wrong once a query exists. */
|
|
71
|
+
const allTagsWithCount = useMemo(
|
|
72
|
+
() => buildTagCounts(hasFilters ? filteredDocs : inventory),
|
|
73
|
+
[inventory, filteredDocs, hasFilters],
|
|
74
|
+
)
|
|
29
75
|
|
|
30
76
|
const visibleTags = useMemo(
|
|
31
77
|
() => allTagsWithCount.filter(({ tag }) => !activeTags.includes(tag)),
|
|
32
78
|
[allTagsWithCount, activeTags]
|
|
33
79
|
)
|
|
34
80
|
|
|
35
|
-
const filteredDocs = useMemo(() => {
|
|
36
|
-
if (activeTags.length === 0) return []
|
|
37
|
-
return inventory.filter((d) => {
|
|
38
|
-
if (!Array.isArray(d.metadata?.tags)) return false
|
|
39
|
-
return activeTags.every((t) => d.metadata.tags.includes(t))
|
|
40
|
-
})
|
|
41
|
-
}, [inventory, activeTags])
|
|
42
|
-
|
|
43
|
-
const hasFilters = activeTags.length > 0
|
|
44
|
-
|
|
45
81
|
return (
|
|
46
82
|
/* NOT AN OVERLAY ANY MORE (user ruling 2026-08-01). This is the EXPANDED
|
|
47
83
|
* BODY of ShellSearchOverlay, rendered inside its panel — so it owns no
|
|
@@ -115,6 +151,13 @@ const TagModeOverlay = () => {
|
|
|
115
151
|
)}
|
|
116
152
|
</div>
|
|
117
153
|
|
|
154
|
+
{/* A QUERY THAT MATCHES NOTHING SAYS SO rather than falling
|
|
155
|
+
* back to the census — the census was the bug. */}
|
|
156
|
+
{hasFilters && filteredDocs.length === 0 && (
|
|
157
|
+
<p className="text-fg-48 kol-mono-12 py-4 border-t border-fg-08">
|
|
158
|
+
No documents match{query ? ` “${query}”` : ''}
|
|
159
|
+
</p>
|
|
160
|
+
)}
|
|
118
161
|
{hasFilters && filteredDocs.length > 0 && (
|
|
119
162
|
<div className="shell-nav-items pt-4 border-t border-fg-08">
|
|
120
163
|
{filteredDocs.map((d) => (
|