@kolkrabbi/kol-component 0.232.0 → 0.234.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.
Files changed (91) hide show
  1. package/package.json +5 -5
  2. package/src/atoms/ActionButton.jsx +1 -1
  3. package/src/atoms/AnimatedTitle.jsx +1 -1
  4. package/src/atoms/AudioPlayer.jsx +1 -1
  5. package/src/atoms/Avatar.jsx +1 -1
  6. package/src/atoms/Badge.jsx +1 -1
  7. package/src/atoms/Figure.jsx +1 -1
  8. package/src/atoms/FileIcon.jsx +1 -1
  9. package/src/atoms/Kbd.jsx +1 -1
  10. package/src/atoms/Pill.jsx +1 -1
  11. package/src/atoms/Tag.jsx +12 -1
  12. package/src/atoms/Textarea.jsx +2 -2
  13. package/src/atoms/ToggleSwitch.jsx +1 -1
  14. package/src/atoms/XYPad.jsx +1 -1
  15. package/src/molecules/AlignmentGrid.jsx +1 -1
  16. package/src/molecules/AudioSheet.jsx +1 -1
  17. package/src/molecules/CodeBlock.jsx +1 -1
  18. package/src/molecules/ColorRamp.jsx +1 -1
  19. package/src/molecules/ContentItem.jsx +1 -1
  20. package/src/molecules/ContentRow.jsx +1 -1
  21. package/src/molecules/CopyButton.jsx +1 -1
  22. package/src/molecules/DocFrontmatter.jsx +1 -1
  23. package/src/molecules/DocPage.jsx +1 -1
  24. package/src/molecules/DocsToc.jsx +1 -1
  25. package/src/molecules/Dropdown.jsx +1 -1
  26. package/src/molecules/DropdownTagFilter.jsx +1 -0
  27. package/src/molecules/EmptyState.jsx +1 -1
  28. package/src/molecules/FieldRow.jsx +1 -2
  29. package/src/molecules/ImageBlock.jsx +1 -1
  30. package/src/molecules/KindPreview.jsx +1 -1
  31. package/src/molecules/MediaTile.jsx +2 -2
  32. package/src/molecules/MenuPopover.jsx +1 -1
  33. package/src/molecules/OptionRow.jsx +29 -20
  34. package/src/molecules/PageHeader.jsx +1 -1
  35. package/src/molecules/PlaybackBar.jsx +1 -1
  36. package/src/molecules/ProfileCard.jsx +1 -1
  37. package/src/molecules/QuantityInput.jsx +1 -1
  38. package/src/molecules/QuickLookFrame.jsx +1 -1
  39. package/src/molecules/RowMenuButton.jsx +1 -1
  40. package/src/molecules/SectionText.jsx +1 -1
  41. package/src/molecules/ShapeDropdown.jsx +1 -1
  42. package/src/molecules/ShellDrawer.jsx +1 -1
  43. package/src/molecules/SortControls.jsx +1 -1
  44. package/src/molecules/SpecList.jsx +1 -1
  45. package/src/molecules/SplitToolButton.jsx +2 -2
  46. package/src/molecules/Stepper.jsx +1 -1
  47. package/src/molecules/SwatchControls.jsx +2 -2
  48. package/src/molecules/TabsRow.jsx +1 -1
  49. package/src/molecules/TiltBento.jsx +1 -1
  50. package/src/molecules/VideoBlock.jsx +1 -1
  51. package/src/molecules/VideoSheet.jsx +1 -1
  52. package/src/organisms/Canvas.jsx +3 -3
  53. package/src/organisms/ColumnBrowser.jsx +14 -8
  54. package/src/organisms/ContentCollection.jsx +1 -1
  55. package/src/organisms/CurveEditor.jsx +1 -1
  56. package/src/organisms/DocumentEditor.jsx +1 -1
  57. package/src/organisms/FeaturedCarousel.jsx +1 -1
  58. package/src/organisms/FramedMediaBand.jsx +1 -1
  59. package/src/organisms/GalleryCarousel.jsx +1 -1
  60. package/src/organisms/KeyframeEditor.jsx +1 -1
  61. package/src/organisms/LayerStack.jsx +2 -2
  62. package/src/organisms/MediaLibrary.jsx +2 -2
  63. package/src/organisms/MediaLibraryExplorer.jsx +1 -1
  64. package/src/organisms/MediaLibraryPages.jsx +16 -14
  65. package/src/organisms/MediaTileGallery.jsx +1 -1
  66. package/src/organisms/RecordManager.jsx +1 -1
  67. package/src/organisms/SectionCards.jsx +1 -1
  68. package/src/organisms/SectionCta.jsx +1 -1
  69. package/src/organisms/SectionFaq.jsx +1 -1
  70. package/src/organisms/SectionHero.jsx +1 -1
  71. package/src/organisms/SectionNewsletter.jsx +1 -1
  72. package/src/organisms/SectionSplit.jsx +1 -1
  73. package/src/organisms/SettingsPanel.jsx +8 -8
  74. package/src/organisms/ShellSearchOverlay.jsx +1 -1
  75. package/src/organisms/ShortcutsOverlay.jsx +1 -1
  76. package/src/organisms/SpectrumControls.jsx +4 -4
  77. package/src/organisms/SpectrumGrid.jsx +1 -1
  78. package/src/organisms/ToolPalette.jsx +1 -1
  79. package/src/utilities/AsciiCursor.jsx +1 -1
  80. package/src/utilities/AssetGrid.jsx +1 -1
  81. package/src/utilities/ButtonGroup.jsx +1 -1
  82. package/src/utilities/CropOverlay.jsx +1 -1
  83. package/src/utilities/CurveOverlay.jsx +1 -1
  84. package/src/utilities/ErrorBoundary.jsx +1 -1
  85. package/src/utilities/ExitPreview.jsx +1 -1
  86. package/src/utilities/FullscreenOverlay.jsx +1 -1
  87. package/src/utilities/OverlayGlassPanel.jsx +1 -1
  88. package/src/utilities/Popover.jsx +1 -1
  89. package/src/utilities/ProsePreview.jsx +1 -1
  90. package/src/utilities/TiltCard.jsx +1 -1
  91. package/src/utilities/TransparentX.jsx +1 -1
