@kolkrabbi/kol-workshop 0.31.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 +7 -7
- 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 +38 -14
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",
|
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
"dependencies": {
|
|
15
15
|
"d3": "^7.9.0",
|
|
16
16
|
"@kolkrabbi/kol-search": "^0.2.0",
|
|
17
|
-
"@kolkrabbi/kol-
|
|
18
|
-
"@kolkrabbi/kol-
|
|
17
|
+
"@kolkrabbi/kol-markdown": "^0.1.2",
|
|
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-
|
|
31
|
-
"@kolkrabbi/kol-framework": "^0.
|
|
32
|
-
"@kolkrabbi/kol-theme": "^0.
|
|
33
|
-
"@kolkrabbi/kol-
|
|
30
|
+
"@kolkrabbi/kol-icons": "^0.30.0",
|
|
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,9 +79,29 @@ 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
|
-
|
|
83
|
-
|
|
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`} /> }
|
|
99
|
+
}
|
|
100
|
+
|
|
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">
|
|
84
103
|
{children}
|
|
104
|
+
{grip}
|
|
85
105
|
</aside>
|
|
86
106
|
)
|
|
87
107
|
|
|
@@ -118,8 +138,9 @@ const MainColumn = ({ children, fullHeight, width = 'canvas', padStart, padEnd }
|
|
|
118
138
|
/* `xl`, not `lg` — the grid only declares a third column at xl (gridCols
|
|
119
139
|
* below). The breakpoint here and the one in gridCols are one decision and
|
|
120
140
|
* must not be stated twice differently. */
|
|
121
|
-
const TocColumn = ({ children }) => (
|
|
122
|
-
<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">
|
|
143
|
+
{grip}
|
|
123
144
|
{/* The width is the grid track (--kol-shell-toc-w). An empty rail still
|
|
124
145
|
* holds its column (user ruling 2026-08-01): a rail that disappears
|
|
125
146
|
* re-flows main and the same page ends up at two widths. */}
|
|
@@ -164,7 +185,10 @@ const ShellLayout = ({ routes = [], basePath = '/', brand: brandProp, brandLogoS
|
|
|
164
185
|
const [prefs, setPrefs] = useState(readSettings)
|
|
165
186
|
const setPref = (key, value) => setPrefs((p) => { const next = { ...p, [key]: value }; writeSettings(next); return next })
|
|
166
187
|
const [navCollapsed, setNavCollapsed] = useState(() => readSettings().navHidden === true)
|
|
188
|
+
|
|
167
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 }) })
|
|
168
192
|
const [isSettingsOpen, setIsSettingsOpen] = useState(false)
|
|
169
193
|
/* ONE QUERY (user ruling 2026-08-01). The palette's text used to live here
|
|
170
194
|
* while tags lived in TagModeContext — two states, and therefore two
|
|
@@ -437,6 +461,7 @@ const ShellLayout = ({ routes = [], basePath = '/', brand: brandProp, brandLogoS
|
|
|
437
461
|
isActive={isActive}
|
|
438
462
|
onNavigate={handleNavigate}
|
|
439
463
|
actions={searchTrigger}
|
|
464
|
+
menuBelowLg
|
|
440
465
|
onMenuClick={() => {
|
|
441
466
|
if (window.matchMedia('(min-width: 1024px)').matches) {
|
|
442
467
|
/* Both rails to ONE target state — independent `!p` flips made
|
|
@@ -464,7 +489,7 @@ const ShellLayout = ({ routes = [], basePath = '/', brand: brandProp, brandLogoS
|
|
|
464
489
|
>
|
|
465
490
|
<div className={`shell-content-grid grid ${gridCols}`} data-layout={layoutType} style={{ paddingInline: 'var(--kol-pad-chrome-x)' }}>
|
|
466
491
|
{showNav && (
|
|
467
|
-
<NavColumn>
|
|
492
|
+
<NavColumn railRef={navGrab.ref} grip={navGrab.grab}>
|
|
468
493
|
{renderSidebar ? renderSidebar({ activeRoute }) : <ShellSidebar routes={routes} basePath={basePath} />}
|
|
469
494
|
</NavColumn>
|
|
470
495
|
)}
|
|
@@ -482,7 +507,7 @@ const ShellLayout = ({ routes = [], basePath = '/', brand: brandProp, brandLogoS
|
|
|
482
507
|
* the grid's third track so main keeps one width across
|
|
483
508
|
* every route (user ruling 2026-08-01). */}
|
|
484
509
|
{showToc && (
|
|
485
|
-
<TocColumn>
|
|
510
|
+
<TocColumn railRef={tocGrab.ref} grip={tocGrab.grab}>
|
|
486
511
|
{effectiveTocContent}
|
|
487
512
|
</TocColumn>
|
|
488
513
|
)}
|
|
@@ -549,13 +574,12 @@ const ShellLayout = ({ routes = [], basePath = '/', brand: brandProp, brandLogoS
|
|
|
549
574
|
* settings page or settings sidebar … to offload some functional
|
|
550
575
|
* settings, search settings, sidenav settings"). A right drawer over
|
|
551
576
|
* the page, the same rows the apps' settings use (SettingsSections). */}
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
</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>
|
|
559
583
|
</div>
|
|
560
584
|
</ShellNavCollapsedContext.Provider>
|
|
561
585
|
</ShellTocCollapsedContext.Provider>
|