@kolkrabbi/kol-shell 0.20.0 → 0.22.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kolkrabbi/kol-shell",
3
- "version": "0.20.0",
3
+ "version": "0.22.0",
4
4
  "private": false,
5
5
  "description": "KOL application shell — fixed 48px NavRail + AppShell layout root, PageShell/PageHeader scaffolds, ContentFilters catalog organism, GridCard, SettingsScaffold, WalkthroughPanel, ShortcutsOverlay. App chrome (kol-framework owns site chrome). Nav items, content, shortcuts and settings are consumer-injected. Sits above @kolkrabbi/kol-{theme,component,framework}.",
6
6
  "license": "MIT",
@@ -20,10 +20,10 @@
20
20
  "react-dom": "^18.3.0 || ^19.0.0"
21
21
  },
22
22
  "devDependencies": {
23
- "@kolkrabbi/kol-component": "^0.132.0",
23
+ "@kolkrabbi/kol-component": "^0.133.0",
24
24
  "@kolkrabbi/kol-icons": "^0.25.0",
25
25
  "@kolkrabbi/kol-framework": "^0.35.0",
26
- "@kolkrabbi/kol-theme": "^0.96.0"
26
+ "@kolkrabbi/kol-theme": "^0.97.0"
27
27
  },
28
28
  "files": [
29
29
  "src",
@@ -1,28 +1,72 @@
1
1
  import { useState } from 'react'
2
- import { Divider } from '@kolkrabbi/kol-component'
2
+ import { ContentFilters } from '@kolkrabbi/kol-component'
3
3
  import PageShell from './PageShell.jsx'
4
4
  import PageHeader from './PageHeader.jsx'
5
- import TabStrip from './TabStrip.jsx'
6
5
 
7
6
  /**
8
- * SettingsScaffold — the settings-page idiom both shells re-implemented
9
- * inline: fixed PageShell, tab-driven PageHeader, TabStrip, Divider, then a
10
- * `flex:1 overflow:auto` body. Content is consumer-authored via
11
- * `renderContent(tabValue)` — sections/rows are content, not markup.
7
+ * SettingsScaffold — the settings-page idiom both shells re-implemented inline:
8
+ * fixed PageShell, a PageHeader masthead, one header row, then a scrolling body.
9
+ * Content is consumer-authored via `renderContent` — sections/rows are content,
10
+ * not markup.
12
11
  *
13
- * Building blocks exported for the body: `SettingsSection` (h2 + column) and
14
- * `LabelRow` (the 160px label-column row the de-facto standard both repos
15
- * and monitor's ColorPickerPage share).
12
+ * THE HEADER ROW IS `ContentFilters`, THE ACTUAL ORGANISM (user ruling
13
+ * 2026-08-30, taken from kol-fxr's approved `/settings`). It was a local
14
+ * TabStrip + Divider, which made this a THIRD header shape in the estate beside
15
+ * PageHeader and ContentFilters — on the one page type every app has. fxr hand-
16
+ * wrote a lookalike to the organism's grammar and it drifted inside the hour:
17
+ * the organism's search is `size="md" iconSize={16} fieldHeight={28}`, and a
18
+ * copy passing none of those renders a different pill in the same row one page
19
+ * over. The user caught it as *"2 different content filters in settings and
20
+ * effexor home why"*.
21
+ *
22
+ * A row that looks like ContentFilters must BE ContentFilters.
23
+ *
24
+ * What that buys over the tab strip it replaces: search across the settings rows
25
+ * (`searchKeys` — a row's own label matches, so `loop`, `aspect`, `undo` all
26
+ * land), filter groups, the view strip, and `trailingActions` for a page's own
27
+ * controls. A TabStrip could carry none of it.
28
+ *
29
+ * Body building blocks live in **kol-component**, not here:
30
+ * `LabeledControlSection` (the eyebrow-headed section) and `SettingsRow` (the
31
+ * 160px label column). This file used to export its own `SettingsSection` +
32
+ * `LabelRow` — a second implementation of that pair, and the DS does not ship
33
+ * two of one thing (user ruling 2026-08-30: "dont ship duplicate components").
34
+ * Both retired to `_tmp/2026-08-30-shell-settings-duplicates/`.
35
+ *
36
+ * The DRAWER is the page's other half and is also kol-component's:
37
+ * `SettingsPanel` (ShellDrawer underneath — scrim, Escape, focus trap, scroll
38
+ * lock). Render page and drawer from ONE definition; that is what stops the two
39
+ * surfaces drifting. Put its opener in `header.actions`.
16
40
  *
17
41
  * Shortcuts single-source: both repos hand-maintained the shortcut list twice
18
42
  * (settings + overlay) and both pairs drifted. Feed ONE array to both your
19
- * `ShortcutsOverlay` and a `LabelRow` map here.
43
+ * `ShortcutsOverlay` and `SettingsShortcuts`.
20
44
  *
21
- * @param {Array} props.tabs - `[{ value, label, title, subtitle }]` — title/subtitle feed the header
22
- * @param {Function} props.renderContent - `(tabValue) => node`
23
- * @param {Object} props.header - PageHeader props spread onto the scaffold's header (`voice`, `size`, `titleClass`, `eyebrow`)
45
+ * @param {Array} props.tabs `[{ value, label, title, subtitle }]` — title/subtitle feed the masthead, label the strip
46
+ * @param {string} props.defaultTab
47
+ * @param {Function} props.renderContent `(tabValue, filteredItems) => node` the scrolling body
48
+ * @param {Object} props.header PageHeader props spread onto the masthead (`voice`, `size`, `titleClass`, `eyebrow`, `actions`)
49
+ * @param {string} props.title the filter row's own title (default `Preferences`)
50
+ * @param {Array} props.items rows to search/filter over; omit and the row is chrome only
51
+ * @param {Array} props.filterGroups ContentFilters groups
52
+ * @param {Array} props.searchKeys which keys `items` are searched on
53
+ * @param {ReactNode} props.trailingActions the page's own controls, right of the row
54
+ * @param {string} props.tone forwarded to ContentFilters (`sunken` is fxr's approved page)
55
+ * @param {Object} props.filtersProps escape hatch — anything else the organism takes
24
56
  */
25
- export default function SettingsScaffold({ tabs = [], defaultTab, renderContent, header }) {
57
+ export default function SettingsScaffold({
58
+ tabs = [],
59
+ defaultTab,
60
+ renderContent,
61
+ header,
62
+ title = 'Preferences',
63
+ items,
64
+ filterGroups,
65
+ searchKeys,
66
+ trailingActions,
67
+ tone = 'sunken',
68
+ filtersProps,
69
+ }) {
26
70
  const [tab, setTab] = useState(defaultTab ?? tabs[0]?.value)
27
71
  const active = tabs.find((t) => t.value === tab)
28
72
 
@@ -30,50 +74,30 @@ export default function SettingsScaffold({ tabs = [], defaultTab, renderContent,
30
74
  <PageShell mode="fixed">
31
75
  {/* `header` is spread onto the PageHeader (PageHeaderMonoTitle addendum,
32
76
  * kol-fxr 2026-08-27): a consumer could not reach this title at all —
33
- * `voice="mono"`, `size`, `titleClass`, `eyebrow` all pass through */}
77
+ * `voice="mono"`, `size`, `titleClass`, `eyebrow` all pass through.
78
+ * `actions` is where the drawer opener goes. */}
34
79
  <PageHeader title={active?.title} subtitle={active?.subtitle} {...header} />
35
- <TabStrip
36
- options={tabs}
37
- value={tab}
38
- onChange={setTab}
39
- className="gap-6"
40
- style={{ marginBottom: 24 }}
80
+ <ContentFilters
81
+ tone={tone}
82
+ title={title}
83
+ items={items ?? []}
84
+ totalCount={items?.length ?? 0}
85
+ filterGroups={filterGroups}
86
+ searchKeys={searchKeys}
87
+ trailingActions={trailingActions}
88
+ /* THE TABS ARE THE VIEW STRIP — the same row fxr's SETTINGS and the home
89
+ * page's RECENT / SAVED run on, not a strip of our own. That is the
90
+ * whole point: one row grammar, not a settings dialect of it. */
91
+ viewModeOptions={tabs.length ? tabs : undefined}
92
+ viewMode={tab}
93
+ onViewModeChange={setTab}
94
+ renderItem={(filtered) => (
95
+ <div style={{ flex: 1, overflow: 'auto', paddingTop: 4, paddingBottom: 4 }}>
96
+ {renderContent?.(tab, filtered)}
97
+ </div>
98
+ )}
99
+ {...filtersProps}
41
100
  />
42
- <Divider className="mb-6" />
43
- <div style={{ flex: 1, overflow: 'auto', paddingTop: 4, paddingBottom: 4 }}>
44
- {renderContent?.(tab)}
45
- </div>
46
101
  </PageShell>
47
102
  )
48
103
  }
49
-
50
- /** Section — `h2` header + 8px column, the body block both repos share. */
51
- export function SettingsSection({ title, children }) {
52
- return (
53
- <div>
54
- {/* fg-96 on helper-14 — the tab strip's role (PageHeaderMonoTitle addendum 2,
55
- * kol-fxr 2026-08-27; was fg-80 / helper-16 with no seam) */}
56
- <h2 className="text-fg-96 kol-helper-14" style={{ marginBottom: 16 }}>{title}</h2>
57
- <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
58
- {children}
59
- </div>
60
- </div>
61
- )
62
- }
63
-
64
- /**
65
- * LabelRow — 160px flex-shrink-0 label column + value. `align="center"` for
66
- * control rows (a toggle), `align="baseline"` (default) for text rows.
67
- */
68
- export function LabelRow({ label, align = 'baseline', children }) {
69
- return (
70
- <div style={{ display: 'flex', gap: 12, alignItems: align }}>
71
- {/* 160 wide, but it yields and truncates when the row is squeezed — the label
72
- * is the prose, the value is the datum (SettingsShortcutsComboWrap, 2026-08-27) */}
73
- <span className="text-fg-48 kol-helper-12 truncate" style={{ flex: '0 1 160px', minWidth: 0 }}>{label}</span>
74
- {typeof children === 'string'
75
- ? <span className="text-fg-32 kol-helper-12">{children}</span>
76
- : children}
77
- </div>
78
- )
79
- }
@@ -1,11 +1,17 @@
1
- import { LabelRow } from './SettingsScaffold.jsx'
1
+ import { SettingsRow } from '@kolkrabbi/kol-component'
2
+
3
+ /* Was kol-shell's own `LabelRow`, retired 2026-08-30 — it was a second
4
+ * implementation of kol-component's `SettingsRow` (same 160px label column,
5
+ * same value cell), and the DS does not ship two of one thing. `align="fill"`
6
+ * keeps the combo left-aligned in its cell, which is what LabelRow's default
7
+ * baseline row did. */
2
8
 
3
9
  /**
4
10
  * SettingsShortcuts — the keyboard-shortcuts block of a settings page
5
11
  * (ShellHomeSystem, kol-fxr 2026-08-27): the same `[{ section, items: [{ id,
6
12
  * label, combo }] }]` array `ShortcutsOverlay` takes, laid out six columns ×
7
13
  * two sections per column, filled column-first (user, 2026-08-27) — each
8
- * section an eyebrow (`kol-eyebrow`, strong ink) over LabelRows. A combo is one
14
+ * section an eyebrow (`kol-eyebrow`, strong ink) over SettingsRows. A combo is one
9
15
  * token by nature — the value cell is `whitespace-nowrap` and the label yields
10
16
  * (SettingsShortcutsComboWrap, kol-monitor 2026-08-27: `⌘ K` broke as `⌘` over
11
17
  * `K` in the six-column grid — the cell was squeezed to min-content, not full).
@@ -20,9 +26,9 @@ export default function SettingsShortcuts({ sections = [], comboLabel = (c) => c
20
26
  <div key={section} style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
21
27
  <span className="kol-eyebrow text-strong mb-2">{section}</span>
22
28
  {items.map((k) => (
23
- <LabelRow key={k.id ?? k.label} label={k.label}>
29
+ <SettingsRow key={k.id ?? k.label} label={k.label} align="fill">
24
30
  <span className="text-fg-32 kol-helper-12 whitespace-nowrap">{comboLabel(k.combo)}</span>
25
- </LabelRow>
31
+ </SettingsRow>
26
32
  ))}
27
33
  </div>
28
34
  ))}
package/src/index.js CHANGED
@@ -19,7 +19,7 @@ export { default as PageHeader } from './PageHeader.jsx'
19
19
  * where it always did. This package shipped a recreated duplicate 0.1.0–0.2.0;
20
20
  * retired 2026-08-15, quarantined in _tmp/. Import it from kol-component. */
21
21
  export { default as TabStrip } from './TabStrip.jsx'
22
- export { default as SettingsScaffold, SettingsSection, LabelRow } from './SettingsScaffold.jsx'
22
+ export { default as SettingsScaffold } from './SettingsScaffold.jsx'
23
23
  export { default as WalkthroughPanel } from './WalkthroughPanel.jsx'
24
24
  export { default as ShortcutsOverlay } from './ShortcutsOverlay.jsx'
25
25
  export { default as Logomark } from './Logomark.jsx'