@@ -4,7 +4,7 @@ import { PopoverPanel, usePopover, Tooltip } from '../utilities/Popover.jsx'
4
4
  import { glyphSize } from '../hooks/glyphLadders.js'
5
5
 
6
6
  /**
7
- * SplitToolButton — single-trigger split tool button + variant menu (the
7
+ * SplitToolButton — A tool button with a variant menu. single-trigger split tool button + variant menu (the
8
8
  * tool-palette idiom: Select · Text · [Shape ◢] · Pattern). A pinned-square
9
9
  * quiet/pressed trigger shows the current variant while the group is `active`
10
10
  * (else the `lastPicked` variant) plus a corner fold indicator; ONE click both
@@ -65,7 +65,7 @@ import { glyphSize } from '../hooks/glyphLadders.js'
65
65
  * @param {string} props.className - Additional classes on the trigger
66
66
  */
67
67
 
68
- /* Corner fold marker — `fold-indicator`, promoted into kol-icon-set-v1
68
+ /* Corner fold marker — `fold-indicator`, promoted into kol-icon-set-interface
69
69
  * 2026-09-03 from kol-fxr's own drawing (editor-set-is-behind-its-source); the
70
70
  * comment that stood here said it did not exist yet and should be promoted, so
71
71
  * this is that. Lives inside the button so it dims with kol-btn-quiet and
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Stepper — number input + chevron buttons, built on the .kol-control shell.
2
+ * Stepper — A number input with step buttons. number input + chevron buttons, built on the .kol-control shell.
3
3
  *
4
4
  * size="xs" / "sm" (default) / "md" / "lg" — matched padding + type class.
5
5
  * Chevron scale follows the size: 6 / 8 / 10 / 12 px each, stacked. xs is the
@@ -92,7 +92,7 @@ export function SwatchStack({
92
92
  }
93
93
 
94
94
  /**
95
- * EyedropPick — eyedropper icon button + a sample chip of the sampled color.
95
+ * EyedropPick — Sample a color from the screen. eyedropper icon button + a sample chip of the sampled color.
96
96
  * The button is hidden when the browser EyeDropper API is unavailable (no
97
97
  * affordance for an action that can't run); when supported but unusable pass
98
98
  * `disabled` to dim it. `onPick` is the app seam where EyeDropper + canvas
@@ -173,7 +173,7 @@ function NoneMarker({ onClear }) {
173
173
  }
174
174
 
175
175
  /**
176
- * SwatchControls — the composed colour-panel top row: SwatchStack (fill /
176
+ * SwatchControls — Fill and stroke swatches with an eyedropper. The composed colour-panel top row: SwatchStack (fill /
177
177
  * stroke paint chips + swap + none) alongside EyedropPick (eyedropper +
178
178
  * sample chip). Fully controlled and store-free; the app owns the swap, clear,
179
179
  * and pick handlers.
@@ -5,7 +5,7 @@ import { Icon } from '@kolkrabbi/kol-icons'
5
5
  /* taxonomy-ok: nests kol-icons's Icon */
6
6
 
7
7
  /**
8
- * TabsRow — labeled underline tab strip: text tabs where the active tab gets
8
+ * TabsRow — Text tabs with an underline. labeled underline tab strip: text tabs where the active tab gets
9
9
  * a 2px bottom underline and emphasis color; inactive tabs are muted and
10
10
  * brighten on hover. Optional leading close button and trailing minimise
11
11
  * chevron render only when their handlers are passed — no outer chrome, the
@@ -64,7 +64,7 @@ function Media({ src, poster, className }) {
64
64
  }
65
65
 
66
66
  /**
67
- * TiltBento — media hover-card for grid/bento walls, the Tilt family's composed
67
+ * TiltBento — A tilting media card for bento walls. media hover-card for grid/bento walls, the Tilt family's composed
68
68
  * tile (was `BentoCard` until 2026-08-27, user ruling: the three tilting things
69
69
  * in the estate are ONE prefix family — `TiltCard` the bare frame, `TiltBento`
70
70
  * this tile, `useTilt` the one hook; `BentoCard` is the alias on the retirement
@@ -15,7 +15,7 @@ export function getEmbedUrl(url) {
15
15
  }
16
16
 
17
17
  /**
18
- * VideoBlock — a captioned prose video: the DS Figure shell (optional label,
18
+ * VideoBlock — A captioned video for prose. a captioned prose video: the DS Figure shell (optional label,
19
19
  * aspect-locked bordered frame, optional figcaption) wrapping either an
20
20
  * <iframe> embed (YouTube/Vimeo, auto-parsed from `url`) or a native <video>
21
21
  * file player. Embed wins when `url` parses; otherwise the `file` player runs.
@@ -5,7 +5,7 @@ import QuickLookFrame from './QuickLookFrame.jsx'
5
5
  /* taxonomy-ok: molecule — nests PlaybackBar + QuickLookFrame (relative). */
6
6
 
