@kolkrabbi/kol-shell 0.47.0 → 0.49.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.47.0",
3
+ "version": "0.49.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",
@@ -14,16 +14,16 @@
14
14
  "peerDependencies": {
15
15
  "@kolkrabbi/kol-component": ">=0.174.0",
16
16
  "@kolkrabbi/kol-icons": ">=0.16.0",
17
- "@kolkrabbi/kol-theme": ">=0.137.0",
17
+ "@kolkrabbi/kol-theme": ">=0.138.0",
18
18
  "gsap": "^3.13.0",
19
19
  "react": "^18.3.0 || ^19.0.0",
20
20
  "react-dom": "^18.3.0 || ^19.0.0"
21
21
  },
22
22
  "devDependencies": {
23
- "@kolkrabbi/kol-component": "^0.176.0",
24
- "@kolkrabbi/kol-framework": "^0.41.0",
25
23
  "@kolkrabbi/kol-icons": "^0.25.0",
26
- "@kolkrabbi/kol-theme": "^0.137.0"
24
+ "@kolkrabbi/kol-component": "^0.177.0",
25
+ "@kolkrabbi/kol-framework": "^0.42.0",
26
+ "@kolkrabbi/kol-theme": "^0.138.0"
27
27
  },
28
28
  "files": [
29
29
  "src",
@@ -1,3 +1,4 @@
1
+ import { useCallback, useRef, useState } from 'react'
1
2
  import { ContentCard, ContentFilters, ContentRow } from '@kolkrabbi/kol-component'
2
3
  import PageShell from './PageShell.jsx'
3
4
  import { PageHeader } from '@kolkrabbi/kol-component'
@@ -52,7 +53,8 @@ import WalkthroughPanel from './WalkthroughPanel.jsx'
52
53
  * `bleed` (default) fills the window — the app tier; `capped` centres on the
53
54
  * framework container — the site tier, so a brand book adopting this page keeps its geometry
54
55
  * @param {ReactNode} trailingActions the header's RIGHT slot (ContentFilters' — MediaLibrary's SELECT / FLAT); never empty beside the divider
55
- * @param {number} minColumn the grid track's floor in px (default 160); a shelf of three decks passes 280 the six-track ceiling still applies
56
+ * @param {number} minColumn the grid track's floor in px (default 160) — a minimum, never a count
57
+ * @param {number} maxColumns the grid's ceiling (default 6, the ruling); a three-deck shelf passes 3 and the tracks share the width
56
58
  * @param {string} cardVariant the grid card's variant, per page (default `catalog`); `toCard` may return `variant` per card
57
59
  * @param {'catalog'|'file'} rowVariant the list row (default `catalog`, the 36px no-thumb row); `file` = the 48px thumb row, for a catalog whose grid shows a cover —
58
60
  * its slots (`date` · `size` · `meta` · `selected`) come from `toCard` like everything else
@@ -103,6 +105,10 @@ export default function CatalogPage({
103
105
  /* the grid track's floor. 160 is a 26-tile catalog's; a shelf of three decks
104
106
  wants ~280 so the cards read at all. A ceiling of 6 tracks still applies. */
105
107
  minColumn = 160,
108
+ /* the ceiling — the six-column ruling is a MAXIMUM; a three-deck shelf says 3
109
+ (kol-client-olina 2026-09-03; user: "why are the cards so small? they could
110
+ use 2 columns each" — raising the floor to force it was backwards) */
111
+ maxColumns = 6,
106
112
  /* the LIST row's variant. `catalog` (default) is the 36px GridCard row — title
107
113
  and detail on one line, no thumb — which is what every app-tier list was
108
114
  ruled on (CatalogPageMonitorParity). A catalog whose grid shows a cover
@@ -124,8 +130,32 @@ export default function CatalogPage({
124
130
  style,
125
131
  }) {
126
132
  const open = walkthrough?.open === true
133
+ /* THE TRACK COUNT, MEASURED (CatalogFilterFirstGroupTrackCount, 2026-09-03).
134
+ * `.kol-catalog-grid` and `.kol-filters-first` both read `--kol-catalog-n`
135
+ * from the page: up to 6 tracks, none narrower than `minColumn`, gap 24 —
136
+ * `min(6, floor((W + 24) / (minColumn + 24)))`, exactly what the old inline
137
+ * `auto-fill` resolved to, now a number the filter row can read too. In JS
138
+ * because the CSS form (`round()` over a length ÷ length) is Chromium-only —
139
+ * theme 0.137.0 rendered ONE column in Firefox. `undefined` until measured
140
+ * → the theme's fallback of 6, the pre-0.137.0 geometry. */
141
+ const [cols, setCols] = useState(null)
142
+ const observer = useRef(null)
143
+ /* a CALLBACK ref: the grid mounts and unmounts with the layout toggle, and a
144
+ * callback ref sees both — an effect keyed on a value out of this scope does
145
+ * not (the first cut referenced `renderItem`'s `layout` here and threw) */
146
+ const gridRef = useCallback((el) => {
147
+ observer.current?.disconnect()
148
+ observer.current = null
149
+ if (!el) return
150
+ const measure = (W) => setCols((c) => { const n = Math.max(1, Math.min(maxColumns, Math.floor((W + 24) / (minColumn + 24)))); return c === n ? c : n })
151
+ measure(el.getBoundingClientRect().width)
152
+ const ro = new ResizeObserver(([entry]) => measure(entry.contentRect.width))
153
+ ro.observe(el)
154
+ observer.current = ro
155
+ }, [minColumn, maxColumns])
156
+
127
157
  return (
128
- <PageShell width={width} className={className} style={{ overflow: 'hidden', '--kol-catalog-min': `${minColumn}px`, ...style }}>
158
+ <PageShell width={width} className={className} style={{ overflow: 'hidden', '--kol-catalog-n': cols ?? undefined, ...style }}>
129
159
  {header && <PageHeader {...header} />}
130
160
  <div style={{ flex: 1, position: 'relative' }}>
131
161
  {open && <WalkthroughPanel steps={walkthrough.steps} iconComponent={walkthrough.iconComponent} />}
@@ -161,10 +191,10 @@ export default function CatalogPage({
161
191
  * six-column ruling — below the ceiling the hide-set is
162
192
  * desktop-only geometry. */
163
193
  return (
164
- /* the catalog grid is a CLASS (`.kol-catalog-grid`, kol-theme ≥0.137.0) so the
165
- * filter row's first group can read the track count it renders; `minColumn`
166
- * reaches it as `--kol-catalog-min` on the page (set on PageShell above) */
167
- <div className={layout === 'list' ? undefined : 'kol-catalog-grid'} style={layout === 'list' ? (listLayout === 'stack' ? { display: 'flex', flexDirection: 'column', gap: 8 } : { display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(min(100%, max(240px, calc((100% - 3 * 8px) / 4))), 1fr))', gap: 8 }) : undefined}>
194
+ /* the catalog grid is a CLASS (`.kol-catalog-grid`, kol-theme ≥0.138.0) reading
195
+ * `--kol-catalog-n`, the count measured above and set on PageShell the same
196
+ * number the filter row's first group reads */
197
+ <div ref={layout === 'list' ? undefined : gridRef} className={layout === 'list' ? undefined : 'kol-catalog-grid'} style={layout === 'list' ? (listLayout === 'stack' ? { display: 'flex', flexDirection: 'column', gap: 8 } : { display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(min(100%, max(240px, calc((100% - 3 * 8px) / 4))), 1fr))', gap: 8 }) : undefined}>
168
198
  {rows.map((item, i) => {
169
199
  if (hidden.has(i)) return null
170
200
  const c = cards[i]