The two pickers rhyme — one two-zone chrome, a face onto a standalone browser, a trigger onto the popover
The colour combo was a wide two-zone field with a second door onto the Colors panel; the symbol picker was a small square button with one. Both now subclass one `ComboFieldControl`, so they are the same width, height, radius and trigger by construction: click the face for the standalone picker, click the chevron for the quick list. The symbol face opens a new floating browser over the OS's own category, ordering and keyword plists out of CoreGlyphs.bundle — searchable, categorised, trademark-restricted glyphs withheld. The palette grows twelve to sixteen per table, filling the hue ring's four widest gaps with lime, jade, indigo and magenta at each table's own saturation and brightness. That gives the Style… popover's background grid a third row and the tint grid its third row of four, and both grids gain an Other… row onto the system colour picker — which the card sidebar's combo has had all along and the primary styling surface never did. An arbitrary hex already round-tripped; it is asserted now, including that an unquoted one is a YAML comment and no value. Claude-Session: https://claude.ai/code/session_014PtZdPwqZuqEDLc6wZMtEy
This commit is contained in:
@@ -240,7 +240,8 @@ struct StyleEditorLayout: Equatable {
|
||||
/// The Style… popover and the board popover's styling area: a fixed frame, its own padding, and
|
||||
/// a symbol grid that scrolls within it.
|
||||
///
|
||||
/// Thirteen background wells (None + the twelve) fall as 7 + 6, which keeps the popover narrow
|
||||
/// Seventeen background wells (None + the sixteen) fall as 7 + 7 + 3 — two rows until the
|
||||
/// palette grew on 2026-08-09, three since — which keeps the popover narrow
|
||||
/// enough to sit beside a card without covering the lane it came from; the symbol grid's cap is
|
||||
/// eight rows or so — enough that it reads as a set rather than as a strip, short enough that the
|
||||
/// popover fits beside a card on a laptop screen.
|
||||
@@ -395,8 +396,15 @@ struct StyleEditorView: View {
|
||||
|
||||
// MARK: - Background
|
||||
|
||||
/// The twelve palette wells and their leading None (03-board-ui.md § Styling ▸ Controls:
|
||||
/// "palette-only in-app … plus a leading **None** well that removes the `background` key").
|
||||
/// The palette wells and their leading None (03-board-ui.md § Styling ▸ Controls: "palette-only
|
||||
/// in-app … plus a leading **None** well that removes the `background` key"), and — since
|
||||
/// 2026-08-09 — an **Other…** row onto the system Colors panel.
|
||||
///
|
||||
/// **"Palette-only in-app" is the sentence that changed**, and it changed before this card:
|
||||
/// `ColorComboView` shipped an **Other…** row of its own, so the card window's sidebar could
|
||||
/// already write an arbitrary hex while the Style… popover — the *primary* styling surface —
|
||||
/// could not. This closes that gap rather than opening a new one. Seventeen wells at seven
|
||||
/// columns is three rows where the old twelve made two, which is the other half of the same change.
|
||||
private func backgroundSection(_ state: StyleFieldState, layout: StyleEditorLayout) -> some View {
|
||||
VStack(alignment: .leading, spacing: layout.wellSpacing) {
|
||||
sectionHeader("Background", current: backgroundCurrent(state), layout: layout)
|
||||
@@ -408,6 +416,32 @@ struct StyleEditorView: View {
|
||||
StyleCommand.apply(background: change, to: target, in: store, recents: recents, on: undo)
|
||||
}
|
||||
)
|
||||
HStack(spacing: 0) {
|
||||
Spacer(minLength: 0)
|
||||
Button("Other…") { openBackgroundPanel(state) }
|
||||
.buttonStyle(.link)
|
||||
.font(.caption)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Opens the Colors panel on the background dimension, seeded with what the target set currently
|
||||
/// reads (nothing, for a mixed set — there is no one colour to start from).
|
||||
///
|
||||
/// **The panel fires continuously**, so this debounces exactly as `CardStyleSection` does for the
|
||||
/// colour combo's own drag: ~400ms trailing, cancelled and replaced on every tick, so a drag
|
||||
/// writes once it settles instead of once per pixel it passes through.
|
||||
///
|
||||
/// It goes to `store.applyStyle` rather than through `StyleCommand.apply`, which is the same call
|
||||
/// the sidebar's debounce makes and for the same reason: `StyleRecents` remembers *deliberate
|
||||
/// palette picks*, and a drag that sweeps through six palette-exact colours on its way somewhere
|
||||
/// else would otherwise flush the row it is supposed to be helping.
|
||||
private func openBackgroundPanel(_ state: StyleFieldState) {
|
||||
let seed: NSColor? = if case let .uniform(value) = state { Palette.nsColor(for: value) } else { nil }
|
||||
let target = self.target
|
||||
SharedColorPanelSession.present(seed: seed, matching: Palette.backgrounds) { [weak store, weak undo] value in
|
||||
guard let store else { return }
|
||||
store.applyStyle(to: target, background: .set(value), icon: .keep, on: undo)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user