7
7
  /**
8
- * VideoSheet — video in the Quick Look window: the frame at the video's own aspect ratio, the
8
+ * VideoSheet — A video in the preview window. video in the Quick Look window: the frame at the video's own aspect ratio, the
9
9
  * QuickTime bar docked in the window's footer (2026-09-23 — it floated over the picture, which
10
10
  * made video the one kind whose controls sat on the media). No native controls; click on the
11
11
  * video toggles play; no autoplay (user 2026-09-23 — Quick Look opens paused), `playsInline`, `preload="metadata"`.
@@ -419,7 +419,7 @@ function isTypingTarget(el) {
419
419
  }
420
420
 
421
421
  /**
422
- * PanZoomViewport — infinite-canvas viewport (pan + zoom), and the one that
422
+ * PanZoomViewport — An infinite canvas to pan and zoom. infinite-canvas viewport (pan + zoom), and the one that
423
423
  * publishes `CanvasZoomContext`.
424
424
  *
425
425
  * Pan: hold Space + drag (cursor grab/grabbing), or two-finger trackpad
@@ -787,7 +787,7 @@ export function useFrameGeom(containerRef, virtualWidth = CANVAS_VIRTUAL_W) {
787
787
  }
788
788
 
789
789
  /**
790
- * CanvasRuler — top + left rulers in virtual-canvas px, mapped through the
790
+ * CanvasRuler — Rulers along the canvas edges. top + left rulers in virtual-canvas px, mapped through the
791
791
  * measured frame geometry (see useFrameGeom).
792
792
  *
793
793
  * Dragging off a ruler starts a new guide: the ruler only ANNOUNCES the
@@ -900,7 +900,7 @@ function GuideLine({ axis, screenPos, interactive, onGrab }) {
900
900
  }
901
901
 
902
902
  /**
903
- * CanvasGuides — ruler guides rendered at the viewport level so each line
903
+ * CanvasGuides — Guide lines across the whole canvas. ruler guides rendered at the viewport level so each line
904
904
  * spans the entire visible canvas area instead of clipping to the letterbox
905
905
  * frame. Positions are stored in virtual canvas px and threaded down as props
906
906
  * — the viewport is chrome and owns no guide state; the screen mapping is the
@@ -12,7 +12,7 @@ import useMarquee from '../hooks/useMarquee.js'
12
12
  import { GRAB_COLUMN } from '../utilities/motion.js'
13
13
 
14
14
  /**
15
- * ColumnBrowser — Finder-style Miller columns over a flat key space (kol-r2b2's
15
+ * ColumnBrowser — Finder-style columns over a bucket. Finder-style Miller columns over a flat key space (kol-r2b2's
16
16
  * `src/ColumnBrowser.jsx`, built and ruled there first — user 2026-08-27: "make
17
17
  * it locally first, then ship it" — and shipped verbatim; ticket ColumnBrowser).
18
18
  * The listing surface's FOLDER view: it replaces the folder rows above the
@@ -249,7 +249,7 @@ const ZONE_BOX = 44
249
249
  * without the stack: the columns pass none, so a desktop row is byte-identical
250
250
  * to what it was. */
