@kolkrabbi/kol-workshop 0.32.0 → 0.33.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/index.js +2 -0
- package/src/search/ResultRow.jsx +22 -27
- package/src/search/SearchPage.jsx +13 -9
- package/src/search/index.js +0 -1
- package/src/shell/ShellLayout.jsx +34 -49
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kolkrabbi/kol-workshop",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.33.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,9 +13,9 @@
|
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
15
|
"d3": "^7.9.0",
|
|
16
|
-
"@kolkrabbi/kol-
|
|
16
|
+
"@kolkrabbi/kol-search": "^0.2.0",
|
|
17
17
|
"@kolkrabbi/kol-markdown": "^0.1.2",
|
|
18
|
-
"@kolkrabbi/kol-
|
|
18
|
+
"@kolkrabbi/kol-brand": "^0.1.3"
|
|
19
19
|
},
|
|
20
20
|
"peerDependencies": {
|
|
21
21
|
"@kolkrabbi/kol-component": ">=0.228.0",
|
|
@@ -27,10 +27,10 @@
|
|
|
27
27
|
"react-router-dom": "^6.0.0 || ^7.0.0"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
|
-
"@kolkrabbi/kol-component": "^0.230.0",
|
|
31
30
|
"@kolkrabbi/kol-icons": "^0.30.0",
|
|
32
|
-
"@kolkrabbi/kol-
|
|
33
|
-
"@kolkrabbi/kol-
|
|
31
|
+
"@kolkrabbi/kol-framework": "^0.48.0",
|
|
32
|
+
"@kolkrabbi/kol-theme": "^0.159.0",
|
|
33
|
+
"@kolkrabbi/kol-component": "^0.230.0"
|
|
34
34
|
},
|
|
35
35
|
"files": [
|
|
36
36
|
"src",
|
package/src/index.js
CHANGED
|
@@ -14,4 +14,6 @@ export * from './tags/index.js'
|
|
|
14
14
|
export * from './exhibit/index.js'
|
|
15
15
|
export * from './compositions/index.js'
|
|
16
16
|
export * from './search/index.js'
|
|
17
|
+
/* retiring — ContentRow replaces it (retirement ledger, 2026-09-30) */
|
|
18
|
+
export { default as ResultRow } from './search/ResultRow.jsx'
|
|
17
19
|
export * from './engine/index.js'
|
package/src/search/ResultRow.jsx
CHANGED
|
@@ -1,33 +1,28 @@
|
|
|
1
|
-
import { Link } from 'react-router-dom'
|
|
2
|
-
|
|
3
1
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* Two variants, each a complete pair — the wash needs its x padding, the underline must not have
|
|
9
|
-
* one (Round 1: x padding without a wash was drawn wrong):
|
|
10
|
-
* `underline` (default) — no ground; the title underlines and the row inks up on hover
|
|
11
|
-
* `wash` — x padding and a ground wash on hover
|
|
12
|
-
*
|
|
13
|
-
* @param {string} to the route the row opens
|
|
14
|
-
* @param {ReactNode} title the result's name (a highlighted node is fine)
|
|
15
|
-
* @param {string} [meta] kind · category · space · date
|
|
16
|
-
* @param {ReactNode} [description]
|
|
17
|
-
* @param {'underline'|'wash'} [variant='underline']
|
|
2
|
+
* @deprecated — use kol-component's `ContentRow` (variant `article`, `media={false}`). ResultRow was
|
|
3
|
+
* minted 2026-09-30 for the search page while the design system already shipped its row; it
|
|
4
|
+
* forwards to ContentRow and drops at 30 days on the retirement ledger. `variant` is ignored.
|
|
18
5
|
*/
|
|
19
|
-
const VARIANTS = {
|
|
20
|
-
underline: { row: 'group flex flex-col gap-1 py-3 opacity-80 hover:opacity-100 focus-visible:opacity-100', title: 'group-hover:underline underline-offset-4' },
|
|
21
|
-
wash: { row: 'flex flex-col gap-1 px-3 py-3 hover:bg-fg-04', title: '' },
|
|
22
|
-
}
|
|
23
6
|
|
|
24
|
-
|
|
25
|
-
|
|
7
|
+
import { useNavigate } from 'react-router-dom'
|
|
8
|
+
import { ContentRow } from '@kolkrabbi/kol-component'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @deprecated — use kol-component's `ContentRow` (variant `article`, `media={false}`). ResultRow was
|
|
12
|
+
* minted 2026-09-30 for the search page while the design system already shipped its row; it
|
|
13
|
+
* forwards to ContentRow and drops at 30 days on the retirement ledger. `variant` is ignored.
|
|
14
|
+
*/
|
|
15
|
+
export default function ResultRow({ to, title, meta, description }) {
|
|
16
|
+
const navigate = useNavigate()
|
|
26
17
|
return (
|
|
27
|
-
<
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
18
|
+
<ContentRow
|
|
19
|
+
variant="article"
|
|
20
|
+
media={false}
|
|
21
|
+
href={to}
|
|
22
|
+
onNavigate={(e) => { e.preventDefault(); navigate(to) }}
|
|
23
|
+
eyebrow={meta}
|
|
24
|
+
title={title}
|
|
25
|
+
body={description}
|
|
26
|
+
/>
|
|
32
27
|
)
|
|
33
28
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { Fragment, useMemo } from 'react'
|
|
2
|
-
import { useSearchParams } from 'react-router-dom'
|
|
3
|
-
import { Button, EmptyState, Input, Pill } from '@kolkrabbi/kol-component'
|
|
4
|
-
import ResultRow from './ResultRow.jsx'
|
|
2
|
+
import { useNavigate, useSearchParams } from 'react-router-dom'
|
|
3
|
+
import { Button, ContentRow, EmptyState, Input, Pill } from '@kolkrabbi/kol-component'
|
|
5
4
|
import { createIndex, search, parseQuery } from '@kolkrabbi/kol-search'
|
|
6
5
|
import { DocHeader, DocSection } from '../docs/DocKit.jsx'
|
|
7
6
|
|
|
@@ -45,7 +44,8 @@ const readToken = (t) => (t.kind === 'term'
|
|
|
45
44
|
? `${t.field} ${t.value}`
|
|
46
45
|
: `${t.negate ? 'not ' : ''}${t.field === 'tags' ? 'tag' : t.field}: ${t.value}${t.via === 'smart' ? ' (from the word)' : ''}`)
|
|
47
46
|
|
|
48
|
-
export default function SearchPage({ items = [], spaces = [], facets = DEFAULT_FACETS, limit = 60
|
|
47
|
+
export default function SearchPage({ items = [], spaces = [], facets = DEFAULT_FACETS, limit = 60 }) {
|
|
48
|
+
const navigate = useNavigate()
|
|
49
49
|
const [params, setParams] = useSearchParams()
|
|
50
50
|
const q = params.get('q') ?? ''
|
|
51
51
|
const index = useMemo(() => createIndex(items), [items])
|
|
@@ -125,12 +125,16 @@ export default function SearchPage({ items = [], spaces = [], facets = DEFAULT_F
|
|
|
125
125
|
<ol className="flex flex-col">
|
|
126
126
|
{out.results.map((r) => (
|
|
127
127
|
<li key={r.item.id} className="border-t border-fg-08 first:border-t-0">
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
128
|
+
{/* THE SHIPPED ROW (2026-09-30): kol-component's ContentRow, the row form of the
|
|
129
|
+
* content-card system — not a row minted here. Article form, no cover. */}
|
|
130
|
+
<ContentRow
|
|
131
|
+
variant="article"
|
|
132
|
+
media={false}
|
|
133
|
+
href={r.item.href}
|
|
134
|
+
onNavigate={(e) => { e.preventDefault(); navigate(r.item.href) }}
|
|
135
|
+
eyebrow={[r.item.kind, r.item.category, r.item.space, r.item.date].filter(Boolean).join(' · ')}
|
|
131
136
|
title={<Highlight text={r.item.title} ranges={r.highlights.title} />}
|
|
132
|
-
|
|
133
|
-
description={r.item.description}
|
|
137
|
+
body={r.item.description}
|
|
134
138
|
/>
|
|
135
139
|
</li>
|
|
136
140
|
))}
|
package/src/search/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import { createContext, useContext, useMemo, useRef, useState, useEffect, Suspen
|
|
|
2
2
|
import { Outlet, Link, useLocation, useNavigate } from 'react-router-dom'
|
|
3
3
|
import { ShellHeader } from '@kolkrabbi/kol-framework'
|
|
4
4
|
import ShellSidebar, { RAIL_FOLD_EVENT } from './ShellSidebar.jsx'
|
|
5
|
-
import { IconFrame, ShellDrawer, ShellSearchOverlay, ShortcutsOverlay, SettingsSections, SettingsSwitch, Tooltip } from '@kolkrabbi/kol-component'
|
|
5
|
+
import { IconFrame, ShellDrawer, ShellSearchOverlay, ShortcutsOverlay, SettingsPanel, SettingsSections, SettingsSwitch, Tooltip, useDragResize } from '@kolkrabbi/kol-component'
|
|
6
6
|
import { createIndex, search } from '@kolkrabbi/kol-search'
|
|
7
7
|
import { useTagMode } from '../tags/TagModeContext.jsx'
|
|
8
8
|
import TagModeOverlay from '../tags/TagModeOverlay.jsx'
|
|
@@ -79,37 +79,27 @@ export const SHELL_SCROLL_ROOT = '#shell-scroll'
|
|
|
79
79
|
* overflow (`.shell-rail`, kol-components-workshop.css): exactly one region
|
|
80
80
|
* tall, a seam toward the page, the chrome inset inside the seam.
|
|
81
81
|
* overflow-x-hidden (2026-07-30): long tree rows must not scroll sideways. */
|
|
82
|
-
/* RESIZABLE RAILS (2026-09-30
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
* token
|
|
86
|
-
* the
|
|
87
|
-
|
|
88
|
-
const
|
|
89
|
-
const
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
99
|
-
return (
|
|
100
|
-
<div
|
|
101
|
-
role="separator"
|
|
102
|
-
aria-orientation="vertical"
|
|
103
|
-
aria-label="Resize rail"
|
|
104
|
-
onPointerDown={start}
|
|
105
|
-
onDoubleClick={() => onChange(null)}
|
|
106
|
-
className={`absolute top-0 hidden h-full w-1.5 cursor-col-resize hover:bg-oq-08 lg:block ${side === 'end' ? 'right-0' : 'left-0'}`}
|
|
107
|
-
/>
|
|
108
|
-
)
|
|
82
|
+
/* RESIZABLE RAILS (2026-09-30) — THE DS GESTURE, not a local one: `useDragResize` (the grab edge
|
|
83
|
+
* SideNav and EditorShell wear — drag resizes, a click or a drag under the snap collapses, arrows
|
|
84
|
+
* step, release near the default snaps back) and the `.kol-rail-grab` pill. Each rail has its own
|
|
85
|
+
* token family (`kol-shell-nav` · `kol-shell-toc`, tokens in kol-framework.css) so the two never
|
|
86
|
+
* drag together; width lasts the session (persistWidth off, the 2026-09-03 ruling). A collapse
|
|
87
|
+
* from the grab is the shell's own "hide the rail" — the same state `[` / `]` toggle. */
|
|
88
|
+
const useRailGrab = (token, side, hide) => {
|
|
89
|
+
const ref = useRef(null)
|
|
90
|
+
const { collapsed, toggleCollapsed, grabProps } = useDragResize(ref, { token, side })
|
|
91
|
+
useEffect(() => {
|
|
92
|
+
if (!collapsed) return
|
|
93
|
+
hide()
|
|
94
|
+
toggleCollapsed() // un-stamp the hook's collapse — the shell's own state now holds it
|
|
95
|
+
document.documentElement.style.removeProperty(`--${token}-w`) // it returns at the stylesheet width
|
|
96
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
97
|
+
}, [collapsed])
|
|
98
|
+
return { ref, grab: <div {...grabProps} className={`kol-rail-grab kol-rail-grab--${side} hidden lg:block`} /> }
|
|
109
99
|
}
|
|
110
100
|
|
|
111
|
-
const NavColumn = ({ children, grip }) => (
|
|
112
|
-
<aside aria-label="Navigation" className="shell-rail shell-rail--nav shell-sidebar-sticky hidden lg:block pt-6 md:pt-6 lg:pt-8 pb-8">
|
|
101
|
+
const NavColumn = ({ children, grip, railRef }) => (
|
|
102
|
+
<aside ref={railRef} aria-label="Navigation" className="shell-rail shell-rail--nav shell-sidebar-sticky hidden lg:block pt-6 md:pt-6 lg:pt-8 pb-8">
|
|
113
103
|
{children}
|
|
114
104
|
{grip}
|
|
115
105
|
</aside>
|
|
@@ -148,8 +138,8 @@ const MainColumn = ({ children, fullHeight, width = 'canvas', padStart, padEnd }
|
|
|
148
138
|
/* `xl`, not `lg` — the grid only declares a third column at xl (gridCols
|
|
149
139
|
* below). The breakpoint here and the one in gridCols are one decision and
|
|
150
140
|
* must not be stated twice differently. */
|
|
151
|
-
const TocColumn = ({ children, grip }) => (
|
|
152
|
-
<aside aria-label="Table of contents" className="shell-rail shell-rail--toc shell-sidebar-sticky hidden xl:block pt-6 md:pt-6 lg:pt-8 pb-8">
|
|
141
|
+
const TocColumn = ({ children, grip, railRef }) => (
|
|
142
|
+
<aside ref={railRef} aria-label="Table of contents" className="shell-rail shell-rail--toc shell-sidebar-sticky hidden xl:block pt-6 md:pt-6 lg:pt-8 pb-8">
|
|
153
143
|
{grip}
|
|
154
144
|
{/* The width is the grid track (--kol-shell-toc-w). An empty rail still
|
|
155
145
|
* holds its column (user ruling 2026-08-01): a rail that disappears
|
|
@@ -195,11 +185,10 @@ const ShellLayout = ({ routes = [], basePath = '/', brand: brandProp, brandLogoS
|
|
|
195
185
|
const [prefs, setPrefs] = useState(readSettings)
|
|
196
186
|
const setPref = (key, value) => setPrefs((p) => { const next = { ...p, [key]: value }; writeSettings(next); return next })
|
|
197
187
|
const [navCollapsed, setNavCollapsed] = useState(() => readSettings().navHidden === true)
|
|
198
|
-
|
|
199
|
-
const [navW, setNavW] = useState(() => readSettings().navW ?? null)
|
|
200
|
-
const [tocW, setTocW] = useState(() => readSettings().tocW ?? null)
|
|
201
|
-
const setRailW = (key, set) => (w) => { set(w); writeSettings({ ...readSettings(), [key]: w }) }
|
|
188
|
+
|
|
202
189
|
const [tocCollapsed, setTocCollapsed] = useState(() => readSettings().tocHidden === true)
|
|
190
|
+
const navGrab = useRailGrab('kol-shell-nav', 'left', () => { setNavCollapsed(true); writeSettings({ ...readSettings(), navHidden: true }) })
|
|
191
|
+
const tocGrab = useRailGrab('kol-shell-toc', 'right', () => { setTocCollapsed(true); writeSettings({ ...readSettings(), tocHidden: true }) })
|
|
203
192
|
const [isSettingsOpen, setIsSettingsOpen] = useState(false)
|
|
204
193
|
/* ONE QUERY (user ruling 2026-08-01). The palette's text used to live here
|
|
205
194
|
* while tags lived in TagModeContext — two states, and therefore two
|
|
@@ -465,10 +454,7 @@ const ShellLayout = ({ routes = [], basePath = '/', brand: brandProp, brandLogoS
|
|
|
465
454
|
<ShellContentWidthContext.Provider value={setContentWidth}>
|
|
466
455
|
<ShellTocCollapsedContext.Provider value={setTocCollapsed}>
|
|
467
456
|
<ShellNavCollapsedContext.Provider value={setNavCollapsed}>
|
|
468
|
-
<div
|
|
469
|
-
className="fixed inset-0 flex flex-col bg-surface-primary text-auto"
|
|
470
|
-
style={{ ...(navW ? { '--kol-shell-nav-w': `${navW}px` } : {}), ...(tocW ? { '--kol-shell-toc-w': `${tocW}px` } : {}) }}
|
|
471
|
-
>
|
|
457
|
+
<div className="fixed inset-0 flex flex-col bg-surface-primary text-auto">
|
|
472
458
|
<ShellHeader
|
|
473
459
|
brand={brand}
|
|
474
460
|
nav={navItems}
|
|
@@ -503,7 +489,7 @@ const ShellLayout = ({ routes = [], basePath = '/', brand: brandProp, brandLogoS
|
|
|
503
489
|
>
|
|
504
490
|
<div className={`shell-content-grid grid ${gridCols}`} data-layout={layoutType} style={{ paddingInline: 'var(--kol-pad-chrome-x)' }}>
|
|
505
491
|
{showNav && (
|
|
506
|
-
<NavColumn
|
|
492
|
+
<NavColumn railRef={navGrab.ref} grip={navGrab.grab}>
|
|
507
493
|
{renderSidebar ? renderSidebar({ activeRoute }) : <ShellSidebar routes={routes} basePath={basePath} />}
|
|
508
494
|
</NavColumn>
|
|
509
495
|
)}
|
|
@@ -521,7 +507,7 @@ const ShellLayout = ({ routes = [], basePath = '/', brand: brandProp, brandLogoS
|
|
|
521
507
|
* the grid's third track so main keeps one width across
|
|
522
508
|
* every route (user ruling 2026-08-01). */}
|
|
523
509
|
{showToc && (
|
|
524
|
-
<TocColumn
|
|
510
|
+
<TocColumn railRef={tocGrab.ref} grip={tocGrab.grab}>
|
|
525
511
|
{effectiveTocContent}
|
|
526
512
|
</TocColumn>
|
|
527
513
|
)}
|
|
@@ -588,13 +574,12 @@ const ShellLayout = ({ routes = [], basePath = '/', brand: brandProp, brandLogoS
|
|
|
588
574
|
* settings page or settings sidebar … to offload some functional
|
|
589
575
|
* settings, search settings, sidenav settings"). A right drawer over
|
|
590
576
|
* the page, the same rows the apps' settings use (SettingsSections). */}
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
</ShellDrawer>
|
|
577
|
+
{/* THE APPROVED DRAWER (2026-09-30): kol-component's `SettingsPanel` — the composition locked
|
|
578
|
+
* 2026-08-27 that media's Display settings and Trash already wear: title header, divided
|
|
579
|
+
* sections. This was a hand-built ShellDrawer + eyebrow + undivided sections. */}
|
|
580
|
+
<SettingsPanel open={isSettingsOpen} variant="drawer" title="Settings" onClose={() => setIsSettingsOpen(false)}>
|
|
581
|
+
<SettingsSections sections={settingsSections} divided />
|
|
582
|
+
</SettingsPanel>
|
|
598
583
|
</div>
|
|
599
584
|
</ShellNavCollapsedContext.Provider>
|
|
600
585
|
</ShellTocCollapsedContext.Provider>
|