251
251
  function Row({
252
- icon, label, active, cursor = false, trailing, onClick, muted = false, size = 'md',
252
+ icon, label, active, trail = false, cursor = false, trailing, onClick, muted = false, size = 'md',
253
253
  indent = 0, meta, zones = false, thumb, onDisclose, disclosed, onContextMenu, drop, dropFiles, markKey,
254
254
  }) {
255
255
  const [over, setOver] = useState(false)
@@ -275,15 +275,18 @@ function Row({
275
275
  },
276
276
  } : {}
277
277
  /* THE DESKTOP ROW IS AN OptionRow (2026-09-30) — the palette's row, on the control ramp, so a size
278
- * setting scales height, glyph and type together. The fills stay this file's CSS (trail vs the
279
- * deepest column's selection is decided by column there, and hover/cursor paint nothing — the
280
- * 2026-09-02 ruling), so `fill={false}`. The `zones` row below is the phone's and keeps its own. */
278
+ * setting scales height and glyph; the name keeps ONE type in every view (`kol-item-name`, the
279
+ * grid's), as Finder does. The deepest selection takes the pressed (white) fill, the folders on the
280
+ * way to it the trail; hover and the bare cursor paint nothing (the 2026-09-02 ruling). The `zones`
281
+ * row below is the phone's and keeps its own. */
281
282
  if (!zones) return (
282
283
  <OptionRow
283
284
  as="li"
284
285
  size={size}
285
- fill={false}
286
- selected={active}
286
+ type="kol-item-name"
287
+ hover={false}
288
+ selected={active && !trail}
289
+ trail={active && trail}
287
290
  muted={muted && !cursor}
288
291
  icon={icon}
289
292
  label={label}
@@ -1039,8 +1042,11 @@ export default function ColumnBrowser({
1039
1042
  label={f.replace(/\/$/, '')}
1040
1043
  markKey={level + f}
1041
1044
  active={f === activeFolder || isPicked(level + f)}
1045
+ /* a folder on the path is the TRAIL unless it is the deepest pick — the column after
1046
+ * it is its own contents with nothing chosen, and no file is showing */
1047
+ trail={f === activeFolder && !isPicked(level + f) && (!!shown || k < levels.length - 2)}
1042
1048
  cursor={cursorActive && cursor.col === k && cursor.idx === i}
1043
- trailing={<Icon name="chevron-right" size={12} className="text-oq-32" />}
1049
+ trailing={<Icon name="chevron-right" size={12} className={f === activeFolder && !shown && k === levels.length - 2 ? '' : 'text-oq-32'} />}
1044
1050
  onContextMenu={onRowContextMenu && ((e) => onRowContextMenu(e, { type: 'folder', path: level + f }))}
1045
1051
  drop={dragFor?.(level + f)}
1046
1052
  dropFiles={onDropFiles ? (list) => onDropFiles(list, level + f) : undefined}
@@ -1,7 +1,7 @@
1
1
  import usePrefersReducedMotion from '../hooks/usePrefersReducedMotion.js'
2
2
 
3
3
  /**
4
- * ContentCollection — the container half of the content-card system: the
4
+ * ContentCollection — A grid or list of content items. the container half of the content-card system: the
5
5
  * grid/list switch plus the motion that belongs to it. Animation lives in
6
6
  * the wrapper — the only place it can (06-content-card-system.md §6);
7
7
  * cards never animate themselves.
@@ -109,7 +109,7 @@ function TermRow({ term, onSet, onRemove, canRemove }) {
109
109
  }
110
110
 
111
111
  /**
112
- * CurveEditor — curve authoring: a kind picker, per-kind ranges and
112
+ * CurveEditor — Author a curve by kind and expression. curve authoring: a kind picker, per-kind ranges and
113
113
  * EXPRESSION fields, and the epicycle term list.
114
114
  *
115
115
  * Lifted from kol-fxr's editor (`compose/inspectors/CurveEditor.jsx`,
@@ -11,7 +11,7 @@ import { splitFrontmatter, joinFrontmatter } from '@kolkrabbi/kol-markdown'
11
11
  import { readDraft, writeDraft, clearDraft } from '../utilities/localDrafts.js'
12
12
 
13
13
  /**
14
- * DocumentEditor — write a text file: open one, or make a new one (media D1 plan v2, 2026-09-26).
14
+ * DocumentEditor — Open or write a text file. write a text file: open one, or make a new one (media D1 plan v2, 2026-09-26).
15
15
  *
16
16
  * The concept is kol-olina's brand notes page (NoteEdit): a title, a markdown body, Write / Preview,
17
17
  * attachments from the bucket, a page for writing something NEW rather than a button hidden in a
@@ -85,7 +85,7 @@ function SlideMedia({ media, active = true, onEnded, onTimeUpdate }) {
85
85
  * the one hero (SectionHeroRound2); this stays as its engine and renders
86
86
  * unchanged. Removed from the barrel at the next major.
87
87
  *
88
- * FeaturedCarousel — a full-width carousel of featured media: each wide slide
88
+ * FeaturedCarousel — A full-width carousel of featured media. a full-width carousel of featured media: each wide slide
89
89
  * is a fixed-height frame with an image or HLS-video background and a centered
90
90
  * OverlayGlassPanel (title / description / CTA), plus prev/next and optional
91
91
  * autoplay with a progress bar.
@@ -1,7 +1,7 @@
1
1
  import Image from '../atoms/Image'
2
2
 
3
3
  /**
4
- * FramedMediaBand — full-width media breather band: a centered, aspect-locked
4
+ * FramedMediaBand — A full-width band with one framed image. full-width media breather band: a centered, aspect-locked
5
5
  * frame (bordered surface-secondary panel, rounded) holding one object-cover
6
6
  * image. Extracted from the foundry TypefacePage, where it sat inlined 5x
7
7
  * verbatim between content sections. The double radius (`rounded` panel,
@@ -9,7 +9,7 @@ import MediaViewer from './MediaViewer.jsx'
9
9
  const DRAG_THRESHOLD_SQ = 25
10
10
 
11
11
  /**
12
- * GalleryCarousel — the project-detail media gallery: a horizontal, drag-scroll
12
+ * GalleryCarousel — A shelf of media that opens fullscreen. the project-detail media gallery: a horizontal, drag-scroll
13
13
  * shelf of image/video tiles where clicking a tile opens THE shared fullscreen
14
14
  * MediaViewer starting at that tile.
15
15
  *
@@ -22,7 +22,7 @@ export const DEFAULT_KEYFRAMES = [
22
22
  ]
23
23
 
24
24
  /**
25
- * KeyframeEditor — a keyframe list over a pose track, kept sorted by `t`.
25
+ * KeyframeEditor — A sorted list of keyframes. a keyframe list over a pose track, kept sorted by `t`.
26
26
  *
27
27
  * { t: 0..1, rot: [x, y, z] RADIANS, pos: [x, y, z], scale, ease }
28
28
  *
@@ -207,7 +207,7 @@ function CanvasRow({ IconC, active, collapsed, onToggleCollapse, onSelect }) {
207
207
  }
208
208
 
209
209
  /**
210
- * LayerStack — the layers panel: a z-stacked tree of rows with HTML5 drag to
210
+ * LayerStack — The layers panel. the layers panel: a z-stacked tree of rows with HTML5 drag to
211
211
  * reorder AND reparent in one gesture, hover-revealed eye + lock toggles,
212
212
  * inline rename, collapsible containers, and a Canvas root row above it all.
213
213
  *
@@ -440,7 +440,7 @@ export default function LayerStack({
440
440
  }
441
441
 
442
442
  /**
443
- * AddLayerButton — the `+` that opens a menu of layer types. Lives in the
443
+ * AddLayerButton — Add a layer from a menu. the `+` that opens a menu of layer types. Lives in the
444
444
  * panel's tab row, not the stack footer (the source's placement). One entry
445
445
  * may expand inline to a kind picker so adding "a shape" doesn't silently
446
446
  * default to the first kind.
@@ -19,7 +19,7 @@ import { SettingsChipRow, chipCls } from './SettingsPanel.jsx'
19
19
  import { filterMedia } from '../utilities/mediaSearch.js'
20
20
 
21
21
  /**
22
- * MediaLibrary — a browser over an object bucket, in two views over one
22
+ * MediaLibrary — Browse and pick files from a bucket. a browser over an object bucket, in two views over one
23
23
  * headless core. Consolidates four consumer forks (kol-ds-fxr, kol-labs-single,
24
24
  * kol-client-kolkrabbi, kol-website/brand — 9 files, ~1542 lines) that had
25
25
  * already diverged: only fxr carried the canvas-taint fix, only labs carried
@@ -263,7 +263,7 @@ function sortFiles(files, { by, dir }) {
263
263
  }
264
264
 
265
265
  /**
266
- * MediaLibraryProvider — the headless core: one list call, then a
266
+ * MediaLibraryProvider — The data core behind the media views. the headless core: one list call, then a
267
267
  * PREFIX-SCOPED view over it: the folders directly under the prefix, the
268
268
  * files at that level (or the whole subtree in `flat`), the kind allow-list,
269
269
  * the search, the sort with direction, and paging.
@@ -4,7 +4,7 @@ import { useModal } from '../molecules/Modal.jsx'
4
4
  import useMediaQuery from '../hooks/useMediaQuery.js'
5
5
 
6
6
  /**
7
- * MediaLibraryExplorer — the media surface, and since 2026-09-22 a thin name for it.
7
+ * MediaLibraryExplorer — The media browser. the media surface, and since 2026-09-22 a thin name for it.
8
8
  *
9
9
  * IT USED TO BE THE MERGE ITSELF: it held a BROWSE · FILES switch and mounted one of two pages,
10
10
  * each of which listed the bucket, drew its own header and printed its own count. The user's read
@@ -1,6 +1,6 @@
1
1
  import { Fragment, createContext, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'
2
2
  import formatSize from '../utilities/formatSize.js'
3
- import { ROW_HEIGHT, ROW_TYPE } from '../molecules/OptionRow.jsx'
3
+ import { ROW_HEIGHT, ROW_FILL } from '../molecules/OptionRow.jsx'
4
4
  import { glyphSize } from '../hooks/glyphLadders.js'
5
5
  import { Icon } from '@kolkrabbi/kol-icons'
6
6
  import Button from '../atoms/Button.jsx'
@@ -303,7 +303,9 @@ function TagEditor({ tags = [], onChange, suggestions = [] }) {
303
303
 
304
304
  /* THE ROWS VIEW'S STEP ON THE CONTROL RAMP (user 2026-09-30) — the same `rowSize` the columns take,
305
305
  * handed down by context rather than threaded through every subtree level. These rows keep their
306
- * own anatomy (twisty, drag hit, table columns) and take only the ramp: box, type, glyph. */
306
+ * own anatomy (twisty, drag hit, table columns) and take the ramp's box and glyph, OptionRow's
307
+ * fills (picked = the pressed white, the folder you are in = the trail) and the one item type,
308
+ * `kol-item-name`, that the columns and the grid use. */
307
309
  const RowSize = createContext('md')
308
310
 
309
311
  /* `depth` indents a row under the folder it was expanded from; `expanded`/`onToggle` draw the
@@ -341,7 +343,7 @@ function FolderRow({ name, icon = 'folder', onClick, onDoubleClick, depth = 0, e
341
343
  return (
342
344
  /* THE COLUMN BROWSER'S ROW (user 2026-09-22): `kol-column-browser-row` carries the 4px pill
343
345
  * inset, the selected fill and the drop fill; no divider, as the columns have none. */
344
- <li onContextMenu={onContextMenu} {...dropProps} data-marquee-key={markKey} data-hit-zone data-drop-over={over || undefined} className={`kol-column-browser-row flex items-center gap-3 border border-transparent px-3 ${ROW_HEIGHT[size]} transition-colors${selected ? ' is-selected' : ''}${current ? ' is-current' : ''}`} style={{ paddingLeft: depth * 20 + 12 }} onClick={onClick} onDoubleClick={onDoubleClick}>
346
+ <li onContextMenu={onContextMenu} {...dropProps} data-marquee-key={markKey} data-hit-zone data-drop-over={over || undefined} className={`kol-column-browser-row flex items-center gap-3 border border-transparent px-3 ${ROW_HEIGHT[size]} transition-colors${selected ? ` is-selected ${ROW_FILL.selected}` : ''}${current ? ' is-current' : ''}${current && !selected ? ` ${ROW_FILL.trail}` : ''}`} style={{ paddingLeft: depth * 20 + 12 }} onClick={onClick} onDoubleClick={onDoubleClick}>
345
347
  {onToggle ? (
346
348
  <button type="button" aria-label={expanded ? `Collapse ${name}` : `Expand ${name}`} aria-expanded={!!expanded}
347
349
  onClick={(e) => { e.stopPropagation(); onToggle() }}
@@ -353,11 +355,11 @@ function FolderRow({ name, icon = 'folder', onClick, onDoubleClick, depth = 0, e
353
355
  {/* THE FOLDER GLYPH IS THE ONE ICON LEFT, so it fills its box (user 2026-09-23: *"only folder
354
356
  should use icon, and it should be bigger"*). Every other kind now renders its own bytes. */}
355
357
  <span data-hit {...hitDrag} className="kol-row-hit flex items-center gap-3 min-w-0">
356
- <span className="shrink-0 flex items-center justify-center text-oq-48"><Icon name={icon} size={glyphSize(size)} /></span>
358
+ <span className={`shrink-0 flex items-center justify-center${selected ? '' : ' text-oq-48'}`}><Icon name={icon} size={glyphSize(size)} /></span>
357
359
  {/* NO TRAILING SLASH (user 2026-09-22). `partition` hands folders back as `audio/` because a
358
360
  slash is what marks one in a flat key space; that is storage's spelling, not a label, and
359
361
  `ColumnBrowser` has always stripped it. The glyph says "folder". */}
360
- <span className={`${ROW_TYPE[size]} min-w-0 truncate text-fg-default`}>{name.replace(/\/$/, '')}</span>
362
+ <span className={`kol-item-name min-w-0 truncate${selected ? '' : ' text-fg-default'}`}>{name.replace(/\/$/, '')}</span>
361
363
  </span>
362
364
  <span className="flex-1" />
363
365
  {/* THE FOLDER'S COUNT SITS IN THE DATE COLUMN and the size column takes a dash — the columns
@@ -365,8 +367,8 @@ function FolderRow({ name, icon = 'folder', onClick, onDoubleClick, depth = 0, e
365
367
  promised a click that opens, and a click now SELECTS (opening is a double-click). */}
366
368
  {cols && <>
367
369
  {cols.tags != null && <span className="shrink-0" style={{ width: cols.tags }} />}
368
- <span className="kol-mono-12 text-fg-32 shrink-0 truncate" style={{ width: cols.date }}>{meta}</span>
369
- <span className="kol-mono-12 text-fg-24 shrink-0 truncate" style={{ width: cols.size }}>—</span>
370
+ <span className={`kol-item-name ${selected ? "opacity-60" : "text-fg-32"} shrink-0 truncate`} style={{ width: cols.date }}>{meta}</span>
371
+ <span className={`kol-item-name ${selected ? "opacity-60" : "text-fg-24"} shrink-0 truncate`} style={{ width: cols.size }}>—</span>
370
372
  </>}
371
373
  <RowMenuButton onOpen={onContextMenu} className="-my-1 -mr-1" />
372
374
  </li>
@@ -430,21 +432,21 @@ function FileRow({ o, onClick, onDoubleClick, depth = 0, formatDate, thumb, cols
430
432
  const size = useContext(RowSize)
431
433
  const dragProps = drag ? { draggable: true, onDragStart: (e) => { e.stopPropagation(); drag.onDragStart(e, o.key) } } : {}
432
434
  return (
433
- <li onContextMenu={onContextMenu} data-marquee-key={markKey ?? o.key} data-hit-zone className={`kol-column-browser-row flex items-center gap-3 border border-transparent px-3 ${ROW_HEIGHT[size]} transition-colors${selected ? ' is-selected' : ''}`} style={{ paddingLeft: depth * 20 + 12 }} onClick={onClick} onDoubleClick={onDoubleClick}>
435
+ <li onContextMenu={onContextMenu} data-marquee-key={markKey ?? o.key} data-hit-zone className={`kol-column-browser-row flex items-center gap-3 border border-transparent px-3 ${ROW_HEIGHT[size]} transition-colors${selected ? ` is-selected ${ROW_FILL.selected}` : ''}`} style={{ paddingLeft: depth * 20 + 12 }} onClick={onClick} onDoubleClick={onDoubleClick}>
434
436
  <span className="w-4 shrink-0" />
435
437
  {/* the hit — icon and name, as in the folder row above */}
436
438
  <span data-hit {...dragProps} className="kol-row-hit flex items-center gap-3 min-w-0">
437
- <span className="shrink-0 flex items-center justify-center text-oq-48 overflow-hidden rounded" style={{ width: glyphSize(size), height: glyphSize(size) }}>
439
+ <span className={`shrink-0 flex items-center justify-center overflow-hidden rounded${selected ? '' : ' text-oq-48'}`} style={{ width: glyphSize(size), height: glyphSize(size) }}>
438
440
  {/* a file with no thumbnail draws its glyph at the FOLDER's size — one icon column (user 2026-09-25) */}
439
441
  {thumb ?? <Icon name={kindOf(o) === 'image' ? 'image' : 'file'} size={glyphSize(size)} />}
440
442
  </span>
441
- <span className={`${ROW_TYPE[size]} min-w-0 truncate text-fg-default`}>{o.displayKey ?? o.key.split('/').pop()}</span>
443
+ <span className={`kol-item-name min-w-0 truncate${selected ? '' : ' text-fg-default'}`}>{o.displayKey ?? o.key.split('/').pop()}</span>
442
444
  </span>
443
445
  <span className="flex-1" />
444
446
  {cols && <>
445
- {cols.tags != null && <span className="kol-mono-12 text-fg-48 shrink-0 truncate" style={{ width: cols.tags }}>{(o.tags ?? []).map((t) => `#${t}`).join(' ')}</span>}
446
- <span className="kol-mono-12 text-fg-32 shrink-0 truncate" style={{ width: cols.date }}>{formatDate?.(o.uploaded)}</span>
447
- <span className="kol-mono-12 text-fg-32 shrink-0 truncate" style={{ width: cols.size }}>{formatSize(o.size)}</span>
447
+ {cols.tags != null && <span className={`kol-item-name ${selected ? "opacity-60" : "text-fg-48"} shrink-0 truncate`} style={{ width: cols.tags }}>{(o.tags ?? []).map((t) => `#${t}`).join(' ')}</span>}
448
+ <span className={`kol-item-name ${selected ? "opacity-60" : "text-fg-32"} shrink-0 truncate`} style={{ width: cols.date }}>{formatDate?.(o.uploaded)}</span>
449
+ <span className={`kol-item-name ${selected ? "opacity-60" : "text-fg-32"} shrink-0 truncate`} style={{ width: cols.size }}>{formatSize(o.size)}</span>
448
450
  </>}
449
451
  <RowMenuButton onOpen={onContextMenu} className="-my-1 -mr-1" />
450
452
  </li>
@@ -594,7 +596,7 @@ function FileEditorHost({ o, client, bucket, onClose, onSaved, assets }) {
594
596
  }
595
597
 
596
598
  /**
597
- * MediaInspector — the full-screen viewer for one file in a set: image, video
599
+ * MediaInspector — View one file at full size. the full-screen viewer for one file in a set: image, video
598
600
  * (scrubbable, audio on), audio, or a document page, in one `QuickLookFrame`, with
599
601
  * prev / next fixed at the viewport edges.
600
602
  *
@@ -2,7 +2,7 @@ import { useState } from 'react'
2
2
  import MediaViewer from './MediaViewer.jsx'
3
3
 
4
4
  /**
5
- * MediaTileGallery — a stack or grid of framed media tiles that open THE
5
+ * MediaTileGallery — Media tiles that open a fullscreen viewer. a stack or grid of framed media tiles that open THE
6
6
  * shared fullscreen MediaViewer at the clicked tile, paged across all items
7
7
  * (the monorepo's FullscreenGallery recreated on the shared viewer instead of
8
8
  * its nav-less single image; the AssetFigure/AssetGrid deps are dropped —
@@ -35,7 +35,7 @@ const ToolbarIcon = ({ name, label, onClick, active = false, triggerClassName })
35
35
  )
36
36
 
37
37
  /**
38
- * RecordManager — full-screen CMS record surface (lobby: RecordManager,
38
+ * RecordManager — A full-screen CMS record editor. full-screen CMS record surface (lobby: RecordManager,
39
39
  * reference: Framer CMS). A reorderable record table plus a slide-over detail
40
40
  * panel of FieldRows.
41
41
  *
@@ -6,7 +6,7 @@ import useSectionTheme from '../hooks/useSectionTheme.js'
6
6
  import { minHeightClass } from './sectionHeights.js'
7
7
 
8
8
  /**
9
- * SectionCards — the "N-up feature cards" band: a `SectionText` header over a
9
+ * SectionCards — A header over a row of feature cards. the "N-up feature cards" band: a `SectionText` header over a
10
10
  * responsive row of `SectionCardItem` cards, capped by an optional centred
11
11
  * action row. `FeaturesCardSection` is this component under its old name and
12
12
  * prop names (`headerLabel` → `headline`, `headerDescription` → `body`,
@@ -5,7 +5,7 @@ import SectionText from '../molecules/SectionText.jsx'
5
5
  import { minHeightClass } from './sectionHeights.js'
6
6
 
7
7
  /**
8
- * SectionCta — the editorial two-column contact/CTA band: a large display
8
+ * SectionCta — A closing call-to-action band. the editorial two-column contact/CTA band: a large display
9
9
  * wordmark on the left (e.g. "/ CONNECT") and a right column of stacked
10
10
  * label-over-value rows, each a `SectionText` (label in the helper voice,
11
11
  * value as a heading-01; a row with `href` renders its value as a link).
@@ -6,7 +6,7 @@ import SectionText from '../molecules/SectionText.jsx'
6
6
  import { minHeightClass } from './sectionHeights.js'
7
7
 
8
8
  /**
9
- * SectionFaq — a `SectionText` header over an `Accordion` of question /
9
+ * SectionFaq — Questions and answers in an accordion. a `SectionText` header over an `Accordion` of question /
10
10
  * answer items. The one NEW surface in the section family (SectionSet,
11
11
  * 2026-08-26) — the behaviour already shipped in the Accordion molecule; this
12
12
  * is the section around it.
@@ -85,7 +85,7 @@ function MediaLayer({ media }) {
85
85
  }
86
86
 
87
87
  /**
88
- * SectionHero — the full-bleed media hero: cover-fit background media (image
88
+ * SectionHero — Full-bleed media hero with a glass panel. the full-bleed media hero: cover-fit background media (image
89
89
  * or video) filling a fixed-height section, an optional surface scrim, and
90
90
  * the text in a glass panel over it. `FullBleedHero` is this component under
91
91
  * its old name (alias kept).
@@ -8,7 +8,7 @@ import useSectionTheme from '../hooks/useSectionTheme.js'
8
8
  import { minHeightClass } from './sectionHeights.js'
9
9
 
10
10
  /**
11
- * SectionNewsletter — the newsletter card of the section family
11
+ * SectionNewsletter — A newsletter signup card. the newsletter card of the section family
12
12
  * (SectionNewsletter, kol-website 2026-08-27 — user: the newsletter as a card
13
13
  * in the family, same ladder, same cap, content centred on y, not its own
14
14
  * thing). `SectionText` (label · headline · body, centred) with the email
@@ -6,7 +6,7 @@ import useSectionTheme from '../hooks/useSectionTheme.js'
6
6
  import { minHeightClass } from './sectionHeights.js'
7
7
 
8
8
  /**
9
- * SectionSplit — the media-and-text split section, on `SectionText` (the
9
+ * SectionSplit — Text beside media. the media-and-text split section, on `SectionText` (the
10
10
  * text column) beside the media frame. Two columns at/above 901px, one
11
11
  * stacked column below. `FeatureSplit` is this component under its old name
12
12
  * and old prop names (kept as an alias — see FeatureSplit.jsx).
@@ -9,7 +9,7 @@ import FullscreenOverlay from '../utilities/FullscreenOverlay.jsx'
9
9
  import { Tooltip } from '../utilities/Popover.jsx'
10
10
 
11
11
  /**
12
- * SettingsPanel — a settings surface for the thing you are looking at: you
12
+ * SettingsPanel — Settings beside the thing they change. a settings surface for the thing you are looking at: you
13
13
  * change how a list looks WHILE looking at it, so it is a slide-over (or an
14
14
  * overlay), never a route (SettingsPanel, kol-r2b2 2026-08-26 — the
15
15
  * per-bucket display settings: kinds allow-list, structure, loading, layout).
@@ -107,7 +107,7 @@ export default function SettingsPanel({
107
107
  }
108
108
 
109
109
  /**
110
- * SettingsRow — a `LabeledControl inline` (SettingsPanelApproved, 2026-08-27):
110
+ * SettingsRow — One labelled setting. a `LabeledControl inline` (SettingsPanelApproved, 2026-08-27):
111
111
  * uppercase label (`kol-helper-10` tracked, meta ink) in a 160px column, the
112
112
  * control fills the rest. A switch sits at the far right (`align="end"`, the
113
113
  * default); a dropdown fills the row (`align="fill"`). No hint sentences on the
@@ -127,7 +127,7 @@ export function SettingsRow({ label, hint, align = 'end', labelWidth = 160, chil
127
127
  )
128
128
  }
129
129
 
130
- /** SettingsSwitch — the row's on/off control: the DS ToggleSwitch, bare, sm.
130
+ /** SettingsSwitch — An on/off setting. the row's on/off control: the DS ToggleSwitch, bare, sm.
131
131
  * `disabledHint` rides a DS `Tooltip` so a switch that cannot act says why;
132
132
  * inside a row's `hint` it wins while hovered, as the native `title` did. */
133
133
  export function SettingsSwitch({ on = false, onChange, disabled = false, disabledHint, label, title }) {
@@ -144,7 +144,7 @@ export function SettingsSwitch({ on = false, onChange, disabled = false, disable
144
144
  return tip ? <Tooltip label={tip}>{toggle}</Tooltip> : toggle
145
145
  }
146
146
 
147
- /** LabeledControlSection — a section of LabeledControls: the EYEBROW
147
+ /** LabeledControlSection — A titled section of labelled controls. a section of LabeledControls: the EYEBROW
148
148
  * (`kol-eyebrow text-fg-80`) standing apart from the rows (gap-3), the rows in
149
149
  * their own stack — `rowGap` 1 for switch rows (24 tall already), 2 for
150
150
  * dropdown rows. `divided` = a hairline above (between sections).
@@ -161,7 +161,7 @@ export function LabeledControlSection({ label, divided = false, rowGap = 2, chil
161
161
  )
162
162
  }
163
163
 
164
- /** SettingsChoice — the row's one-of-N control: the DS Dropdown, sm · primary
164
+ /** SettingsChoice — A one-of-many setting. the row's one-of-N control: the DS Dropdown, sm · primary
165
165
  * (SettingsPanelEyebrowAndDropdowns — user: "put the toggles inside a dropdown,
166
166
  * because it's super messy like it is"). Options are values or `{ value, label }`;
167
167
  * width is the call site's (`className="w-40"`).
@@ -189,7 +189,7 @@ export const CHIP_CLS = 'kol-control kol-control-sm kol-mono-12'
189
189
  export const chipCls = (on) => `${CHIP_CLS} ${on ? 'kol-control--filled' : 'text-meta hover:text-emphasis'}`
190
190
 
191
191
  /**
192
- * SettingsMulti — a many-of-N control as ONE Dropdown (SettingsPanelApproved,
192
+ * SettingsMulti — A many-of-many setting in one dropdown. a many-of-N control as ONE Dropdown (SettingsPanelApproved,
193
193
  * 2026-08-27 — the kinds row): the trigger reads `N of M <noun>`, every entry
194
194
  * toggles and `✓` marks the ones that are on. No grouping.
195
195
  * @param {Array} options [{ value, label }]
@@ -210,7 +210,7 @@ export function SettingsMulti({ options = [], selected = [], onToggle, noun = 'k
210
210
  }
211
211
 
212
212
  /**
213
- * SettingsChipRow — a wrap of toggle chips with optional counts (an
213
+ * SettingsChipRow — A row of toggle chips. a wrap of toggle chips with optional counts (an
214
214
  * allow-list: every chip sets a default, never a gate). `allChip` puts an
215
215
  * "all" chip first — the same control chip, filled when every option is on
216
216
  * (SettingsPanelEyebrowAndDropdowns — user: "Show all in ghost mode? makes no
@@ -247,7 +247,7 @@ export function SettingsChipRow({ options = [], selected = [], onToggle, allChip
247
247
  )
248
248
  }
249
249
 
250
- /** SettingsFooter — a Divider, then one `IconFrame refresh` (primary · sm) at the
250
+ /** SettingsFooter — Reset settings to their defaults. a Divider, then one `IconFrame refresh` (primary · sm) at the
251
251
  * right = reset to defaults (SettingsPanelApproved, 2026-08-27). No status word,
252
252
  * no text button. `customised` is accepted for compatibility and unused.
253
253
  *
@@ -59,7 +59,7 @@ export function HighlightMatch({ label, query, ranges }) {
59
59
  }
60
60
 
61
61
  /**
62
- * ShellSearchOverlay — the ⌘K command palette: fullscreen dim + centered
62
+ * ShellSearchOverlay — The ⌘K search palette. the ⌘K command palette: fullscreen dim + centered
63
63
  * panel, a bare SearchInput on top, result rows beneath (HighlightMatch
64
64
  * label, dim hint line, right-aligned group label). Distinct from Modal
65
65
  * (prompt/confirm only) — this is the search/command primitive.
@@ -2,7 +2,7 @@ import { useEffect } from 'react'
2
2
  import SettingsSections from './SettingsSections.jsx'
3
3
 
4
4
  /**
5
- * ShortcutsOverlay — the keyboard-shortcut sheet: blurred scrim, centred
5
+ * ShortcutsOverlay — The keyboard shortcut sheet. the keyboard-shortcut sheet: blurred scrim, centred
6
6
  * panel, a 2-col grid (label · keys), Esc / backdrop-click close.
7
7
  * Ported from the shared cut (mirror's, "copied from kol-monitor").
8
8
  *
@@ -41,7 +41,7 @@ const HUE_GRADIENT =
41
41
  'linear-gradient(to right, hsl(0,100%,50%), hsl(60,100%,50%), hsl(120,100%,50%), hsl(180,100%,50%), hsl(240,100%,50%), hsl(300,100%,50%), hsl(360,100%,50%))'
42
42
 
43
43
  /**
44
- * HueStrip — 1D hue slider: a rainbow bar with a draggable knob. Pointer
44
+ * HueStrip — A hue slider. 1D hue slider: a rainbow bar with a draggable knob. Pointer
45
45
  * drag anywhere on the strip; focus + Left/Right (or Down/Up) arrow keys
46
46
  * nudge hue by 1° (role="slider").
47
47
  *
@@ -116,7 +116,7 @@ export function HueStrip({ hue, onChange }) {
116
116
  }
117
117
 
118
118
  /**
119
- * SBSquare — 2D saturation/value picker: white→hue horizontal gradient with
119
+ * SBSquare — Pick saturation and value on a square. 2D saturation/value picker: white→hue horizontal gradient with
120
120
  * a black overlay fading upward, crosshair handle. Fills its container
121
121
  * (consumer owns the size — and the rounding: apply `rounded-[var(--kol-radius-xs)]
122
122
  * overflow-hidden` on the wrapper). Focus + arrow keys nudge: Left/Right =
@@ -233,7 +233,7 @@ function vertexAt(angleRad) {
233
233
  }
234
234
 
235
235
  /**
236
- * WheelTriangle — HSV color wheel: conic hue ring with an inscribed,
236
+ * WheelTriangle — A hue wheel with a value triangle. HSV color wheel: conic hue ring with an inscribed,
237
237
  * rotating HSV triangle for saturation/value. Ring press snaps hue to the
238
238
  * cursor angle and drags; triangle press grabs the SV handle with a
239
239
  * cursor↔handle offset so fine tuning never teleports it. Focus + arrow
@@ -478,7 +478,7 @@ function SvgHandle({ cx, cy, r = 2.4 }) {
478
478
  }
479
479
 
480
480
  /**
481
- * SpectrumControls — the composed classic square picker: HueStrip stacked
481
+ * SpectrumControls — A hue strip over a saturation square. The composed classic square picker: HueStrip stacked
482
482
  * over a fill-height SBSquare, per the source color panel's Hue mode layout
483
483
  * (SBSquare wrapped in `rounded-[var(--kol-radius-xs)] overflow-hidden`, see its comment).
484
484
  * Fills its container — the consumer owns the outer size. For the ring
@@ -4,7 +4,7 @@ import { resolveCssVar, resolveCssColor, isLight } from '../hooks/cssVar.js'
4
4
  import { Tooltip } from '../utilities/Popover.jsx'
5
5
 
6
6
  /**
7
- * SpectrumGrid — matrix view of the whole ramp system: rows = ramps, columns =
7
+ * SpectrumGrid — Every ramp by every stop. matrix view of the whole ramp system: rows = ramps, columns =
8
8
  * stops (50 → 900). Each cell is a ColorSwatch tile with the stop number and
9
9
  * hex printed over it (text color contrast-corrected via isLight), so the whole
10
10
  * palette is scannable in one grid.
@@ -5,7 +5,7 @@ import SplitToolButton from '../molecules/SplitToolButton.jsx'
5
5
  import { Tooltip } from '../utilities/Popover.jsx'
6
6
 
7
7
  /**
8
- * ToolPalette — the editor's tool bar as one row (kol-fxr `shell/panels/ToolPalette.jsx`,
8
+ * ToolPalette — The editor's tool bar. the editor's tool bar as one row (kol-fxr `shell/panels/ToolPalette.jsx`,
9
9
  * editor-panels-the-held-specs A3, 2026-09-25). Four kinds of cell, all on one pinned-square rung:
10
10
  *
11
11
  * - `tool` — arms a mode; lit while `activeId` is its id