{
  "$schema": "https://ui.shadcn.com/schema/registry.json",
  "name": "tradecn",
  "homepage": "https://tradecn.dev",
  "items": [
    {
      "name": "format",
      "type": "registry:lib",
      "title": "Format",
      "description": "Number formatting for trading screens: tick-derived precision, 32nds and 64ths with + halves, yields, basis points, DV01, compact notional and millions, coupons in eighths, maturities, ticks between two prices, a quote basis per instrument (price, yield, discount, spread) with its own step, signed values, one null sentinel.",
      "registryDependencies": [],
      "files": [
        {
          "path": "registry/tradecn/lib/format.ts",
          "type": "registry:lib"
        }
      ],
      "categories": [
        "format"
      ],
      "docs": "Pure functions, no dependencies. Bind a convention per instrument with createInstrumentFormatter and pass its price() to a grid column.",
      "cssVars": {
        "light": {
          "tradecn-font-accessible": "'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-accessible-mono": "'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-mono": "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-numeric": "var(--tradecn-font-sans)",
          "tradecn-font-sans": "'Inter', ui-sans-serif, system-ui, sans-serif"
        },
        "dark": {
          "tradecn-font-accessible": "'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-accessible-mono": "'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-mono": "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-numeric": "var(--tradecn-font-sans)",
          "tradecn-font-sans": "'Inter', ui-sans-serif, system-ui, sans-serif"
        }
      },
      "css": {
        ":root[data-accessibility=\"hyperlegible\"], [data-accessibility=\"hyperlegible\"]": {
          "--tradecn-font-sans": "var(--tradecn-font-accessible)",
          "--tradecn-font-mono": "var(--tradecn-font-accessible-mono)"
        }
      }
    },
    {
      "name": "row-store",
      "type": "registry:lib",
      "title": "Row Store",
      "description": "Store-agnostic row store for high-rate feeds: apply one delta batch per frame, subscribe per row, sorted and filtered views with a reorder hold, and a frame batcher for message-driven feeds.",
      "registryDependencies": [],
      "files": [
        {
          "path": "registry/tradecn/lib/row-store.ts",
          "type": "registry:lib"
        },
        {
          "path": "registry/tradecn/hooks/use-row-store.ts",
          "type": "registry:hook"
        }
      ],
      "categories": [
        "grid",
        "feed"
      ],
      "docs": "Your feed calls store.applyDeltas(batch) once per frame. Components read rows with useRow(store, id) and re-render only when that row changes. Wrap message-at-a-time feeds in createFrameBatcher."
    },
    {
      "name": "flash-cell",
      "type": "registry:ui",
      "title": "Flash Cell",
      "description": "A cell that flashes on change, colored up, down, or flat, as a fill or an inset ring. Web Animations on the existing element, a shared memory so virtualized rows resume mid-flash, reduced-motion aware. Introduces the up/down/flat tokens.",
      "dependencies": [
        "cn@^0.3.0"
      ],
      "registryDependencies": [],
      "files": [
        {
          "path": "registry/tradecn/ui/flash-cell.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/tradecn/hooks/use-flash.ts",
          "type": "registry:hook"
        }
      ],
      "categories": [
        "grid",
        "feed"
      ],
      "docs": "Adds up, down, flat and their -soft tokens to your stylesheet as bg-up-soft, text-down and so on. Override --up and --down in your own CSS; later installs keep your values. Zero change is flat, never up.",
      "meta": {
        "components": [
          {
            "title": "FlashCell",
            "description": "Cell wrapper that flashes on value change."
          }
        ]
      },
      "cssVars": {
        "light": {
          "down": "oklch(0.46 0.19 40)",
          "down-soft": "oklch(0.46 0.19 40 / 18%)",
          "flat": "var(--color-muted-foreground)",
          "flat-soft": "oklch(0.55 0 0 / 14%)",
          "up": "oklch(0.52 0.13 165)",
          "up-soft": "oklch(0.52 0.13 165 / 18%)"
        },
        "dark": {
          "down": "oklch(0.66 0.19 40)",
          "down-soft": "oklch(0.66 0.19 40 / 18%)",
          "flat": "var(--color-muted-foreground)",
          "flat-soft": "oklch(0.7 0 0 / 14%)",
          "up": "oklch(0.74 0.15 165)",
          "up-soft": "oklch(0.74 0.15 165 / 18%)"
        }
      }
    },
    {
      "name": "data-grid",
      "type": "registry:ui",
      "title": "Data Grid",
      "description": "Virtualized grid fed by a row store one row at a time: a delta re-renders one row, numeric cells flash by direction, frozen and resizable columns, keyboard selection by row id, a reorder hold, a right-click menu, footer totals, cells edited in place as commands the server answers, presets for blotter, watchlist, RFQ, option chain, tape, and parameters.",
      "dependencies": [
        "cn@^0.3.0",
        "@tanstack/react-virtual@^3.14.13"
      ],
      "registryDependencies": [
        "checkbox",
        "context-menu",
        "dropdown-menu"
      ],
      "files": [
        {
          "path": "registry/tradecn/ui/data-grid.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/tradecn/hooks/use-flash.ts",
          "type": "registry:hook"
        },
        {
          "path": "registry/tradecn/hooks/use-row-store.ts",
          "type": "registry:hook"
        },
        {
          "path": "registry/tradecn/lib/row-store.ts",
          "type": "registry:lib"
        },
        {
          "path": "registry/tradecn/lib/format.ts",
          "type": "registry:lib"
        },
        {
          "path": "registry/tradecn/lib/grid-rules.ts",
          "type": "registry:lib"
        }
      ],
      "categories": [
        "grid",
        "blotter",
        "watchlist"
      ],
      "docs": "Feed it a RowStore (installed alongside) and column definitions. Fixed row height; the grid positions rows itself. Adds @tanstack/react-virtual and the up/down/flat tokens. Composes your checkbox, context-menu, and dropdown-menu.",
      "meta": {
        "components": [
          {
            "title": "DataGrid",
            "description": "The grid."
          },
          {
            "title": "DATA_GRID_PRESETS",
            "description": "Density and behavior bundles: blotter, watchlist, rfq, option-chain, tape, parameters."
          },
          {
            "title": "exportCsv",
            "description": "CSV of given rows through the column formatters."
          },
          {
            "title": "editProblem",
            "description": "What a column's parse or validate returns instead of a value: a sentence for the cell."
          }
        ]
      },
      "cssVars": {
        "light": {
          "down": "oklch(0.46 0.19 40)",
          "down-soft": "oklch(0.46 0.19 40 / 18%)",
          "expiring": "oklch(0.53 0.2 350)",
          "expiring-soft": "oklch(0.53 0.2 350 / 18%)",
          "flat": "var(--color-muted-foreground)",
          "flat-soft": "oklch(0.55 0 0 / 14%)",
          "stale": "oklch(0.52 0.13 75)",
          "stale-soft": "oklch(0.52 0.13 75 / 16%)",
          "tradecn-font-accessible": "'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-accessible-mono": "'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-mono": "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-numeric": "var(--tradecn-font-sans)",
          "tradecn-font-sans": "'Inter', ui-sans-serif, system-ui, sans-serif",
          "up": "oklch(0.52 0.13 165)",
          "up-soft": "oklch(0.52 0.13 165 / 18%)"
        },
        "dark": {
          "down": "oklch(0.66 0.19 40)",
          "down-soft": "oklch(0.66 0.19 40 / 18%)",
          "expiring": "oklch(0.74 0.17 350)",
          "expiring-soft": "oklch(0.74 0.17 350 / 18%)",
          "flat": "var(--color-muted-foreground)",
          "flat-soft": "oklch(0.7 0 0 / 14%)",
          "stale": "oklch(0.8 0.15 80)",
          "stale-soft": "oklch(0.8 0.15 80 / 16%)",
          "tradecn-font-accessible": "'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-accessible-mono": "'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-mono": "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-numeric": "var(--tradecn-font-sans)",
          "tradecn-font-sans": "'Inter', ui-sans-serif, system-ui, sans-serif",
          "up": "oklch(0.74 0.15 165)",
          "up-soft": "oklch(0.74 0.15 165 / 18%)"
        }
      },
      "css": {
        ":root[data-accessibility=\"hyperlegible\"], [data-accessibility=\"hyperlegible\"]": {
          "--tradecn-font-sans": "var(--tradecn-font-accessible)",
          "--tradecn-font-mono": "var(--tradecn-font-accessible-mono)"
        }
      }
    },
    {
      "name": "feed-health",
      "type": "registry:ui",
      "title": "Feed Health",
      "description": "A strip that shows each feed's connection state, the age of its data, and a staleness tier, with drop counts for coalesced lanes and gap indicators for ordered ones. Each feed offers the actions the server allows on it in a menu of its own, pending until the feed reports back. Pluggable thresholds and session calendar; only the leaves tick.",
      "dependencies": [
        "cn@^0.3.0"
      ],
      "registryDependencies": [
        "badge",
        "dropdown-menu",
        "separator",
        "spinner",
        "tooltip"
      ],
      "files": [
        {
          "path": "registry/tradecn/ui/feed-health.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/tradecn/hooks/use-clock.ts",
          "type": "registry:hook"
        },
        {
          "path": "registry/tradecn/lib/clock.ts",
          "type": "registry:lib"
        }
      ],
      "categories": [
        "feed",
        "chrome"
      ],
      "docs": "Adds the stale and up tokens and installs the shared one-second clock alongside. Composes your badge, separator, spinner, tooltip, and dropdown-menu. The default thresholds (2 s aging, 10 s stale) are placeholders: agree real ones with the people who trade on the data and pass them in. A feed's actions are the ids the server allows on it; a press shows pending until the feed's state moves.",
      "meta": {
        "components": [
          {
            "title": "FeedHealth",
            "description": "The strip."
          },
          {
            "title": "FeedAge",
            "description": "A self-ticking age for one feed."
          },
          {
            "title": "stalenessTier",
            "description": "Pure tiering: live, aging, stale, offline, closed."
          },
          {
            "title": "createClock",
            "description": "A shared interval that runs only while subscribed."
          },
          {
            "title": "feedActionsFor",
            "description": "The actions a feed offers now: yours, kept to the ids the server allows."
          }
        ]
      },
      "cssVars": {
        "light": {
          "stale": "oklch(0.52 0.13 75)",
          "stale-soft": "oklch(0.52 0.13 75 / 16%)",
          "up": "oklch(0.52 0.13 165)"
        },
        "dark": {
          "stale": "oklch(0.8 0.15 80)",
          "stale-soft": "oklch(0.8 0.15 80 / 16%)",
          "up": "oklch(0.74 0.15 165)"
        }
      }
    },
    {
      "name": "use-hotkeys",
      "type": "registry:hook",
      "title": "Hotkeys",
      "description": "A hotkey registry: bindings declared as data with an id, keys, a scope, and a description; one keydown listener; scopes read from the DOM so a panel's keys beat global ones; chords with a timeout; conflict detection; remapping with the consumer's persistence. Typing is protected, menus are left alone, and a dialog is a wall.",
      "registryDependencies": [],
      "files": [
        {
          "path": "registry/tradecn/hooks/use-hotkeys.tsx",
          "type": "registry:hook"
        },
        {
          "path": "registry/tradecn/lib/hotkeys.ts",
          "type": "registry:lib"
        }
      ],
      "categories": [
        "hotkeys"
      ],
      "docs": "Declare bindings once, wrap the app in HotkeysProvider, attach handlers with useHotkey. list() is the single source for a palette's shortcut column and a help overlay. No dependencies beyond React.",
      "meta": {
        "components": [
          {
            "title": "HotkeysProvider",
            "description": "Puts a registry in context and listens for keydown."
          },
          {
            "title": "HotkeyScope",
            "description": "Marks a subtree as a scope; keys pressed inside it reach that scope's bindings first."
          },
          {
            "title": "useHotkey",
            "description": "Attaches a handler to a declared binding while the component is mounted."
          },
          {
            "title": "useHotkeys",
            "description": "The registry from context."
          },
          {
            "title": "useHotkeyList",
            "description": "Every binding with its keys in force, live."
          },
          {
            "title": "usePendingChord",
            "description": "The chord typed so far, or null."
          }
        ]
      }
    },
    {
      "name": "command-palette",
      "type": "registry:ui",
      "title": "Command Palette",
      "description": "A command palette on your own shadcn command: actions from a registry, a second action per row on Shift+Enter, async symbol search through an adapter you supply, recents, and shortcuts rendered live from the hotkey registry. A go-bar variant renders inline and reads SYMBOL FUNCTION commands through your grammar.",
      "dependencies": [
        "cn@^0.3.0"
      ],
      "registryDependencies": [
        "badge",
        "command",
        "kbd"
      ],
      "files": [
        {
          "path": "registry/tradecn/ui/command-palette.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/tradecn/hooks/use-hotkeys.tsx",
          "type": "registry:hook"
        },
        {
          "path": "registry/tradecn/lib/hotkeys.ts",
          "type": "registry:lib"
        }
      ],
      "categories": [
        "palette",
        "hotkeys"
      ],
      "docs": "Composes your command (and the dialog and input-group it brings), kbd, and badge. Installs the hotkey registry alongside; inside a HotkeysProvider the palette declares palette.open on mod+k, and remaps show on the rows. No dependency beyond cn: cmdk comes with your own command component.",
      "meta": {
        "components": [
          {
            "title": "CommandPalette",
            "description": "The palette, as a dialog or an inline go-bar."
          },
          {
            "title": "createActionRegistry",
            "description": "Actions and recents, shared by every palette that reads it."
          },
          {
            "title": "scorePaletteAction",
            "description": "The ranking the palette filters with, as a pure function."
          }
        ]
      }
    },
    {
      "name": "panel",
      "type": "registry:ui",
      "title": "Panel",
      "description": "The frame around a book, a chart, or a blotter. A panel is a hotkey scope, so its keys and its palette actions come with it. A header that is a drag handle, a symbol tag you click to retype, link groups that carry a symbol between panels and between windows, and a popout that moves the panel to its own window without remounting it.",
      "dependencies": [
        "cn@^0.3.0"
      ],
      "registryDependencies": [
        "input"
      ],
      "files": [
        {
          "path": "registry/tradecn/ui/panel.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/tradecn/hooks/use-link-group.tsx",
          "type": "registry:hook"
        },
        {
          "path": "registry/tradecn/hooks/use-popout.ts",
          "type": "registry:hook"
        },
        {
          "path": "registry/tradecn/hooks/use-hotkeys.tsx",
          "type": "registry:hook"
        },
        {
          "path": "registry/tradecn/lib/link-group.ts",
          "type": "registry:lib"
        },
        {
          "path": "registry/tradecn/lib/hotkeys.ts",
          "type": "registry:lib"
        }
      ],
      "categories": [
        "layout",
        "chrome"
      ],
      "docs": "Adds the panel-active, panel-drag-target, panel-error, panel-sync, and link-1 to link-4 tokens. Composes your input. Link groups need a LinkGroupProvider above the panels; it opens a BroadcastChannel so other windows follow, and transport={null} keeps links in one window. Installs the hotkey registry alongside; a Panel works without a HotkeysProvider and its keys start working inside one.",
      "meta": {
        "components": [
          {
            "title": "Panel",
            "description": "The frame, and the hotkey scope panel:<kind>."
          },
          {
            "title": "PanelHeader",
            "description": "The strip along the top, and the drag handle."
          },
          {
            "title": "PanelTitle",
            "description": "Names the panel, to the eye and to a screen reader."
          },
          {
            "title": "PanelActions",
            "description": "Your buttons at the far end of the header."
          },
          {
            "title": "PanelContent",
            "description": "The scrolling body."
          },
          {
            "title": "SymbolTag",
            "description": "The panel's symbol: click to retype, Enter commits, Escape and clicking away cancel."
          },
          {
            "title": "LinkGroupDot",
            "description": "The panel's link group as a color and a number; click to move to the next."
          },
          {
            "title": "PanelPopout",
            "description": "Renders its children in the page or in the popout window, without remounting them."
          }
        ]
      },
      "cssVars": {
        "light": {
          "link-1": "oklch(0.53 0.2 25)",
          "link-2": "oklch(0.5 0.15 250)",
          "link-3": "oklch(0.5 0.13 150)",
          "link-4": "oklch(0.52 0.12 85)",
          "panel-active": "var(--color-primary)",
          "panel-drag-target": "var(--color-ring)",
          "panel-error": "var(--color-destructive)",
          "panel-sync": "oklch(0.5 0.12 220)",
          "tradecn-font-accessible": "'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-accessible-mono": "'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-mono": "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace"
        },
        "dark": {
          "link-1": "oklch(0.7 0.19 25)",
          "link-2": "oklch(0.7 0.14 250)",
          "link-3": "oklch(0.74 0.14 150)",
          "link-4": "oklch(0.85 0.15 90)",
          "panel-active": "var(--color-primary)",
          "panel-drag-target": "var(--color-ring)",
          "panel-error": "var(--color-destructive)",
          "panel-sync": "oklch(0.78 0.12 220)",
          "tradecn-font-accessible": "'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-accessible-mono": "'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-mono": "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace"
        }
      },
      "css": {
        ":root[data-accessibility=\"hyperlegible\"], [data-accessibility=\"hyperlegible\"]": {
          "--tradecn-font-sans": "var(--tradecn-font-accessible)",
          "--tradecn-font-mono": "var(--tradecn-font-accessible-mono)"
        }
      }
    },
    {
      "name": "sparkline",
      "type": "registry:ui",
      "title": "Sparkline",
      "description": "A line small enough for a grid cell that still says which way, how far, and between what. Colored by direction against the first reading or a baseline, with the same direction in words for a screen reader. A missing reading is a gap, not a shift. Fixed size for a grid column, or it fills its box through one shared ResizeObserver. An optional crosshair that moves with the pointer and the arrow keys.",
      "dependencies": [
        "cn@^0.3.0"
      ],
      "registryDependencies": [],
      "files": [
        {
          "path": "registry/tradecn/ui/sparkline.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/tradecn/hooks/use-flash.ts",
          "type": "registry:hook"
        },
        {
          "path": "registry/tradecn/lib/sparkline-geometry.ts",
          "type": "registry:lib"
        }
      ],
      "categories": [
        "grid",
        "watchlist"
      ],
      "docs": "Adds the up, down, flat tokens and their -soft variants, the same ones flash-cell uses. Give it width and height inside a grid column and it observes nothing. It draws every reading you pass; decimate a long history before it gets here.",
      "meta": {
        "components": [
          {
            "title": "Sparkline",
            "description": "The line, and the crosshair when interactive."
          }
        ]
      },
      "cssVars": {
        "light": {
          "down": "oklch(0.46 0.19 40)",
          "down-soft": "oklch(0.46 0.19 40 / 18%)",
          "flat": "var(--color-muted-foreground)",
          "flat-soft": "oklch(0.55 0 0 / 14%)",
          "up": "oklch(0.52 0.13 165)",
          "up-soft": "oklch(0.52 0.13 165 / 18%)"
        },
        "dark": {
          "down": "oklch(0.66 0.19 40)",
          "down-soft": "oklch(0.66 0.19 40 / 18%)",
          "flat": "var(--color-muted-foreground)",
          "flat-soft": "oklch(0.7 0 0 / 14%)",
          "up": "oklch(0.74 0.15 165)",
          "up-soft": "oklch(0.74 0.15 165 / 18%)"
        }
      }
    },
    {
      "name": "watchlist",
      "type": "registry:ui",
      "title": "Watchlist",
      "description": "The data grid as a watchlist: symbol, last, bid, ask, change, change %, and volume, with prices printed through your convention per row. A field that adds a symbol and goes to it instead when it is already there, and three ways to take one off: Delete, a button on the row, and the right-click menu. The list stays yours.",
      "dependencies": [
        "cn@^0.3.0",
        "@tanstack/react-virtual@^3.14.13"
      ],
      "registryDependencies": [
        "button",
        "checkbox",
        "context-menu",
        "dropdown-menu",
        "input"
      ],
      "files": [
        {
          "path": "registry/tradecn/ui/watchlist.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/tradecn/ui/data-grid.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/tradecn/hooks/use-flash.ts",
          "type": "registry:hook"
        },
        {
          "path": "registry/tradecn/hooks/use-row-store.ts",
          "type": "registry:hook"
        },
        {
          "path": "registry/tradecn/lib/row-store.ts",
          "type": "registry:lib"
        },
        {
          "path": "registry/tradecn/lib/format.ts",
          "type": "registry:lib"
        },
        {
          "path": "registry/tradecn/lib/grid-rules.ts",
          "type": "registry:lib"
        }
      ],
      "categories": [
        "watchlist",
        "grid"
      ],
      "docs": "Installs the data grid, the row store, and format alongside, and adds @tanstack/react-virtual and the up/down/flat tokens. Composes your input, button, and context-menu, plus the checkbox and dropdown-menu the grid uses. Rows are keyed by symbol: createRowStore({ getRowId: (r) => r.symbol }).",
      "meta": {
        "components": [
          {
            "title": "Watchlist",
            "description": "The grid, the add field, and the remove affordances."
          },
          {
            "title": "watchlistColumns",
            "description": "The default columns, as a list to spread into your own."
          }
        ]
      },
      "cssVars": {
        "light": {
          "down": "oklch(0.46 0.19 40)",
          "down-soft": "oklch(0.46 0.19 40 / 18%)",
          "expiring": "oklch(0.53 0.2 350)",
          "expiring-soft": "oklch(0.53 0.2 350 / 18%)",
          "flat": "var(--color-muted-foreground)",
          "flat-soft": "oklch(0.55 0 0 / 14%)",
          "stale": "oklch(0.52 0.13 75)",
          "stale-soft": "oklch(0.52 0.13 75 / 16%)",
          "tradecn-font-accessible": "'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-accessible-mono": "'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-mono": "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-numeric": "var(--tradecn-font-sans)",
          "tradecn-font-sans": "'Inter', ui-sans-serif, system-ui, sans-serif",
          "up": "oklch(0.52 0.13 165)",
          "up-soft": "oklch(0.52 0.13 165 / 18%)"
        },
        "dark": {
          "down": "oklch(0.66 0.19 40)",
          "down-soft": "oklch(0.66 0.19 40 / 18%)",
          "expiring": "oklch(0.74 0.17 350)",
          "expiring-soft": "oklch(0.74 0.17 350 / 18%)",
          "flat": "var(--color-muted-foreground)",
          "flat-soft": "oklch(0.7 0 0 / 14%)",
          "stale": "oklch(0.8 0.15 80)",
          "stale-soft": "oklch(0.8 0.15 80 / 16%)",
          "tradecn-font-accessible": "'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-accessible-mono": "'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-mono": "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-numeric": "var(--tradecn-font-sans)",
          "tradecn-font-sans": "'Inter', ui-sans-serif, system-ui, sans-serif",
          "up": "oklch(0.74 0.15 165)",
          "up-soft": "oklch(0.74 0.15 165 / 18%)"
        }
      },
      "css": {
        ":root[data-accessibility=\"hyperlegible\"], [data-accessibility=\"hyperlegible\"]": {
          "--tradecn-font-sans": "var(--tradecn-font-accessible)",
          "--tradecn-font-mono": "var(--tradecn-font-accessible-mono)"
        }
      }
    },
    {
      "name": "blotter",
      "type": "registry:ui",
      "title": "Blotter",
      "description": "The data grid as an order blotter: time, symbol, side, quantity, filled, price, status, and account. The status is the server's word, printed as it arrives and never worked out. An action such as Cancel is offered only for the orders whose allowedActions the server put it in, the button says how many that is, and the check is made again against the store as the click lands.",
      "dependencies": [
        "cn@^0.3.0",
        "@tanstack/react-virtual@^3.14.13"
      ],
      "registryDependencies": [
        "button",
        "checkbox",
        "context-menu",
        "dropdown-menu"
      ],
      "files": [
        {
          "path": "registry/tradecn/ui/blotter.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/tradecn/ui/data-grid.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/tradecn/hooks/use-flash.ts",
          "type": "registry:hook"
        },
        {
          "path": "registry/tradecn/hooks/use-row-store.ts",
          "type": "registry:hook"
        },
        {
          "path": "registry/tradecn/lib/row-store.ts",
          "type": "registry:lib"
        },
        {
          "path": "registry/tradecn/lib/format.ts",
          "type": "registry:lib"
        },
        {
          "path": "registry/tradecn/lib/grid-rules.ts",
          "type": "registry:lib"
        }
      ],
      "categories": [
        "blotter",
        "grid"
      ],
      "docs": "Installs the data grid, the row store, and format alongside, and adds @tanstack/react-virtual and the up/down/flat tokens. Composes your button and context-menu, plus the checkbox and dropdown-menu the grid uses. Delete cancels nothing until you name the action it runs.",
      "meta": {
        "components": [
          {
            "title": "Blotter",
            "description": "The grid, the toolbar, and the actions."
          },
          {
            "title": "blotterColumns",
            "description": "The default columns, as a list to spread into your own."
          },
          {
            "title": "allowedRows",
            "description": "The orders among some ids that allow an action, read from the store now."
          }
        ]
      },
      "cssVars": {
        "light": {
          "down": "oklch(0.46 0.19 40)",
          "down-soft": "oklch(0.46 0.19 40 / 18%)",
          "expiring": "oklch(0.53 0.2 350)",
          "expiring-soft": "oklch(0.53 0.2 350 / 18%)",
          "flat": "var(--color-muted-foreground)",
          "flat-soft": "oklch(0.55 0 0 / 14%)",
          "stale": "oklch(0.52 0.13 75)",
          "stale-soft": "oklch(0.52 0.13 75 / 16%)",
          "tradecn-font-accessible": "'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-accessible-mono": "'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-mono": "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-numeric": "var(--tradecn-font-sans)",
          "tradecn-font-sans": "'Inter', ui-sans-serif, system-ui, sans-serif",
          "up": "oklch(0.52 0.13 165)",
          "up-soft": "oklch(0.52 0.13 165 / 18%)"
        },
        "dark": {
          "down": "oklch(0.66 0.19 40)",
          "down-soft": "oklch(0.66 0.19 40 / 18%)",
          "expiring": "oklch(0.74 0.17 350)",
          "expiring-soft": "oklch(0.74 0.17 350 / 18%)",
          "flat": "var(--color-muted-foreground)",
          "flat-soft": "oklch(0.7 0 0 / 14%)",
          "stale": "oklch(0.8 0.15 80)",
          "stale-soft": "oklch(0.8 0.15 80 / 16%)",
          "tradecn-font-accessible": "'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-accessible-mono": "'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-mono": "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-numeric": "var(--tradecn-font-sans)",
          "tradecn-font-sans": "'Inter', ui-sans-serif, system-ui, sans-serif",
          "up": "oklch(0.74 0.15 165)",
          "up-soft": "oklch(0.74 0.15 165 / 18%)"
        }
      },
      "css": {
        ":root[data-accessibility=\"hyperlegible\"], [data-accessibility=\"hyperlegible\"]": {
          "--tradecn-font-sans": "var(--tradecn-font-accessible)",
          "--tradecn-font-mono": "var(--tradecn-font-accessible-mono)"
        }
      }
    },
    {
      "name": "workspace",
      "type": "registry:ui",
      "title": "Workspace",
      "description": "Panels that dock, tab, float, and pop out, on dockview. The dock owns where things sit; tradecn owns what each panel is: a kind, a title, and the small JSON it asked to keep. Every docked panel is a hotkey scope of its kind with the active border, and the dock decides which one is active. A layout is written out some milliseconds after the last change, carries its own persistence boundaries, and is read back through a parser that takes nothing on trust.",
      "dependencies": [
        "cn@^0.3.0",
        "dockview-react@^8.3.1"
      ],
      "registryDependencies": [
        "input"
      ],
      "files": [
        {
          "path": "registry/tradecn/ui/workspace.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/tradecn/ui/panel.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/tradecn/hooks/use-link-group.tsx",
          "type": "registry:hook"
        },
        {
          "path": "registry/tradecn/hooks/use-popout.ts",
          "type": "registry:hook"
        },
        {
          "path": "registry/tradecn/hooks/use-hotkeys.tsx",
          "type": "registry:hook"
        },
        {
          "path": "registry/tradecn/lib/workspace-layout.ts",
          "type": "registry:lib"
        },
        {
          "path": "registry/tradecn/lib/link-group.ts",
          "type": "registry:lib"
        },
        {
          "path": "registry/tradecn/lib/hotkeys.ts",
          "type": "registry:lib"
        }
      ],
      "categories": [
        "layout",
        "chrome"
      ],
      "docs": "Adds dockview-react, which brings dockview and dockview-core: three MIT packages from one repository and nothing under them. Installs panel and the hotkey registry alongside, composes your input, and adds the panel and link tokens. Appends one class to your stylesheet, dockview-theme-tradecn, that maps the dock onto your own tokens. A popout opens /popout.html on your origin, so serve one, or pass popoutUrl.",
      "meta": {
        "components": [
          {
            "title": "Workspace",
            "description": "The dock, with your panels by kind."
          },
          {
            "title": "useWorkspacePanel",
            "description": "Inside a panel: its id, kind, title, kept state, whether it is active, and close, float, pop out, maximize."
          }
        ]
      },
      "css": {
        "@layer components": {
          ".dockview-theme-tradecn": {
            "--dv-group-view-background-color": "var(--background)",
            "--dv-tabs-and-actions-container-background-color": "var(--muted)",
            "--dv-tabs-and-actions-container-font-size": "0.75rem",
            "--dv-tabs-and-actions-container-height": "1.75rem",
            "--dv-activegroup-visiblepanel-tab-background-color": "var(--background)",
            "--dv-activegroup-hiddenpanel-tab-background-color": "var(--muted)",
            "--dv-inactivegroup-visiblepanel-tab-background-color": "var(--background)",
            "--dv-inactivegroup-hiddenpanel-tab-background-color": "var(--muted)",
            "--dv-activegroup-visiblepanel-tab-color": "var(--foreground)",
            "--dv-activegroup-hiddenpanel-tab-color": "var(--muted-foreground)",
            "--dv-inactivegroup-visiblepanel-tab-color": "var(--muted-foreground)",
            "--dv-inactivegroup-hiddenpanel-tab-color": "var(--muted-foreground)",
            "--dv-tab-divider-color": "var(--border)",
            "--dv-tab-font-size": "inherit",
            "--dv-tab-margin": "0",
            "--dv-tab-border-radius": "0px",
            "--dv-tab-close-icon-size": "inherit",
            "--dv-tabs-container-scrollbar-color": "var(--border)",
            "--dv-separator-border": "var(--border)",
            "--dv-border-radius": "0px",
            "--dv-spacing-padding": "0px",
            "--dv-sash-color": "transparent",
            "--dv-sash-border-radius": "0px",
            "--dv-active-sash-color": "var(--ring)",
            "--dv-active-sash-transition-duration": "0.1s",
            "--dv-active-sash-transition-delay": "0.3s",
            "--dv-drag-over-background-color": "color-mix(in oklab, var(--ring) 20%, transparent)",
            "--dv-drag-over-border-color": "var(--ring)",
            "--dv-drag-over-border": "1px solid var(--ring)",
            "--dv-edge-dock-indicator-color": "var(--ring)",
            "--dv-icon-hover-background-color": "var(--accent)",
            "--dv-scrollbar-background-color": "var(--border)",
            "--dv-paneview-active-outline-color": "var(--ring)",
            "--dv-paneview-header-border-color": "var(--border)",
            "--dv-dropdown-border-radius": "var(--radius)",
            "--dv-overlay-z-index": "30",
            "--dv-floating-box-shadow": "0 8px 32px rgb(0 0 0 / 0.25), 0 2px 8px rgb(0 0 0 / 0.15)",
            "--dv-floating-border": "1px solid var(--border)",
            "--dv-floating-group-border": "1px solid var(--border)",
            "--dv-floating-group-dragging-opacity": "0.6",
            "--dv-floating-titlebar-height": "1.75rem",
            "--dv-floating-titlebar-background-color": "var(--muted)",
            "--dv-floating-titlebar-border-bottom": "1px solid var(--border)",
            "--dv-tab-group-color-grey": "var(--muted-foreground)",
            "--dv-tab-group-color-blue": "var(--chart-1)",
            "--dv-tab-group-color-red": "var(--chart-2)",
            "--dv-tab-group-color-yellow": "var(--chart-3)",
            "--dv-tab-group-color-green": "var(--chart-4)",
            "--dv-tab-group-color-pink": "var(--chart-5)",
            "--dv-tab-group-color-purple": "var(--chart-1)",
            "--dv-tab-group-color-cyan": "var(--chart-2)",
            "--dv-tab-group-color-orange": "var(--chart-3)",
            "--dv-tab-group-chip-padding": "0 0.375rem",
            "--dv-tab-group-chip-border-radius": "var(--radius)",
            "--dv-tab-group-chip-font-size": "0.6875rem",
            "--dv-tab-group-line-height": "2px",
            "--dv-tab-group-line-opacity": "0.6"
          }
        },
        ":root[data-accessibility=\"hyperlegible\"], [data-accessibility=\"hyperlegible\"]": {
          "--tradecn-font-sans": "var(--tradecn-font-accessible)",
          "--tradecn-font-mono": "var(--tradecn-font-accessible-mono)"
        }
      },
      "cssVars": {
        "light": {
          "link-1": "oklch(0.53 0.2 25)",
          "link-2": "oklch(0.5 0.15 250)",
          "link-3": "oklch(0.5 0.13 150)",
          "link-4": "oklch(0.52 0.12 85)",
          "panel-active": "var(--color-primary)",
          "panel-drag-target": "var(--color-ring)",
          "panel-error": "var(--color-destructive)",
          "panel-sync": "oklch(0.5 0.12 220)",
          "tradecn-font-accessible": "'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-accessible-mono": "'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-mono": "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace"
        },
        "dark": {
          "link-1": "oklch(0.7 0.19 25)",
          "link-2": "oklch(0.7 0.14 250)",
          "link-3": "oklch(0.74 0.14 150)",
          "link-4": "oklch(0.85 0.15 90)",
          "panel-active": "var(--color-primary)",
          "panel-drag-target": "var(--color-ring)",
          "panel-error": "var(--color-destructive)",
          "panel-sync": "oklch(0.78 0.12 220)",
          "tradecn-font-accessible": "'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-accessible-mono": "'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-mono": "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace"
        }
      }
    },
    {
      "name": "ticket",
      "type": "registry:block",
      "title": "Ticket",
      "description": "An order ticket that types a price the way the instrument quotes it (99-16+), steps it by the tick with the arrows and the buttons, and hands a checked draft to the action you named. Buttons are the actions the server allowed and nothing else; the status is the server word, printed as it is; an acknowledgement rings the ticket once in primary. Quick sizes as buttons under the quantity and on mod+1 to mod+9. Its keys work while you type in it, and inside a dialog.",
      "dependencies": [
        "cn@^0.3.0"
      ],
      "registryDependencies": [
        "button",
        "button-group",
        "field",
        "input",
        "input-group",
        "kbd",
        "native-select"
      ],
      "files": [
        {
          "path": "registry/tradecn/blocks/ticket/ticket.tsx",
          "type": "registry:block"
        },
        {
          "path": "registry/tradecn/ui/quote-field.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/tradecn/hooks/use-flash.ts",
          "type": "registry:hook"
        },
        {
          "path": "registry/tradecn/hooks/use-hotkeys.tsx",
          "type": "registry:hook"
        },
        {
          "path": "registry/tradecn/lib/format.ts",
          "type": "registry:lib"
        },
        {
          "path": "registry/tradecn/lib/hotkeys.ts",
          "type": "registry:lib"
        },
        {
          "path": "registry/tradecn/lib/limits.ts",
          "type": "registry:lib"
        }
      ],
      "categories": [
        "ticket"
      ],
      "docs": "The registry first block: ticket.tsx goes to your components alias, not components/ui. Installs quote-field, format, use-flash, and the hotkey registry alongside, composes your button, button-group, field, input, input-group, kbd, and native-select, and adds the up and down tokens. Inside a HotkeysProvider it declares ticket.send (mod+enter), ticket.flip, ticket.tick-up, ticket.tick-down, and ticket.size-1 to ticket.size-9 (mod+1 to mod+9, the quick sizes) as editing bindings unless you did.",
      "meta": {
        "components": [
          {
            "title": "Ticket",
            "description": "The ticket."
          },
          {
            "title": "TICKET_BINDINGS",
            "description": "Its four editing bindings, to spread into your own list or leave to it."
          },
          {
            "title": "describeDraft",
            "description": "A draft in words: Buy 5 ZN @ 99-16+."
          },
          {
            "title": "checkDraft",
            "description": "What stops a draft from being sent, as a pure function."
          },
          {
            "title": "parseQuantity",
            "description": "Whole numbers with or without separators, or null."
          }
        ]
      },
      "cssVars": {
        "light": {
          "down": "oklch(0.46 0.19 40)",
          "down-soft": "oklch(0.46 0.19 40 / 18%)",
          "flat": "var(--color-muted-foreground)",
          "flat-soft": "oklch(0.55 0 0 / 14%)",
          "stale": "oklch(0.52 0.13 75)",
          "tradecn-font-accessible": "'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-accessible-mono": "'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-mono": "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-numeric": "var(--tradecn-font-sans)",
          "tradecn-font-sans": "'Inter', ui-sans-serif, system-ui, sans-serif",
          "up": "oklch(0.52 0.13 165)",
          "up-soft": "oklch(0.52 0.13 165 / 18%)"
        },
        "dark": {
          "down": "oklch(0.66 0.19 40)",
          "down-soft": "oklch(0.66 0.19 40 / 18%)",
          "flat": "var(--color-muted-foreground)",
          "flat-soft": "oklch(0.7 0 0 / 14%)",
          "stale": "oklch(0.8 0.15 80)",
          "tradecn-font-accessible": "'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-accessible-mono": "'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-mono": "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-numeric": "var(--tradecn-font-sans)",
          "tradecn-font-sans": "'Inter', ui-sans-serif, system-ui, sans-serif",
          "up": "oklch(0.74 0.15 165)",
          "up-soft": "oklch(0.74 0.15 165 / 18%)"
        }
      },
      "css": {
        ":root[data-accessibility=\"hyperlegible\"], [data-accessibility=\"hyperlegible\"]": {
          "--tradecn-font-sans": "var(--tradecn-font-accessible)",
          "--tradecn-font-mono": "var(--tradecn-font-accessible-mono)"
        }
      }
    },
    {
      "name": "countdown",
      "type": "registry:ui",
      "title": "Countdown",
      "description": "Time left to a moment: digits that tick once a second on one shared clock, and a bar that shrinks to nothing on one animation the compositor runs, turning in the last seconds and stopping at zero. It rounds up, so it never says zero while time is left, and it decides nothing: the server's word says whether the thing it timed is over.",
      "dependencies": [
        "cn@^0.3.0"
      ],
      "registryDependencies": [],
      "files": [
        {
          "path": "registry/tradecn/ui/countdown.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/tradecn/hooks/use-clock.ts",
          "type": "registry:hook"
        },
        {
          "path": "registry/tradecn/lib/clock.ts",
          "type": "registry:lib"
        }
      ],
      "categories": [
        "rfq",
        "ticket"
      ],
      "docs": "Adds the expiring and expiring-soft tokens and installs the shared one-second clock alongside, the same file feed-health ticks on. The ten-second soon threshold is a placeholder: agree the real one with the people who work the screen and pass it in.",
      "meta": {
        "components": [
          {
            "title": "Countdown",
            "description": "The digits and the bar."
          },
          {
            "title": "countdownTier",
            "description": "Pure tiering: plenty, soon, expired."
          },
          {
            "title": "formatRemaining",
            "description": "Time left as digits, rounded up."
          },
          {
            "title": "PROVISIONAL_COUNTDOWN_THRESHOLDS",
            "description": "The placeholder soon threshold, named so nobody mistakes it for a decision."
          }
        ]
      },
      "cssVars": {
        "light": {
          "expiring": "oklch(0.53 0.2 350)",
          "expiring-soft": "oklch(0.53 0.2 350 / 18%)"
        },
        "dark": {
          "expiring": "oklch(0.74 0.17 350)",
          "expiring-soft": "oklch(0.74 0.17 350 / 18%)"
        }
      }
    },
    {
      "name": "quote-field",
      "type": "registry:ui",
      "title": "Quote Field",
      "description": "A field that types a quote the way the instrument quotes it: 99-16+ for a note on price, 4.253 for a bill on discount, 12.6 for credit on a spread. It parses on every keystroke and hands the number up, marks what is not a quote on blur and prints a good one back in the notation, and steps by the instrument's own step with the arrows and two buttons, ten with Shift. A held modifier leaves the arrows to the registry above it.",
      "dependencies": [
        "cn@^0.3.0"
      ],
      "registryDependencies": [
        "field",
        "input-group"
      ],
      "files": [
        {
          "path": "registry/tradecn/ui/quote-field.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/tradecn/lib/format.ts",
          "type": "registry:lib"
        }
      ],
      "categories": [
        "ticket",
        "rfq"
      ],
      "docs": "Installs format alongside and composes your field and input-group. Controlled: the value is yours and the text follows it. It declares no keys and checks nothing but whether the text is a quote; what your ticket's check finds goes in error.",
      "meta": {
        "components": [
          {
            "title": "QuoteField",
            "description": "The field."
          }
        ]
      },
      "cssVars": {
        "light": {
          "tradecn-font-accessible": "'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-accessible-mono": "'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-mono": "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-numeric": "var(--tradecn-font-sans)",
          "tradecn-font-sans": "'Inter', ui-sans-serif, system-ui, sans-serif"
        },
        "dark": {
          "tradecn-font-accessible": "'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-accessible-mono": "'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-mono": "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-numeric": "var(--tradecn-font-sans)",
          "tradecn-font-sans": "'Inter', ui-sans-serif, system-ui, sans-serif"
        }
      },
      "css": {
        ":root[data-accessibility=\"hyperlegible\"], [data-accessibility=\"hyperlegible\"]": {
          "--tradecn-font-sans": "var(--tradecn-font-accessible)",
          "--tradecn-font-mono": "var(--tradecn-font-accessible-mono)"
        }
      }
    },
    {
      "name": "rfq-ticket",
      "type": "registry:block",
      "title": "RFQ Ticket",
      "description": "A dealer's ticket for a request for quote: the inquiry as it came (who, which way, how much, in what, with the venue's words on it), the market beside it, the levels already quoted, the auto price when there is one, a countdown to its end, and the fields to type a level in the instrument's own basis. The buttons are the actions the server allowed, checked again as the click lands; the status is the venue's word, printed as it is. One ticket is one inquiry, so a new one never lands in a ticket being worked. Quick sizes, the inquiry's own first, as buttons and mod+1 to mod+9, and the draft carries the size the quote is for.",
      "dependencies": [
        "cn@^0.3.0"
      ],
      "registryDependencies": [
        "badge",
        "button",
        "field",
        "input-group",
        "kbd"
      ],
      "files": [
        {
          "path": "registry/tradecn/blocks/rfq-ticket/rfq-ticket.tsx",
          "type": "registry:block"
        },
        {
          "path": "registry/tradecn/ui/quote-field.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/tradecn/ui/countdown.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/tradecn/hooks/use-clock.ts",
          "type": "registry:hook"
        },
        {
          "path": "registry/tradecn/hooks/use-flash.ts",
          "type": "registry:hook"
        },
        {
          "path": "registry/tradecn/hooks/use-hotkeys.tsx",
          "type": "registry:hook"
        },
        {
          "path": "registry/tradecn/lib/clock.ts",
          "type": "registry:lib"
        },
        {
          "path": "registry/tradecn/lib/format.ts",
          "type": "registry:lib"
        },
        {
          "path": "registry/tradecn/lib/hotkeys.ts",
          "type": "registry:lib"
        },
        {
          "path": "registry/tradecn/lib/limits.ts",
          "type": "registry:lib"
        }
      ],
      "categories": [
        "rfq",
        "ticket"
      ],
      "docs": "A block: rfq-ticket.tsx goes to your components alias. Installs quote-field, countdown, format, use-flash, the shared clock, and the hotkey registry alongside, composes your badge, button, field, input-group, and kbd, and adds the up, down, flat, and expiring tokens. Inside a HotkeysProvider it declares rfq.send (mod+enter), rfq.tick-up, rfq.tick-down, and rfq.suggested (mod+shift+a) as editing bindings unless you did. Mount it with key={inquiry.id}.",
      "meta": {
        "components": [
          {
            "title": "RfqTicket",
            "description": "The ticket."
          },
          {
            "title": "RFQ_TICKET_BINDINGS",
            "description": "Its four editing bindings, to spread into your own list or leave to it."
          },
          {
            "title": "checkQuote",
            "description": "What stops a quote from being sent, as a pure function."
          },
          {
            "title": "describeQuote",
            "description": "A quote in words: Offer 5mm T 4 1/8 05/15/34 @ 99-16+."
          },
          {
            "title": "quoteDistance",
            "description": "How far a level sits from the market, in ticks or basis points."
          },
          {
            "title": "quotedSides",
            "description": "Which sides the dealer quotes for a client who buys, sells, or asks a market."
          },
          {
            "title": "formatSize",
            "description": "The size as the desk says it: millions, or contracts."
          }
        ]
      },
      "cssVars": {
        "light": {
          "down": "oklch(0.46 0.19 40)",
          "down-soft": "oklch(0.46 0.19 40 / 18%)",
          "expiring": "oklch(0.53 0.2 350)",
          "expiring-soft": "oklch(0.53 0.2 350 / 18%)",
          "flat": "var(--color-muted-foreground)",
          "flat-soft": "oklch(0.55 0 0 / 14%)",
          "stale": "oklch(0.52 0.13 75)",
          "tradecn-font-accessible": "'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-accessible-mono": "'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-mono": "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-numeric": "var(--tradecn-font-sans)",
          "tradecn-font-sans": "'Inter', ui-sans-serif, system-ui, sans-serif",
          "up": "oklch(0.52 0.13 165)",
          "up-soft": "oklch(0.52 0.13 165 / 18%)"
        },
        "dark": {
          "down": "oklch(0.66 0.19 40)",
          "down-soft": "oklch(0.66 0.19 40 / 18%)",
          "expiring": "oklch(0.74 0.17 350)",
          "expiring-soft": "oklch(0.74 0.17 350 / 18%)",
          "flat": "var(--color-muted-foreground)",
          "flat-soft": "oklch(0.7 0 0 / 14%)",
          "stale": "oklch(0.8 0.15 80)",
          "tradecn-font-accessible": "'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-accessible-mono": "'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-mono": "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-numeric": "var(--tradecn-font-sans)",
          "tradecn-font-sans": "'Inter', ui-sans-serif, system-ui, sans-serif",
          "up": "oklch(0.74 0.15 165)",
          "up-soft": "oklch(0.74 0.15 165 / 18%)"
        }
      },
      "css": {
        ":root[data-accessibility=\"hyperlegible\"], [data-accessibility=\"hyperlegible\"]": {
          "--tradecn-font-sans": "var(--tradecn-font-accessible)",
          "--tradecn-font-mono": "var(--tradecn-font-accessible-mono)"
        }
      }
    },
    {
      "name": "rfq-stack",
      "type": "registry:ui",
      "title": "RFQ Stack",
      "description": "The stack of open inquiries: the data grid in its RFQ preset with an inquiry's columns, a compact countdown in every row on one shared clock, a threshold that hides the small auto-quoted ones, and a mark on the one in the ticket. The order is the desk's, held still under a hand; an arrival never moves the viewport, the focus, the mark, or the order. With it, useActiveInquiry: the one rule a stack and a ticket agree on, that the active inquiry stays until the trader acts or the server ends it, and an arrival never changes it.",
      "dependencies": [
        "cn@^0.3.0",
        "@tanstack/react-virtual@^3.14.13"
      ],
      "registryDependencies": [
        "checkbox",
        "context-menu",
        "dropdown-menu",
        "input"
      ],
      "files": [
        {
          "path": "registry/tradecn/ui/rfq-stack.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/tradecn/ui/data-grid.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/tradecn/ui/countdown.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/tradecn/hooks/use-active-inquiry.ts",
          "type": "registry:hook"
        },
        {
          "path": "registry/tradecn/hooks/use-clock.ts",
          "type": "registry:hook"
        },
        {
          "path": "registry/tradecn/hooks/use-flash.ts",
          "type": "registry:hook"
        },
        {
          "path": "registry/tradecn/hooks/use-row-store.ts",
          "type": "registry:hook"
        },
        {
          "path": "registry/tradecn/lib/clock.ts",
          "type": "registry:lib"
        },
        {
          "path": "registry/tradecn/lib/format.ts",
          "type": "registry:lib"
        },
        {
          "path": "registry/tradecn/lib/row-store.ts",
          "type": "registry:lib"
        },
        {
          "path": "registry/tradecn/lib/grid-rules.ts",
          "type": "registry:lib"
        }
      ],
      "categories": [
        "rfq",
        "grid"
      ],
      "docs": "Installs the data grid, the countdown, the row store, format, and useActiveInquiry alongside, adds @tanstack/react-virtual and the up, down, flat, and expiring tokens, and composes your input plus the checkbox, context-menu, and dropdown-menu the grid uses. The order and the threshold are yours. The hook decides nothing about an inquiry being over: it reads the server's word through isEnded.",
      "meta": {
        "components": [
          {
            "title": "RfqStack",
            "description": "The grid, the threshold field, and the active mark."
          },
          {
            "title": "useRfqStackView",
            "description": "The desk's order with the threshold folded in, for the stack and useActiveInquiry to share."
          },
          {
            "title": "rfqStackColumns",
            "description": "The default columns, as a list to spread into your own."
          },
          {
            "title": "rfqThresholdFilter",
            "description": "Hides an auto-quoted inquiry under a size; one a person answers always shows."
          },
          {
            "title": "stackOrder",
            "description": "One comparator from several: the first that tells two apart decides."
          },
          {
            "title": "bySize",
            "description": "Largest first."
          },
          {
            "title": "byTimeLeft",
            "description": "Soonest to end first."
          },
          {
            "title": "byArrival",
            "description": "Newest first."
          },
          {
            "title": "formatStackSize",
            "description": "The size as the desk says it: millions, or contracts."
          }
        ]
      },
      "cssVars": {
        "light": {
          "down": "oklch(0.46 0.19 40)",
          "down-soft": "oklch(0.46 0.19 40 / 18%)",
          "expiring": "oklch(0.53 0.2 350)",
          "expiring-soft": "oklch(0.53 0.2 350 / 18%)",
          "flat": "var(--color-muted-foreground)",
          "flat-soft": "oklch(0.55 0 0 / 14%)",
          "stale": "oklch(0.52 0.13 75)",
          "stale-soft": "oklch(0.52 0.13 75 / 16%)",
          "tradecn-font-accessible": "'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-accessible-mono": "'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-mono": "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-numeric": "var(--tradecn-font-sans)",
          "tradecn-font-sans": "'Inter', ui-sans-serif, system-ui, sans-serif",
          "up": "oklch(0.52 0.13 165)",
          "up-soft": "oklch(0.52 0.13 165 / 18%)"
        },
        "dark": {
          "down": "oklch(0.66 0.19 40)",
          "down-soft": "oklch(0.66 0.19 40 / 18%)",
          "expiring": "oklch(0.74 0.17 350)",
          "expiring-soft": "oklch(0.74 0.17 350 / 18%)",
          "flat": "var(--color-muted-foreground)",
          "flat-soft": "oklch(0.7 0 0 / 14%)",
          "stale": "oklch(0.8 0.15 80)",
          "stale-soft": "oklch(0.8 0.15 80 / 16%)",
          "tradecn-font-accessible": "'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-accessible-mono": "'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-mono": "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-numeric": "var(--tradecn-font-sans)",
          "tradecn-font-sans": "'Inter', ui-sans-serif, system-ui, sans-serif",
          "up": "oklch(0.74 0.15 165)",
          "up-soft": "oklch(0.74 0.15 165 / 18%)"
        }
      },
      "css": {
        ":root[data-accessibility=\"hyperlegible\"], [data-accessibility=\"hyperlegible\"]": {
          "--tradecn-font-sans": "var(--tradecn-font-accessible)",
          "--tradecn-font-mono": "var(--tradecn-font-accessible-mono)"
        }
      }
    },
    {
      "name": "perf-monitor",
      "type": "registry:ui",
      "title": "Perf Monitor",
      "description": "The frame rate on the screen it measures: frames in the window, p50, p99, max, dropped (a gap past one and a half budgets), and long tasks where the browser reports them, a histogram of frame gaps against the budget, and a line per lane from its row store (rows, batches a second, drops or sequence and gap, age). One number into a ring per frame and a redraw four times a second, so watching costs almost nothing. It decides nothing: the numbers are read on the machine that matters, against a threshold written down beforehand.",
      "dependencies": [
        "cn@^0.3.0"
      ],
      "registryDependencies": [],
      "files": [
        {
          "path": "registry/tradecn/ui/perf-monitor.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/tradecn/lib/frame-stats.ts",
          "type": "registry:lib"
        }
      ],
      "categories": [
        "feed",
        "chrome"
      ],
      "docs": "Installs frame-stats alongside (the sampler and the arithmetic as pure functions) and adds the stale token for an open gap. Composes nothing of shadcn's. Make the sampler yourself and pass it to read report() from a harness or a gate; the monitor starts it while mounted and stops it after.",
      "meta": {
        "components": [
          {
            "title": "PerfMonitor",
            "description": "The numbers, the histogram, and the lanes."
          }
        ]
      },
      "cssVars": {
        "light": {
          "stale": "oklch(0.52 0.13 75)",
          "tradecn-font-accessible": "'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-accessible-mono": "'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-mono": "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace"
        },
        "dark": {
          "stale": "oklch(0.8 0.15 80)",
          "tradecn-font-accessible": "'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-accessible-mono": "'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-mono": "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace"
        }
      },
      "css": {
        ":root[data-accessibility=\"hyperlegible\"], [data-accessibility=\"hyperlegible\"]": {
          "--tradecn-font-sans": "var(--tradecn-font-accessible)",
          "--tradecn-font-mono": "var(--tradecn-font-accessible-mono)"
        }
      }
    },
    {
      "name": "hotkey-editor",
      "type": "registry:ui",
      "title": "Hotkey Editor",
      "description": "The settings screen for the hotkey registry: every binding under its group with its keys in force, a field that finds one by its words or its keys, and three ways to change one: press the new shortcut, type a chord as text, or reset it. Conflicts are said under the rows they touch, in words. Every change goes through the registry, so the app's persistence hears it through onChange; the editor keeps nothing of its own.",
      "dependencies": [
        "cn@^0.3.0"
      ],
      "registryDependencies": [
        "badge",
        "button",
        "input",
        "kbd"
      ],
      "files": [
        {
          "path": "registry/tradecn/ui/hotkey-editor.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/tradecn/hooks/use-hotkeys.tsx",
          "type": "registry:hook"
        },
        {
          "path": "registry/tradecn/lib/hotkeys.ts",
          "type": "registry:lib"
        }
      ],
      "categories": [
        "hotkeys"
      ],
      "docs": "Installs the hotkey registry alongside and composes your badge, button, input, and kbd. Needs a HotkeysProvider above it. Export hands you the overrides; Import calls you back to open your picker and registry.load what you read. Adds the stale token for the conflict lines.",
      "meta": {
        "components": [
          {
            "title": "HotkeyEditor",
            "description": "The list, the search, and the three ways to change a shortcut."
          },
          {
            "title": "DEFAULT_HOTKEY_EDITOR_LABELS",
            "description": "Every word on it, to replace in part."
          },
          {
            "title": "matchesQuery",
            "description": "Whether a binding answers a search, by words or by keys."
          },
          {
            "title": "groupOf",
            "description": "The group a binding is listed under."
          },
          {
            "title": "scopeWord",
            "description": "The word for a scope on a row."
          }
        ]
      },
      "cssVars": {
        "light": {
          "stale": "oklch(0.52 0.13 75)",
          "tradecn-font-accessible": "'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-accessible-mono": "'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-mono": "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace"
        },
        "dark": {
          "stale": "oklch(0.8 0.15 80)",
          "tradecn-font-accessible": "'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-accessible-mono": "'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-mono": "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace"
        }
      },
      "css": {
        ":root[data-accessibility=\"hyperlegible\"], [data-accessibility=\"hyperlegible\"]": {
          "--tradecn-font-sans": "var(--tradecn-font-accessible)",
          "--tradecn-font-mono": "var(--tradecn-font-accessible-mono)"
        }
      }
    },
    {
      "name": "column-chooser",
      "type": "registry:ui",
      "title": "Column Chooser",
      "description": "The dialog over one grid's columns: every column with show and hide, reorder by drag or by keyboard, a width reset where a column was resized, a search box, frozen columns marked, and reset all. It reads and writes the grid's own column state and stores nothing, and a rule that names a column is said in words beside it.",
      "categories": [
        "grid"
      ],
      "dependencies": [
        "cn@^0.3.0",
        "@tanstack/react-virtual@^3.14.13"
      ],
      "registryDependencies": [
        "badge",
        "button",
        "checkbox",
        "context-menu",
        "dialog",
        "dropdown-menu",
        "input"
      ],
      "files": [
        {
          "path": "registry/tradecn/ui/column-chooser.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/tradecn/ui/data-grid.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/tradecn/hooks/use-flash.ts",
          "type": "registry:hook"
        },
        {
          "path": "registry/tradecn/hooks/use-row-store.ts",
          "type": "registry:hook"
        },
        {
          "path": "registry/tradecn/lib/row-store.ts",
          "type": "registry:lib"
        },
        {
          "path": "registry/tradecn/lib/format.ts",
          "type": "registry:lib"
        },
        {
          "path": "registry/tradecn/lib/grid-rules.ts",
          "type": "registry:lib"
        }
      ],
      "meta": {
        "components": [
          {
            "title": "ColumnChooser",
            "description": "The panel in your dialog, a hotkey wall while it is open."
          },
          {
            "title": "ColumnChooserPanel",
            "description": "The list inline: for a sheet, a tab, or a settings page."
          },
          {
            "title": "DEFAULT_COLUMN_CHOOSER_LABELS",
            "description": "Every word on it, to replace in part."
          },
          {
            "title": "chooserRows",
            "description": "The columns as the chooser lists them: the grid's order, frozen first, hidden in place."
          },
          {
            "title": "moveColumnTo",
            "description": "The state with one column in another's place, on its own side of the frozen line."
          },
          {
            "title": "moveColumnBy",
            "description": "The state with one column one place up or down."
          },
          {
            "title": "setColumnVisible",
            "description": "The state with one column shown or hidden."
          },
          {
            "title": "resetColumnWidth",
            "description": "The state with one column's width forgotten."
          },
          {
            "title": "isDefaultColumnState",
            "description": "True when the state changes nothing."
          }
        ]
      },
      "docs": "Installs the data grid alongside, since its column state is what this edits, and composes your badge, button, checkbox, dialog, and input plus the checkbox, context-menu, and dropdown-menu the grid uses. Adds @tanstack/react-virtual and the grid's tokens.",
      "cssVars": {
        "light": {
          "down": "oklch(0.46 0.19 40)",
          "down-soft": "oklch(0.46 0.19 40 / 18%)",
          "expiring": "oklch(0.53 0.2 350)",
          "expiring-soft": "oklch(0.53 0.2 350 / 18%)",
          "flat": "var(--color-muted-foreground)",
          "flat-soft": "oklch(0.55 0 0 / 14%)",
          "stale": "oklch(0.52 0.13 75)",
          "stale-soft": "oklch(0.52 0.13 75 / 16%)",
          "tradecn-font-accessible": "'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-accessible-mono": "'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-mono": "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-numeric": "var(--tradecn-font-sans)",
          "tradecn-font-sans": "'Inter', ui-sans-serif, system-ui, sans-serif",
          "up": "oklch(0.52 0.13 165)",
          "up-soft": "oklch(0.52 0.13 165 / 18%)"
        },
        "dark": {
          "down": "oklch(0.66 0.19 40)",
          "down-soft": "oklch(0.66 0.19 40 / 18%)",
          "expiring": "oklch(0.74 0.17 350)",
          "expiring-soft": "oklch(0.74 0.17 350 / 18%)",
          "flat": "var(--color-muted-foreground)",
          "flat-soft": "oklch(0.7 0 0 / 14%)",
          "stale": "oklch(0.8 0.15 80)",
          "stale-soft": "oklch(0.8 0.15 80 / 16%)",
          "tradecn-font-accessible": "'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-accessible-mono": "'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-mono": "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-numeric": "var(--tradecn-font-sans)",
          "tradecn-font-sans": "'Inter', ui-sans-serif, system-ui, sans-serif",
          "up": "oklch(0.74 0.15 165)",
          "up-soft": "oklch(0.74 0.15 165 / 18%)"
        }
      },
      "css": {
        ":root[data-accessibility=\"hyperlegible\"], [data-accessibility=\"hyperlegible\"]": {
          "--tradecn-font-sans": "var(--tradecn-font-accessible)",
          "--tradecn-font-mono": "var(--tradecn-font-accessible-mono)"
        }
      }
    },
    {
      "name": "rules-editor",
      "type": "registry:ui",
      "title": "Rules Editor",
      "description": "The editor over one grid's rules: highlights, filters, and the sort stack as lists to add to, edit, reorder, and remove, each rule built from a column picker, an op picker narrowed by the column's kind, a value typed in the column's own format, and for a highlight a tone shown in its token. A live count beside each rule says how many rows it matches right now. The column chooser sits in a fourth tab. It produces the rules objects and stores nothing.",
      "categories": [
        "grid"
      ],
      "dependencies": [
        "cn@^0.3.0",
        "@tanstack/react-virtual@^3.14.13"
      ],
      "registryDependencies": [
        "badge",
        "button",
        "checkbox",
        "context-menu",
        "dialog",
        "dropdown-menu",
        "input",
        "native-select"
      ],
      "files": [
        {
          "path": "registry/tradecn/ui/rules-editor.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/tradecn/ui/column-chooser.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/tradecn/ui/data-grid.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/tradecn/hooks/use-flash.ts",
          "type": "registry:hook"
        },
        {
          "path": "registry/tradecn/hooks/use-row-store.ts",
          "type": "registry:hook"
        },
        {
          "path": "registry/tradecn/lib/row-store.ts",
          "type": "registry:lib"
        },
        {
          "path": "registry/tradecn/lib/format.ts",
          "type": "registry:lib"
        },
        {
          "path": "registry/tradecn/lib/grid-rules.ts",
          "type": "registry:lib"
        }
      ],
      "meta": {
        "components": [
          {
            "title": "RulesEditor",
            "description": "The four tabs: highlights, filters, sort, and the column chooser."
          },
          {
            "title": "DEFAULT_RULES_EDITOR_LABELS",
            "description": "Every word on it, to replace in part."
          },
          {
            "title": "valueShape",
            "description": "What an op wants typed: one value, two, many, or nothing."
          },
          {
            "title": "withOp",
            "description": "A condition under a new op, the typed value kept when the shape is the same."
          },
          {
            "title": "withColumn",
            "description": "A condition on a new column, its op if the column offers it."
          },
          {
            "title": "parseValues",
            "description": "A comma-separated field as a list of values."
          },
          {
            "title": "valuesText",
            "description": "A list of values as the field's text."
          },
          {
            "title": "moveItem",
            "description": "A list with one item moved to another's index."
          },
          {
            "title": "newHighlight",
            "description": "A new highlight on the first column, its first op, in up."
          },
          {
            "title": "newFilter",
            "description": "A new filter on the first column, its first op."
          },
          {
            "title": "newSort",
            "description": "A new sort key on the first column not yet in the stack."
          },
          {
            "title": "newRuleId",
            "description": "An id for a new highlight, unique on this page."
          }
        ]
      },
      "docs": "Installs the column chooser and the data grid alongside and composes your button, input, and native-select, plus the badge, checkbox, dialog, context-menu, and dropdown-menu they use. Adds @tanstack/react-virtual and the grid's tokens. Hand the rules it produces to the grid's rules prop.",
      "cssVars": {
        "light": {
          "down": "oklch(0.46 0.19 40)",
          "down-soft": "oklch(0.46 0.19 40 / 18%)",
          "expiring": "oklch(0.53 0.2 350)",
          "expiring-soft": "oklch(0.53 0.2 350 / 18%)",
          "flat": "var(--color-muted-foreground)",
          "flat-soft": "oklch(0.55 0 0 / 14%)",
          "stale": "oklch(0.52 0.13 75)",
          "stale-soft": "oklch(0.52 0.13 75 / 16%)",
          "tradecn-font-accessible": "'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-accessible-mono": "'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-mono": "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-numeric": "var(--tradecn-font-sans)",
          "tradecn-font-sans": "'Inter', ui-sans-serif, system-ui, sans-serif",
          "up": "oklch(0.52 0.13 165)",
          "up-soft": "oklch(0.52 0.13 165 / 18%)"
        },
        "dark": {
          "down": "oklch(0.66 0.19 40)",
          "down-soft": "oklch(0.66 0.19 40 / 18%)",
          "expiring": "oklch(0.74 0.17 350)",
          "expiring-soft": "oklch(0.74 0.17 350 / 18%)",
          "flat": "var(--color-muted-foreground)",
          "flat-soft": "oklch(0.7 0 0 / 14%)",
          "stale": "oklch(0.8 0.15 80)",
          "stale-soft": "oklch(0.8 0.15 80 / 16%)",
          "tradecn-font-accessible": "'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-accessible-mono": "'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-mono": "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-numeric": "var(--tradecn-font-sans)",
          "tradecn-font-sans": "'Inter', ui-sans-serif, system-ui, sans-serif",
          "up": "oklch(0.74 0.15 165)",
          "up-soft": "oklch(0.74 0.15 165 / 18%)"
        }
      },
      "css": {
        ":root[data-accessibility=\"hyperlegible\"], [data-accessibility=\"hyperlegible\"]": {
          "--tradecn-font-sans": "var(--tradecn-font-accessible)",
          "--tradecn-font-mono": "var(--tradecn-font-accessible-mono)"
        }
      }
    },
    {
      "name": "alerts",
      "type": "registry:ui",
      "title": "Alerts",
      "description": "The strip of notices: the newest few, each with the severity as your word and the tone as a bar beside it, the count when one notice stands for many, the actions the server allowed, a dismiss, a clear-all, and a button that opens the whole list in the data grid. Over a store that coalesces by key and keeps to a cap, so a thousand events at the close are a few rows. It never takes focus; a screen reader hears a new notice politely, and at once only for a severity you name. With it, useToastBridge forwards each new notice to whatever toast you have.",
      "categories": [
        "feed",
        "chrome"
      ],
      "dependencies": [
        "cn@^0.3.0",
        "@tanstack/react-virtual@^3.14.13"
      ],
      "registryDependencies": [
        "badge",
        "button",
        "checkbox",
        "context-menu",
        "dialog",
        "dropdown-menu"
      ],
      "files": [
        {
          "path": "registry/tradecn/ui/alerts.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/tradecn/lib/alert-store.ts",
          "type": "registry:lib"
        },
        {
          "path": "registry/tradecn/ui/data-grid.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/tradecn/hooks/use-flash.ts",
          "type": "registry:hook"
        },
        {
          "path": "registry/tradecn/hooks/use-row-store.ts",
          "type": "registry:hook"
        },
        {
          "path": "registry/tradecn/lib/row-store.ts",
          "type": "registry:lib"
        },
        {
          "path": "registry/tradecn/lib/format.ts",
          "type": "registry:lib"
        },
        {
          "path": "registry/tradecn/lib/grid-rules.ts",
          "type": "registry:lib"
        }
      ],
      "meta": {
        "components": [
          {
            "title": "Alerts",
            "description": "The strip, the more button, and the dialog with the whole list."
          },
          {
            "title": "AlertList",
            "description": "The whole list in the data grid, newest first, for a panel of your own."
          },
          {
            "title": "alertColumns",
            "description": "Time, severity, title, message, count, as a list to spread."
          },
          {
            "title": "useAlertView",
            "description": "A view of the store newest first."
          },
          {
            "title": "useToastBridge",
            "description": "Forwards each new notice to your toast function; imports no toast package."
          },
          {
            "title": "ALERT_TONE_BAR",
            "description": "The bar's class per tone."
          },
          {
            "title": "ALERT_TONE_TEXT",
            "description": "The severity's class per tone."
          },
          {
            "title": "DEFAULT_ALERTS_LABELS",
            "description": "Every word on it, to replace in part."
          }
        ]
      },
      "docs": "Installs the alert store and the data grid alongside and composes your badge, button, and dialog plus the checkbox, context-menu, and dropdown-menu the grid uses. Adds @tanstack/react-virtual and the up, down, flat, stale, and expiring tokens for the tones. Imports no toast package: useToastBridge takes your function.",
      "cssVars": {
        "light": {
          "down": "oklch(0.46 0.19 40)",
          "down-soft": "oklch(0.46 0.19 40 / 18%)",
          "expiring": "oklch(0.53 0.2 350)",
          "expiring-soft": "oklch(0.53 0.2 350 / 18%)",
          "flat": "var(--color-muted-foreground)",
          "flat-soft": "oklch(0.55 0 0 / 14%)",
          "stale": "oklch(0.52 0.13 75)",
          "stale-soft": "oklch(0.52 0.13 75 / 16%)",
          "tradecn-font-accessible": "'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-accessible-mono": "'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-mono": "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-numeric": "var(--tradecn-font-sans)",
          "tradecn-font-sans": "'Inter', ui-sans-serif, system-ui, sans-serif",
          "up": "oklch(0.52 0.13 165)",
          "up-soft": "oklch(0.52 0.13 165 / 18%)"
        },
        "dark": {
          "down": "oklch(0.66 0.19 40)",
          "down-soft": "oklch(0.66 0.19 40 / 18%)",
          "expiring": "oklch(0.74 0.17 350)",
          "expiring-soft": "oklch(0.74 0.17 350 / 18%)",
          "flat": "var(--color-muted-foreground)",
          "flat-soft": "oklch(0.7 0 0 / 14%)",
          "stale": "oklch(0.8 0.15 80)",
          "stale-soft": "oklch(0.8 0.15 80 / 16%)",
          "tradecn-font-accessible": "'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-accessible-mono": "'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-mono": "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-numeric": "var(--tradecn-font-sans)",
          "tradecn-font-sans": "'Inter', ui-sans-serif, system-ui, sans-serif",
          "up": "oklch(0.74 0.15 165)",
          "up-soft": "oklch(0.74 0.15 165 / 18%)"
        }
      },
      "css": {
        ":root[data-accessibility=\"hyperlegible\"], [data-accessibility=\"hyperlegible\"]": {
          "--tradecn-font-sans": "var(--tradecn-font-accessible)",
          "--tradecn-font-mono": "var(--tradecn-font-accessible-mono)"
        }
      }
    },
    {
      "name": "status-bar",
      "type": "registry:ui",
      "title": "Status Bar",
      "description": "The strip at the bottom of every terminal: which environment this is, in a word and a tone that reads at a glance; what time it is where the markets are, on one shared one-second clock; who is signed in; and your own readouts in the slots between. Every string is the consumer's, and it imports none of the items that usually sit in it.",
      "categories": [
        "chrome"
      ],
      "dependencies": [
        "cn@^0.3.0"
      ],
      "registryDependencies": [
        "badge"
      ],
      "files": [
        {
          "path": "registry/tradecn/ui/status-bar.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/tradecn/hooks/use-clock.ts",
          "type": "registry:hook"
        },
        {
          "path": "registry/tradecn/lib/clock.ts",
          "type": "registry:lib"
        },
        {
          "path": "registry/tradecn/lib/format.ts",
          "type": "registry:lib"
        }
      ],
      "meta": {
        "components": [
          {
            "title": "StatusBar",
            "description": "The bar: environment, your left, your center, the clocks, the user, your right."
          },
          {
            "title": "STATUS_TONE_CLASS",
            "description": "The environment badge's classes per tone."
          },
          {
            "title": "formatClock",
            "description": "The time in a zone, or the null token for a zone the runtime does not know."
          },
          {
            "title": "clockFormat",
            "description": "The cached formatter for a zone."
          },
          {
            "title": "DEFAULT_STATUS_BAR_LABELS",
            "description": "Every word on it, to replace in part."
          }
        ]
      },
      "docs": "Installs the shared clock and format alongside and composes your badge. Adds the up, down, flat, stale, and expiring tokens for the environment's tone. Put feed-health and perf-monitor in its slots yourself; it does not import them.",
      "cssVars": {
        "light": {
          "down": "oklch(0.46 0.19 40)",
          "down-soft": "oklch(0.46 0.19 40 / 18%)",
          "expiring": "oklch(0.53 0.2 350)",
          "expiring-soft": "oklch(0.53 0.2 350 / 18%)",
          "flat": "var(--color-muted-foreground)",
          "flat-soft": "oklch(0.55 0 0 / 14%)",
          "stale": "oklch(0.52 0.13 75)",
          "stale-soft": "oklch(0.52 0.13 75 / 16%)",
          "tradecn-font-accessible": "'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-accessible-mono": "'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-mono": "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-numeric": "var(--tradecn-font-sans)",
          "tradecn-font-sans": "'Inter', ui-sans-serif, system-ui, sans-serif",
          "up": "oklch(0.52 0.13 165)",
          "up-soft": "oklch(0.52 0.13 165 / 18%)"
        },
        "dark": {
          "down": "oklch(0.66 0.19 40)",
          "down-soft": "oklch(0.66 0.19 40 / 18%)",
          "expiring": "oklch(0.74 0.17 350)",
          "expiring-soft": "oklch(0.74 0.17 350 / 18%)",
          "flat": "var(--color-muted-foreground)",
          "flat-soft": "oklch(0.7 0 0 / 14%)",
          "stale": "oklch(0.8 0.15 80)",
          "stale-soft": "oklch(0.8 0.15 80 / 16%)",
          "tradecn-font-accessible": "'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-accessible-mono": "'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-mono": "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-numeric": "var(--tradecn-font-sans)",
          "tradecn-font-sans": "'Inter', ui-sans-serif, system-ui, sans-serif",
          "up": "oklch(0.74 0.15 165)",
          "up-soft": "oklch(0.74 0.15 165 / 18%)"
        }
      },
      "css": {
        ":root[data-accessibility=\"hyperlegible\"], [data-accessibility=\"hyperlegible\"]": {
          "--tradecn-font-sans": "var(--tradecn-font-accessible)",
          "--tradecn-font-mono": "var(--tradecn-font-accessible-mono)"
        }
      }
    },
    {
      "name": "parameter-grid",
      "type": "registry:ui",
      "title": "Parameter Grid",
      "description": "A parameter table on the data grid: one row per instrument, tier, or pair, a frozen name, an enable box that asks the server and never flips itself, the values typed in place and held pending until the server's row comes back with them, a rejection printed in the cell, when the server last changed each row, and a mark on the rows changed since a moment. Every value is the consumer's; nothing here decides one.",
      "dependencies": [
        "cn@^0.3.0",
        "@tanstack/react-virtual@^3.14.13"
      ],
      "registryDependencies": [
        "checkbox",
        "context-menu",
        "dropdown-menu"
      ],
      "files": [
        {
          "path": "registry/tradecn/ui/parameter-grid.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/tradecn/ui/data-grid.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/tradecn/hooks/use-flash.ts",
          "type": "registry:hook"
        },
        {
          "path": "registry/tradecn/hooks/use-row-store.ts",
          "type": "registry:hook"
        },
        {
          "path": "registry/tradecn/lib/row-store.ts",
          "type": "registry:lib"
        },
        {
          "path": "registry/tradecn/lib/format.ts",
          "type": "registry:lib"
        },
        {
          "path": "registry/tradecn/lib/grid-rules.ts",
          "type": "registry:lib"
        }
      ],
      "categories": [
        "grid",
        "ticket"
      ],
      "docs": "Installs the data grid, the row store, and format alongside, adds @tanstack/react-virtual and the up, down, flat, stale, and expiring tokens the grid carries, and composes your checkbox plus the context-menu and dropdown-menu the grid uses. An edit is a command: onEdit sends it, and the cell waits for the server's row.",
      "meta": {
        "components": [
          {
            "title": "ParameterGrid",
            "description": "The grid in its parameters preset, with the as-of line."
          },
          {
            "title": "parameterColumns",
            "description": "Name, the enable box, a column per parameter, and the updated time, as a list to spread."
          },
          {
            "title": "parameterEdit",
            "description": "The grid's edit for one parameter: parse, format, the range check, the step."
          },
          {
            "title": "allowsAction",
            "description": "True when the server's list for the row names the action."
          },
          {
            "title": "DEFAULT_PARAMETER_GRID_LABELS",
            "description": "Every word on it, to replace in part."
          }
        ]
      },
      "cssVars": {
        "light": {
          "down": "oklch(0.46 0.19 40)",
          "down-soft": "oklch(0.46 0.19 40 / 18%)",
          "expiring": "oklch(0.53 0.2 350)",
          "expiring-soft": "oklch(0.53 0.2 350 / 18%)",
          "flat": "var(--color-muted-foreground)",
          "flat-soft": "oklch(0.55 0 0 / 14%)",
          "stale": "oklch(0.52 0.13 75)",
          "stale-soft": "oklch(0.52 0.13 75 / 16%)",
          "tradecn-font-accessible": "'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-accessible-mono": "'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-mono": "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-numeric": "var(--tradecn-font-sans)",
          "tradecn-font-sans": "'Inter', ui-sans-serif, system-ui, sans-serif",
          "up": "oklch(0.52 0.13 165)",
          "up-soft": "oklch(0.52 0.13 165 / 18%)"
        },
        "dark": {
          "down": "oklch(0.66 0.19 40)",
          "down-soft": "oklch(0.66 0.19 40 / 18%)",
          "expiring": "oklch(0.74 0.17 350)",
          "expiring-soft": "oklch(0.74 0.17 350 / 18%)",
          "flat": "var(--color-muted-foreground)",
          "flat-soft": "oklch(0.7 0 0 / 14%)",
          "stale": "oklch(0.8 0.15 80)",
          "stale-soft": "oklch(0.8 0.15 80 / 16%)",
          "tradecn-font-accessible": "'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-accessible-mono": "'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-mono": "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-numeric": "var(--tradecn-font-sans)",
          "tradecn-font-sans": "'Inter', ui-sans-serif, system-ui, sans-serif",
          "up": "oklch(0.74 0.15 165)",
          "up-soft": "oklch(0.74 0.15 165 / 18%)"
        }
      },
      "css": {
        ":root[data-accessibility=\"hyperlegible\"], [data-accessibility=\"hyperlegible\"]": {
          "--tradecn-font-sans": "var(--tradecn-font-accessible)",
          "--tradecn-font-mono": "var(--tradecn-font-accessible-mono)"
        }
      }
    },
    {
      "name": "positions",
      "type": "registry:ui",
      "title": "Positions",
      "description": "The data grid as a book of positions: instrument, the position signed in its unit (millions of notional or a count of contracts) with the side named, average, mark, the day's and the total P&L colored by their sign with the sign printed, a risk column the desk names, and totals of what is shown under the body. Every number is the server's; the grid prints and adds and never multiplies a position by a mark. One grid per book.",
      "dependencies": [
        "cn@^0.3.0",
        "@tanstack/react-virtual@^3.14.13"
      ],
      "registryDependencies": [
        "checkbox",
        "context-menu",
        "dropdown-menu"
      ],
      "files": [
        {
          "path": "registry/tradecn/ui/positions.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/tradecn/ui/data-grid.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/tradecn/hooks/use-flash.ts",
          "type": "registry:hook"
        },
        {
          "path": "registry/tradecn/hooks/use-row-store.ts",
          "type": "registry:hook"
        },
        {
          "path": "registry/tradecn/lib/row-store.ts",
          "type": "registry:lib"
        },
        {
          "path": "registry/tradecn/lib/format.ts",
          "type": "registry:lib"
        },
        {
          "path": "registry/tradecn/lib/grid-rules.ts",
          "type": "registry:lib"
        }
      ],
      "categories": [
        "grid",
        "blotter"
      ],
      "docs": "Installs the data grid, the row store, and format alongside, adds @tanstack/react-virtual and the up, down, flat, stale, and expiring tokens the grid carries, and composes the checkbox, context-menu, and dropdown-menu the grid uses. The P&L and the risk are the server's numbers; pass price, pnl, and risk formatters for the desk's notation.",
      "meta": {
        "components": [
          {
            "title": "Positions",
            "description": "The grid with a book's columns and totals under it."
          },
          {
            "title": "positionsColumns",
            "description": "Book, instrument, position, average, mark, day P&L, total P&L, risk, as a list to spread."
          },
          {
            "title": "positionsTotals",
            "description": "The footer: the count, and the sums of the P&Ls and the risk."
          },
          {
            "title": "formatPosition",
            "description": "The position signed in its unit: +5mm, \u2212120."
          },
          {
            "title": "positionSide",
            "description": "long, short, or flat."
          }
        ]
      },
      "cssVars": {
        "light": {
          "down": "oklch(0.46 0.19 40)",
          "down-soft": "oklch(0.46 0.19 40 / 18%)",
          "expiring": "oklch(0.53 0.2 350)",
          "expiring-soft": "oklch(0.53 0.2 350 / 18%)",
          "flat": "var(--color-muted-foreground)",
          "flat-soft": "oklch(0.55 0 0 / 14%)",
          "stale": "oklch(0.52 0.13 75)",
          "stale-soft": "oklch(0.52 0.13 75 / 16%)",
          "tradecn-font-accessible": "'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-accessible-mono": "'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-mono": "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-numeric": "var(--tradecn-font-sans)",
          "tradecn-font-sans": "'Inter', ui-sans-serif, system-ui, sans-serif",
          "up": "oklch(0.52 0.13 165)",
          "up-soft": "oklch(0.52 0.13 165 / 18%)"
        },
        "dark": {
          "down": "oklch(0.66 0.19 40)",
          "down-soft": "oklch(0.66 0.19 40 / 18%)",
          "expiring": "oklch(0.74 0.17 350)",
          "expiring-soft": "oklch(0.74 0.17 350 / 18%)",
          "flat": "var(--color-muted-foreground)",
          "flat-soft": "oklch(0.7 0 0 / 14%)",
          "stale": "oklch(0.8 0.15 80)",
          "stale-soft": "oklch(0.8 0.15 80 / 16%)",
          "tradecn-font-accessible": "'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-accessible-mono": "'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-mono": "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-numeric": "var(--tradecn-font-sans)",
          "tradecn-font-sans": "'Inter', ui-sans-serif, system-ui, sans-serif",
          "up": "oklch(0.74 0.15 165)",
          "up-soft": "oklch(0.74 0.15 165 / 18%)"
        }
      },
      "css": {
        ":root[data-accessibility=\"hyperlegible\"], [data-accessibility=\"hyperlegible\"]": {
          "--tradecn-font-sans": "var(--tradecn-font-accessible)",
          "--tradecn-font-mono": "var(--tradecn-font-accessible-mono)"
        }
      }
    },
    {
      "name": "audit-trail",
      "type": "registry:ui",
      "title": "Audit Trail",
      "description": "The life of one order or inquiry as events on a tape: when, the server's word for what happened, who, a line of the server's, and how many fields changed, following its tail as events land. Beside it a pane that lists one selected event's changes as field, before, and after, or the difference between two selected events, every field whose value at the later one differs from its value at the earlier one. Export the events shown as CSV. The words and the values are the server's; nothing here decides what an event means.",
      "dependencies": [
        "cn@^0.3.0",
        "@tanstack/react-virtual@^3.14.13"
      ],
      "registryDependencies": [
        "button",
        "checkbox",
        "context-menu",
        "dropdown-menu"
      ],
      "files": [
        {
          "path": "registry/tradecn/ui/audit-trail.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/tradecn/ui/data-grid.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/tradecn/hooks/use-flash.ts",
          "type": "registry:hook"
        },
        {
          "path": "registry/tradecn/hooks/use-row-store.ts",
          "type": "registry:hook"
        },
        {
          "path": "registry/tradecn/lib/row-store.ts",
          "type": "registry:lib"
        },
        {
          "path": "registry/tradecn/lib/format.ts",
          "type": "registry:lib"
        },
        {
          "path": "registry/tradecn/lib/grid-rules.ts",
          "type": "registry:lib"
        }
      ],
      "categories": [
        "grid",
        "blotter"
      ],
      "docs": "Installs the data grid, the row store, and format alongside, adds @tanstack/react-virtual and the up, down, flat, stale, and expiring tokens the grid carries, and composes your button plus the checkbox, context-menu, and dropdown-menu the grid uses. Pass value(field, value, event) for the desk's notation and onExport for the CSV.",
      "meta": {
        "components": [
          {
            "title": "AuditTrail",
            "description": "The tape of events with the changes pane beside it and the export button."
          },
          {
            "title": "auditTrailColumns",
            "description": "Time, event, by, message, changes, as a list to spread."
          },
          {
            "title": "foldChanges",
            "description": "Every field's value after the events up to one: the state at that moment."
          },
          {
            "title": "diffEvents",
            "description": "The fields whose value differs between two events, with both values."
          },
          {
            "title": "formatAuditValue",
            "description": "A value as the pane prints it: text, or the null token for nothing."
          },
          {
            "title": "DEFAULT_AUDIT_TRAIL_LABELS",
            "description": "Every word on it, to replace in part."
          }
        ]
      },
      "cssVars": {
        "light": {
          "down": "oklch(0.46 0.19 40)",
          "down-soft": "oklch(0.46 0.19 40 / 18%)",
          "expiring": "oklch(0.53 0.2 350)",
          "expiring-soft": "oklch(0.53 0.2 350 / 18%)",
          "flat": "var(--color-muted-foreground)",
          "flat-soft": "oklch(0.55 0 0 / 14%)",
          "stale": "oklch(0.52 0.13 75)",
          "stale-soft": "oklch(0.52 0.13 75 / 16%)",
          "tradecn-font-accessible": "'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-accessible-mono": "'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-mono": "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-numeric": "var(--tradecn-font-sans)",
          "tradecn-font-sans": "'Inter', ui-sans-serif, system-ui, sans-serif",
          "up": "oklch(0.52 0.13 165)",
          "up-soft": "oklch(0.52 0.13 165 / 18%)"
        },
        "dark": {
          "down": "oklch(0.66 0.19 40)",
          "down-soft": "oklch(0.66 0.19 40 / 18%)",
          "expiring": "oklch(0.74 0.17 350)",
          "expiring-soft": "oklch(0.74 0.17 350 / 18%)",
          "flat": "var(--color-muted-foreground)",
          "flat-soft": "oklch(0.7 0 0 / 14%)",
          "stale": "oklch(0.8 0.15 80)",
          "stale-soft": "oklch(0.8 0.15 80 / 16%)",
          "tradecn-font-accessible": "'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-accessible-mono": "'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-mono": "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-numeric": "var(--tradecn-font-sans)",
          "tradecn-font-sans": "'Inter', ui-sans-serif, system-ui, sans-serif",
          "up": "oklch(0.74 0.15 165)",
          "up-soft": "oklch(0.74 0.15 165 / 18%)"
        }
      },
      "css": {
        ":root[data-accessibility=\"hyperlegible\"], [data-accessibility=\"hyperlegible\"]": {
          "--tradecn-font-sans": "var(--tradecn-font-accessible)",
          "--tradecn-font-mono": "var(--tradecn-font-accessible-mono)"
        }
      }
    },
    {
      "name": "layout-manager",
      "type": "registry:ui",
      "title": "Layout Manager",
      "description": "Named layouts for a workspace: save the current one under a name, load one (with a warning first when it asks for a panel kind the workspace lacks), rename, duplicate, delete with an ask-again, import and export the JSON, and go back to the default. The list is the consumer's, read from and written to a preferences slot by two functions; every change is a new list, and every load is a request the consumer answers with the workspace's api.",
      "dependencies": [
        "cn@^0.3.0"
      ],
      "registryDependencies": [
        "badge",
        "button",
        "input"
      ],
      "files": [
        {
          "path": "registry/tradecn/ui/layout-manager.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/tradecn/lib/workspace-layout.ts",
          "type": "registry:lib"
        },
        {
          "path": "registry/tradecn/lib/preferences.ts",
          "type": "registry:lib"
        }
      ],
      "categories": [
        "layout"
      ],
      "docs": "Installs the workspace layout lib and preferences alongside and composes your badge, button, and input. It imports nothing from the workspace itself, so no dockview comes with it. Adds the stale token for the unknown-kinds badge.",
      "meta": {
        "components": [
          {
            "title": "LayoutManager",
            "description": "The list with save, load, rename, duplicate, delete, import, export, and reset."
          },
          {
            "title": "readLayoutTemplates",
            "description": "The templates out of a preferences slot."
          },
          {
            "title": "writeLayoutTemplates",
            "description": "The envelope with the templates written to the slot."
          },
          {
            "title": "parseLayoutTemplates",
            "description": "A stored list read back, taking nothing on trust."
          },
          {
            "title": "saveTemplate",
            "description": "The list with a layout saved under a name, replacing the one already named."
          },
          {
            "title": "importTemplate",
            "description": "The list with a pasted layout added, or null when the text is not one."
          },
          {
            "title": "exportTemplate",
            "description": "A template's layout as JSON text."
          },
          {
            "title": "DEFAULT_LAYOUT_MANAGER_LABELS",
            "description": "Every word on it, to replace in part."
          }
        ]
      },
      "cssVars": {
        "light": {
          "stale": "oklch(0.52 0.13 75)",
          "tradecn-font-accessible": "'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-accessible-mono": "'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-mono": "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace"
        },
        "dark": {
          "stale": "oklch(0.8 0.15 80)",
          "tradecn-font-accessible": "'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-accessible-mono": "'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-mono": "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace"
        }
      },
      "css": {
        ":root[data-accessibility=\"hyperlegible\"], [data-accessibility=\"hyperlegible\"]": {
          "--tradecn-font-sans": "var(--tradecn-font-accessible)",
          "--tradecn-font-mono": "var(--tradecn-font-accessible-mono)"
        }
      }
    },
    {
      "name": "instrument-search",
      "type": "registry:ui",
      "title": "Instrument Search",
      "description": "A typed field over your instrument search that reads what was typed first, a CUSIP with its check digit, an ISIN with its Luhn digit, a ticker, or a coupon and maturity the way a run prints it, hands the server the query with that hint, and lists what came back on your own command. Keyboard first; only answers to the query on screen; the identifier is checked here and resolved by the server. With it, the recognizers as a lib and an adapter for the palette's symbol rows.",
      "dependencies": [
        "cn@^0.3.0"
      ],
      "registryDependencies": [
        "badge",
        "command"
      ],
      "files": [
        {
          "path": "registry/tradecn/ui/instrument-search.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/tradecn/lib/instrument-query.ts",
          "type": "registry:lib"
        }
      ],
      "categories": [
        "palette",
        "watchlist"
      ],
      "docs": "Installs the recognizers (lib/instrument-query.ts) alongside and composes your command and badge. The search is yours: search(query, hint, signal). toSymbolAdapter(search) hands the same function to the command palette.",
      "meta": {
        "components": [
          {
            "title": "InstrumentSearch",
            "description": "The field, the hint under it, and the list of the server's answers."
          },
          {
            "title": "useInstrumentSearch",
            "description": "The debounced, abortable search over the recognized query."
          },
          {
            "title": "toSymbolAdapter",
            "description": "The command palette's symbols adapter over the same search."
          },
          {
            "title": "matchedIdentifier",
            "description": "The identifier on a hit that the query named."
          },
          {
            "title": "DEFAULT_INSTRUMENT_SEARCH_LABELS",
            "description": "Every word on it, to replace in part."
          }
        ]
      },
      "cssVars": {
        "light": {
          "tradecn-font-accessible": "'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-accessible-mono": "'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-mono": "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace"
        },
        "dark": {
          "tradecn-font-accessible": "'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-accessible-mono": "'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-mono": "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace"
        }
      },
      "css": {
        ":root[data-accessibility=\"hyperlegible\"], [data-accessibility=\"hyperlegible\"]": {
          "--tradecn-font-sans": "var(--tradecn-font-accessible)",
          "--tradecn-font-mono": "var(--tradecn-font-accessible-mono)"
        }
      }
    },
    {
      "name": "depth-ladder",
      "type": "registry:ui",
      "title": "Depth Ladder",
      "description": "A price ladder around the market: one rung per tick over a range of prices, the bid size to the left of the price and the ask size to its right, the desk's own resting size marked on its rung, and one flash per level change. Levels come from a row store keyed by tick index, so a change at one price wakes one rung. The mid stays in the middle until a key, a pointer, or a scroll touches the ladder; then the prices hold still and Recenter or Home puts the mid back. A click in the bid column stages a buy at that price and a click in the ask column a sell, handed to the consumer through onStage; the ladder sends nothing.",
      "dependencies": [
        "cn@^0.3.0",
        "@tanstack/react-virtual@^3.14.13"
      ],
      "registryDependencies": [],
      "files": [
        {
          "path": "registry/tradecn/ui/depth-ladder.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/tradecn/hooks/use-flash.ts",
          "type": "registry:hook"
        },
        {
          "path": "registry/tradecn/hooks/use-row-store.ts",
          "type": "registry:hook"
        },
        {
          "path": "registry/tradecn/lib/row-store.ts",
          "type": "registry:lib"
        },
        {
          "path": "registry/tradecn/lib/format.ts",
          "type": "registry:lib"
        }
      ],
      "categories": [
        "grid",
        "ticket"
      ],
      "docs": "Installs the row store, format, and the flash hook alongside, adds @tanstack/react-virtual and the up, down, and flat tokens the two size columns and the flashes use, and composes no shadcn component. Key the store by tick index with levelId; the ladder prints prices through the convention you pass and sends nothing itself: onStage hands you the price and the side.",
      "meta": {
        "components": [
          {
            "title": "DepthLadder",
            "description": "The ladder: bid sizes, prices, ask sizes, the mid in the middle, one flash per level change."
          },
          {
            "title": "levelId",
            "description": "The row id of a level: its tick index as a string."
          },
          {
            "title": "tickIndexOf",
            "description": "A price as a count of ticks from zero, rounded to the grid."
          },
          {
            "title": "priceAtTick",
            "description": "The price at a tick index, cleaned of float noise."
          },
          {
            "title": "DEFAULT_DEPTH_LADDER_LABELS",
            "description": "Every word the ladder says."
          }
        ]
      },
      "cssVars": {
        "light": {
          "down": "oklch(0.46 0.19 40)",
          "down-soft": "oklch(0.46 0.19 40 / 18%)",
          "flat": "var(--color-muted-foreground)",
          "flat-soft": "oklch(0.55 0 0 / 14%)",
          "tradecn-font-accessible": "'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-accessible-mono": "'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-mono": "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-numeric": "var(--tradecn-font-sans)",
          "tradecn-font-sans": "'Inter', ui-sans-serif, system-ui, sans-serif",
          "up": "oklch(0.52 0.13 165)",
          "up-soft": "oklch(0.52 0.13 165 / 18%)"
        },
        "dark": {
          "down": "oklch(0.66 0.19 40)",
          "down-soft": "oklch(0.66 0.19 40 / 18%)",
          "flat": "var(--color-muted-foreground)",
          "flat-soft": "oklch(0.7 0 0 / 14%)",
          "tradecn-font-accessible": "'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-accessible-mono": "'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-mono": "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-numeric": "var(--tradecn-font-sans)",
          "tradecn-font-sans": "'Inter', ui-sans-serif, system-ui, sans-serif",
          "up": "oklch(0.74 0.15 165)",
          "up-soft": "oklch(0.74 0.15 165 / 18%)"
        }
      },
      "css": {
        ":root[data-accessibility=\"hyperlegible\"], [data-accessibility=\"hyperlegible\"]": {
          "--tradecn-font-sans": "var(--tradecn-font-accessible)",
          "--tradecn-font-mono": "var(--tradecn-font-accessible-mono)"
        }
      }
    },
    {
      "name": "spread-matrix",
      "type": "registry:ui",
      "title": "Spread Matrix",
      "description": "Instruments down the side and across the top, each cell the spread of the row over the column, in ticks of the row's price or in basis points of yield, printed with its sign and flashing by the direction it moved. Every row reads its own quote and every cell its column's, so a quote that moves wakes the cells it is part of and nothing else. Given a list of structures, curves and butterflies as two or three weighted legs, the table lists those as rows instead of the full matrix. It reads quotes and prints spreads; it stages and sends nothing.",
      "dependencies": [
        "cn@^0.3.0"
      ],
      "registryDependencies": [],
      "files": [
        {
          "path": "registry/tradecn/ui/spread-matrix.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/tradecn/hooks/use-flash.ts",
          "type": "registry:hook"
        },
        {
          "path": "registry/tradecn/hooks/use-row-store.ts",
          "type": "registry:hook"
        },
        {
          "path": "registry/tradecn/lib/row-store.ts",
          "type": "registry:lib"
        },
        {
          "path": "registry/tradecn/lib/format.ts",
          "type": "registry:lib"
        }
      ],
      "categories": [
        "grid",
        "watchlist"
      ],
      "docs": "Installs the row store, format, and the flash hook alongside, adds the up, down, and flat tokens the flashes use, and composes no shadcn component. Key the store by instrument id; each cell is the row less the column in the row's ticks or in basis points, with its sign, and the matrix sends nothing.",
      "meta": {
        "components": [
          {
            "title": "SpreadMatrix",
            "description": "The table: instruments as rows and columns, or structures as rows, each cell a signed spread that flashes as it moves."
          },
          {
            "title": "spreadBetween",
            "description": "One value less another in ticks of a tick or in basis points; null when a side is missing."
          },
          {
            "title": "structureSpread",
            "description": "A structure's spread: its legs' values weighted and summed, in the basis."
          },
          {
            "title": "formatSpread",
            "description": "A spread with its sign and no unit."
          },
          {
            "title": "defaultWeights",
            "description": "The weights a curve or a butterfly takes when none are given."
          },
          {
            "title": "DEFAULT_SPREAD_MATRIX_LABELS",
            "description": "Every word the table says."
          }
        ]
      },
      "cssVars": {
        "light": {
          "down": "oklch(0.46 0.19 40)",
          "down-soft": "oklch(0.46 0.19 40 / 18%)",
          "flat": "var(--color-muted-foreground)",
          "flat-soft": "oklch(0.55 0 0 / 14%)",
          "tradecn-font-accessible": "'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-accessible-mono": "'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-mono": "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-numeric": "var(--tradecn-font-sans)",
          "tradecn-font-sans": "'Inter', ui-sans-serif, system-ui, sans-serif",
          "up": "oklch(0.52 0.13 165)",
          "up-soft": "oklch(0.52 0.13 165 / 18%)"
        },
        "dark": {
          "down": "oklch(0.66 0.19 40)",
          "down-soft": "oklch(0.66 0.19 40 / 18%)",
          "flat": "var(--color-muted-foreground)",
          "flat-soft": "oklch(0.7 0 0 / 14%)",
          "tradecn-font-accessible": "'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-accessible-mono": "'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-mono": "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-numeric": "var(--tradecn-font-sans)",
          "tradecn-font-sans": "'Inter', ui-sans-serif, system-ui, sans-serif",
          "up": "oklch(0.74 0.15 165)",
          "up-soft": "oklch(0.74 0.15 165 / 18%)"
        }
      },
      "css": {
        ":root[data-accessibility=\"hyperlegible\"], [data-accessibility=\"hyperlegible\"]": {
          "--tradecn-font-sans": "var(--tradecn-font-accessible)",
          "--tradecn-font-mono": "var(--tradecn-font-accessible-mono)"
        }
      }
    },
    {
      "name": "quote-panel",
      "type": "registry:block",
      "title": "Quote Panel",
      "description": "A market maker's two-way panel: one row per instrument with the market's bid and ask, the desk's bid and ask, the skew and the width, a size per side, the server's status word, and the actions the server allows on the row. Every level, size, skew, and width is typed in place through the grid's editing contract, so an edit is a command the server answers and the cell shows it as pending until the row comes back with it; the limits table runs on every edit, a block refusing the value and a confirm asking once. Pull all asks again. Everything the panel shows is the server's.",
      "dependencies": [
        "cn@^0.3.0",
        "@tanstack/react-virtual@^3.14.13"
      ],
      "registryDependencies": [
        "button",
        "checkbox",
        "context-menu",
        "dropdown-menu"
      ],
      "files": [
        {
          "path": "registry/tradecn/blocks/quote-panel/quote-panel.tsx",
          "type": "registry:block"
        },
        {
          "path": "registry/tradecn/ui/data-grid.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/tradecn/hooks/use-flash.ts",
          "type": "registry:hook"
        },
        {
          "path": "registry/tradecn/hooks/use-row-store.ts",
          "type": "registry:hook"
        },
        {
          "path": "registry/tradecn/lib/row-store.ts",
          "type": "registry:lib"
        },
        {
          "path": "registry/tradecn/lib/format.ts",
          "type": "registry:lib"
        },
        {
          "path": "registry/tradecn/lib/grid-rules.ts",
          "type": "registry:lib"
        },
        {
          "path": "registry/tradecn/lib/limits.ts",
          "type": "registry:lib"
        }
      ],
      "categories": [
        "grid",
        "ticket"
      ],
      "docs": "Installs the data grid, the row store, format, grid rules, the limits table, and the flash hook alongside, adds @tanstack/react-virtual and the grid's tokens, and composes the consumer's button plus the grid's checkbox, context-menu, and dropdown-menu. The rows are the server's: an edit is a request sent through onEdit, a button is an action the row's allowedActions names, and the status word moves only when the server's next batch says so.",
      "meta": {
        "components": [
          {
            "title": "QuotePanel",
            "description": "The two-way panel: the grid in its parameters preset with the quote columns and Pull all over it."
          },
          {
            "title": "quotePanelColumns",
            "description": "Instrument, status, the market's two-way, the desk's, skew, width, the sizes, and the actions, to spread into a list of your own."
          },
          {
            "title": "quoteEdit",
            "description": "One field's editing contract: its notation, its step, the crossed check, and the limits."
          },
          {
            "title": "allowsQuoteAction",
            "description": "True when the row's allowedActions names the id."
          },
          {
            "title": "DEFAULT_QUOTE_PANEL_LABELS",
            "description": "Every word the panel says."
          }
        ]
      },
      "cssVars": {
        "light": {
          "down": "oklch(0.46 0.19 40)",
          "down-soft": "oklch(0.46 0.19 40 / 18%)",
          "expiring": "oklch(0.53 0.2 350)",
          "expiring-soft": "oklch(0.53 0.2 350 / 18%)",
          "flat": "var(--color-muted-foreground)",
          "flat-soft": "oklch(0.55 0 0 / 14%)",
          "stale": "oklch(0.52 0.13 75)",
          "stale-soft": "oklch(0.52 0.13 75 / 16%)",
          "tradecn-font-accessible": "'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-accessible-mono": "'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-mono": "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-numeric": "var(--tradecn-font-sans)",
          "tradecn-font-sans": "'Inter', ui-sans-serif, system-ui, sans-serif",
          "up": "oklch(0.52 0.13 165)",
          "up-soft": "oklch(0.52 0.13 165 / 18%)"
        },
        "dark": {
          "down": "oklch(0.66 0.19 40)",
          "down-soft": "oklch(0.66 0.19 40 / 18%)",
          "expiring": "oklch(0.74 0.17 350)",
          "expiring-soft": "oklch(0.74 0.17 350 / 18%)",
          "flat": "var(--color-muted-foreground)",
          "flat-soft": "oklch(0.7 0 0 / 14%)",
          "stale": "oklch(0.8 0.15 80)",
          "stale-soft": "oklch(0.8 0.15 80 / 16%)",
          "tradecn-font-accessible": "'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-accessible-mono": "'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-mono": "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-numeric": "var(--tradecn-font-sans)",
          "tradecn-font-sans": "'Inter', ui-sans-serif, system-ui, sans-serif",
          "up": "oklch(0.74 0.15 165)",
          "up-soft": "oklch(0.74 0.15 165 / 18%)"
        }
      },
      "css": {
        ":root[data-accessibility=\"hyperlegible\"], [data-accessibility=\"hyperlegible\"]": {
          "--tradecn-font-sans": "var(--tradecn-font-accessible)",
          "--tradecn-font-mono": "var(--tradecn-font-accessible-mono)"
        }
      }
    },
    {
      "name": "session-guard",
      "type": "registry:ui",
      "title": "Session Guard",
      "description": "A session ends and the trader must not lose a half-typed ticket. The guard reads when the session expires and does three things with it: in the warning window a banner with a countdown and a button that asks for more time; at expiry a dialog that is a hotkey wall, blocking every action underneath and unmounting nothing, so drafts and layouts stay; and a readout of the phase for the status bar. It holds no token and knows no protocol: the consumer's sign-in is the dialog's children, and onReauthenticate is how it is asked.",
      "dependencies": [
        "cn@^0.3.0"
      ],
      "registryDependencies": [
        "button",
        "dialog"
      ],
      "files": [
        {
          "path": "registry/tradecn/ui/session-guard.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/tradecn/ui/countdown.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/tradecn/hooks/use-clock.ts",
          "type": "registry:hook"
        },
        {
          "path": "registry/tradecn/lib/clock.ts",
          "type": "registry:lib"
        },
        {
          "path": "registry/tradecn/lib/format.ts",
          "type": "registry:lib"
        }
      ],
      "categories": [
        "chrome"
      ],
      "docs": "Installs the countdown, the shared clock, and format alongside, adds the expiring tokens the banner and the readout use, and composes the consumer's button and dialog. Pass when the session ends and your sign-in as the children; the guard closes when expiresAt moves, never on its own.",
      "meta": {
        "components": [
          {
            "title": "SessionGuard",
            "description": "The banner in the warning window and the wall at expiry, over one number: when the session ends."
          },
          {
            "title": "SessionStatus",
            "description": "The phase and the time left for a status bar."
          },
          {
            "title": "useSessionStatus",
            "description": "The phase, the time left, and the end, ticking on the shared clock."
          },
          {
            "title": "sessionStatus",
            "description": "The phase rule as a pure function of the end, the moment, and the warning window."
          },
          {
            "title": "DEFAULT_SESSION_GUARD_LABELS",
            "description": "Every word the guard says."
          },
          {
            "title": "DEFAULT_WARN_MS",
            "description": "Two minutes: how long before the end the banner shows."
          }
        ]
      },
      "cssVars": {
        "light": {
          "expiring": "oklch(0.53 0.2 350)",
          "expiring-soft": "oklch(0.53 0.2 350 / 18%)",
          "tradecn-font-accessible": "'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-accessible-mono": "'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-mono": "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-numeric": "var(--tradecn-font-sans)",
          "tradecn-font-sans": "'Inter', ui-sans-serif, system-ui, sans-serif"
        },
        "dark": {
          "expiring": "oklch(0.74 0.17 350)",
          "expiring-soft": "oklch(0.74 0.17 350 / 18%)",
          "tradecn-font-accessible": "'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-accessible-mono": "'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-mono": "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-numeric": "var(--tradecn-font-sans)",
          "tradecn-font-sans": "'Inter', ui-sans-serif, system-ui, sans-serif"
        }
      },
      "css": {
        ":root[data-accessibility=\"hyperlegible\"], [data-accessibility=\"hyperlegible\"]": {
          "--tradecn-font-sans": "var(--tradecn-font-accessible)",
          "--tradecn-font-mono": "var(--tradecn-font-accessible-mono)"
        }
      }
    },
    {
      "name": "price-chart",
      "type": "registry:ui",
      "title": "Price Chart",
      "description": "An intraday chart on uPlot: a line through the closes or a candle per bar, over a store of bars keyed by their start time, with a crosshair the pointer and the arrow keys both move, the time axis in the venue's zone, the last price printed with its sign and its change, and overlays named in a legend and drawn in the chart tokens. Every color is read from your own tokens when the chart mounts and again when the mode changes, so the canvas wears the theme the page wears. A frame of ticks folds into the open bar through the store, and the chart reads its columns once per applied batch.",
      "dependencies": [
        "cn@^0.3.0",
        "uplot@^1.6.32"
      ],
      "registryDependencies": [],
      "files": [
        {
          "path": "registry/tradecn/ui/price-chart.tsx",
          "type": "registry:ui"
        },
        {
          "path": "registry/tradecn/lib/price-series.ts",
          "type": "registry:lib"
        },
        {
          "path": "registry/tradecn/hooks/use-row-store.ts",
          "type": "registry:hook"
        },
        {
          "path": "registry/tradecn/hooks/use-flash.ts",
          "type": "registry:hook"
        },
        {
          "path": "registry/tradecn/lib/row-store.ts",
          "type": "registry:lib"
        },
        {
          "path": "registry/tradecn/lib/format.ts",
          "type": "registry:lib"
        }
      ],
      "categories": [
        "watchlist"
      ],
      "docs": "Adds uplot, MIT, a canvas chart with no dependencies of its own and 52 KB minified: a tick chart wants a canvas that redraws in one call per frame and a candle series, and neither is in shadcn's chart wrapper, which draws through Recharts in SVG. Installs the row store, format, the flash hook, and price-series alongside, adds the direction tokens and the eight chart tokens (an install leaves the five your project already has alone), and composes no shadcn component. Key the store by barId(time), feed it with foldTicks or whole bars, and pass the instrument's convention; the chart reads your tokens off the page and sends nothing.",
      "meta": {
        "components": [
          {
            "title": "PriceChart",
            "description": "The chart: a line or candles over the store's bars, the readout, the crosshair, the legend."
          },
          {
            "title": "CHART_TOKEN_CLASS",
            "description": "The eight chart tokens as utilities, in Okabe and Ito's order."
          },
          {
            "title": "DEFAULT_PRICE_CHART_LABELS",
            "description": "Every word the chart says."
          }
        ]
      },
      "cssVars": {
        "light": {
          "chart-1": "oklch(0.56 0.15 65)",
          "chart-2": "oklch(0.52 0.12 230)",
          "chart-3": "oklch(0.52 0.13 165)",
          "chart-4": "oklch(0.55 0.13 95)",
          "chart-5": "oklch(0.5 0.14 250)",
          "chart-6": "oklch(0.5 0.18 40)",
          "chart-7": "oklch(0.52 0.15 345)",
          "chart-8": "var(--color-foreground)",
          "down": "oklch(0.46 0.19 40)",
          "down-soft": "oklch(0.46 0.19 40 / 18%)",
          "flat": "var(--color-muted-foreground)",
          "flat-soft": "oklch(0.55 0 0 / 14%)",
          "tradecn-font-accessible": "'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-accessible-mono": "'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-mono": "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-numeric": "var(--tradecn-font-sans)",
          "tradecn-font-sans": "'Inter', ui-sans-serif, system-ui, sans-serif",
          "up": "oklch(0.52 0.13 165)",
          "up-soft": "oklch(0.52 0.13 165 / 18%)"
        },
        "dark": {
          "chart-1": "oklch(0.8 0.15 70)",
          "chart-2": "oklch(0.78 0.11 230)",
          "chart-3": "oklch(0.74 0.15 165)",
          "chart-4": "oklch(0.88 0.15 100)",
          "chart-5": "oklch(0.72 0.12 250)",
          "chart-6": "oklch(0.7 0.18 40)",
          "chart-7": "oklch(0.76 0.13 345)",
          "chart-8": "var(--color-foreground)",
          "down": "oklch(0.66 0.19 40)",
          "down-soft": "oklch(0.66 0.19 40 / 18%)",
          "flat": "var(--color-muted-foreground)",
          "flat-soft": "oklch(0.7 0 0 / 14%)",
          "tradecn-font-accessible": "'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-accessible-mono": "'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-mono": "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-numeric": "var(--tradecn-font-sans)",
          "tradecn-font-sans": "'Inter', ui-sans-serif, system-ui, sans-serif",
          "up": "oklch(0.74 0.15 165)",
          "up-soft": "oklch(0.74 0.15 165 / 18%)"
        }
      },
      "css": {
        ":root[data-accessibility=\"hyperlegible\"], [data-accessibility=\"hyperlegible\"]": {
          "--tradecn-font-sans": "var(--tradecn-font-accessible)",
          "--tradecn-font-mono": "var(--tradecn-font-accessible-mono)"
        }
      }
    },
    {
      "name": "grid-rules",
      "type": "registry:lib",
      "title": "Grid Rules",
      "description": "Rules as data for a grid: color a cell or a row when a value crosses a line, show only the rows that pass, order by a stack of columns, all as plain objects a desk writes without a build. A value is typed in the column's own format, a tone is a token name, and an applied rule says itself in words beside the color.",
      "categories": [
        "grid"
      ],
      "registryDependencies": [],
      "files": [
        {
          "path": "registry/tradecn/lib/grid-rules.ts",
          "type": "registry:lib"
        }
      ],
      "docs": "Pure functions, no dependencies. Hand a GridRules object to the data grid's rules prop, or compile it yourself with compileFilter, compileComparator, and applyRules. Adds the up, down, flat, stale, and expiring tokens with their soft variants, which the tones draw from.",
      "cssVars": {
        "light": {
          "down": "oklch(0.46 0.19 40)",
          "down-soft": "oklch(0.46 0.19 40 / 18%)",
          "expiring": "oklch(0.53 0.2 350)",
          "expiring-soft": "oklch(0.53 0.2 350 / 18%)",
          "flat": "var(--color-muted-foreground)",
          "flat-soft": "oklch(0.55 0 0 / 14%)",
          "stale": "oklch(0.52 0.13 75)",
          "stale-soft": "oklch(0.52 0.13 75 / 16%)",
          "up": "oklch(0.52 0.13 165)",
          "up-soft": "oklch(0.52 0.13 165 / 18%)"
        },
        "dark": {
          "down": "oklch(0.66 0.19 40)",
          "down-soft": "oklch(0.66 0.19 40 / 18%)",
          "expiring": "oklch(0.74 0.17 350)",
          "expiring-soft": "oklch(0.74 0.17 350 / 18%)",
          "flat": "var(--color-muted-foreground)",
          "flat-soft": "oklch(0.7 0 0 / 14%)",
          "stale": "oklch(0.8 0.15 80)",
          "stale-soft": "oklch(0.8 0.15 80 / 16%)",
          "up": "oklch(0.74 0.15 165)",
          "up-soft": "oklch(0.74 0.15 165 / 18%)"
        }
      }
    },
    {
      "name": "preferences",
      "type": "registry:lib",
      "title": "Preferences",
      "description": "The envelope everything a trader or a desk changes without a build travels in: slots with a version and a JSON value for a workspace layout, a grid's column state, hotkey overrides, rules, a threshold, and a payload that says which slots may leave as a desk template, which are one person's, and which never leave a session. Parse, set, read, diff, export, import, and migrate; not a store and not a provider.",
      "categories": [
        "layout"
      ],
      "registryDependencies": [],
      "files": [
        {
          "path": "registry/tradecn/lib/preferences.ts",
          "type": "registry:lib"
        }
      ],
      "docs": "Pure functions, no dependencies. The items keep their controlled props; wire a slot to a prop with setSlot and readSlot, and keep the envelope wherever your app keeps things."
    },
    {
      "name": "alert-store",
      "type": "registry:lib",
      "title": "Alert Store",
      "description": "A store for notices over the row store's ordered lane: a notice with the same key replaces the last and grows its count instead of adding a row, a cap lets the oldest without an action go first, and the severity, tone, and allowed actions are the consumer's words and the server's ids, never decided here.",
      "categories": [
        "feed"
      ],
      "registryDependencies": [],
      "files": [
        {
          "path": "registry/tradecn/lib/alert-store.ts",
          "type": "registry:lib"
        },
        {
          "path": "registry/tradecn/lib/row-store.ts",
          "type": "registry:lib"
        }
      ],
      "docs": "Installs the row store alongside. createAlertStore().store is a RowStore<Alert> on the ordered lane, so a grid, a view, or useRowIds reads it like any feed."
    },
    {
      "name": "session-calendar",
      "type": "registry:lib",
      "title": "Session Calendar",
      "description": "A session calendar from sessions, holidays, and early closes in a venue's own time zone, the SessionCalendar feed-health reads plus the next transition, the time to the close, and whether a date is a trading day. Sessions may run across midnight. Time zones and daylight saving go through Intl.DateTimeFormat, no dependency, and the lib ships no venue's data.",
      "categories": [
        "feed"
      ],
      "registryDependencies": [],
      "files": [
        {
          "path": "registry/tradecn/lib/session-calendar.ts",
          "type": "registry:lib"
        }
      ],
      "docs": "Pure functions, no dependencies. Hand the calendar to feed-health's session prop, and its status(now) the clock's now. The sessions, holidays, and early closes are yours, from the venue's own published calendar."
    },
    {
      "name": "limits",
      "type": "registry:lib",
      "title": "Limits",
      "description": "Fat-finger checks as data: a size above a line, a level too far from the market in the instrument's own unit, a side the book may not take, each a block or an ask-again, returned in the shape the tickets print. The numbers are the desk's, the words are the consumer's, and nothing here decides a limit.",
      "categories": [
        "ticket"
      ],
      "registryDependencies": [],
      "files": [
        {
          "path": "registry/tradecn/lib/limits.ts",
          "type": "registry:lib"
        },
        {
          "path": "registry/tradecn/lib/format.ts",
          "type": "registry:lib"
        }
      ],
      "docs": "Installs format alongside. Hand a Limits object to ticket's or rfq-ticket's limits prop, or run checkLimits yourself before a send of your own.",
      "cssVars": {
        "light": {
          "tradecn-font-accessible": "'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-accessible-mono": "'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-mono": "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-numeric": "var(--tradecn-font-sans)",
          "tradecn-font-sans": "'Inter', ui-sans-serif, system-ui, sans-serif"
        },
        "dark": {
          "tradecn-font-accessible": "'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-accessible-mono": "'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-mono": "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-numeric": "var(--tradecn-font-sans)",
          "tradecn-font-sans": "'Inter', ui-sans-serif, system-ui, sans-serif"
        }
      },
      "css": {
        ":root[data-accessibility=\"hyperlegible\"], [data-accessibility=\"hyperlegible\"]": {
          "--tradecn-font-sans": "var(--tradecn-font-accessible)",
          "--tradecn-font-mono": "var(--tradecn-font-accessible-mono)"
        }
      }
    },
    {
      "name": "window-set",
      "type": "registry:lib",
      "title": "Window Set",
      "description": "For shells whose windows are separate JavaScript contexts: the set of windows a desk has, which layout each shows, and where each sits, as data with its boundaries, and a controller that drives the consumer's adapter over the shell's own calls: open a window, close one, hear one close, ask where one is. restore() on launch opens the main window first and the rest after it; a snapshot reads the bounds back. No shell package is imported.",
      "dependencies": [],
      "registryDependencies": [],
      "files": [
        {
          "path": "registry/tradecn/lib/window-set.ts",
          "type": "registry:lib"
        },
        {
          "path": "registry/tradecn/lib/preferences.ts",
          "type": "registry:lib"
        }
      ],
      "categories": [
        "layout"
      ],
      "docs": "Installs the preferences envelope alongside, so the set can travel in a slot. Write the adapter over your shell (a Tauri WebviewWindow, an Electron BrowserWindow through a preload bridge) and call restore() with the stored set on launch; docs/shells.md has the whole shape and a recipe for each.",
      "meta": {
        "components": [
          {
            "title": "createWindowSet",
            "description": "The controller over your adapter: restore, open, close, snapshot, and a subscription to the open set."
          },
          {
            "title": "parseWindowSet",
            "description": "A stored set, as an object or JSON text, taken on no trust."
          },
          {
            "title": "windowSetOf",
            "description": "A set from records: one main at most, no duplicate ids."
          },
          {
            "title": "mainWindow",
            "description": "The window that opens first and whose close ends the desk."
          },
          {
            "title": "readWindowSet",
            "description": "The set kept in a preferences slot."
          },
          {
            "title": "writeWindowSet",
            "description": "The envelope with the set written to its slot."
          },
          {
            "title": "defaultWindowUrl",
            "description": "The document's own path with the window and layout ids in the query."
          },
          {
            "title": "WINDOW_SET_BOUNDARIES",
            "description": "What a set carries as a template, per person, and never."
          }
        ]
      }
    },
    {
      "name": "tradecn-amber",
      "type": "registry:theme",
      "title": "Amber",
      "description": "An amber theme with a day side and a night side: warm paper in light, near-black in dark, and blue and vermilion for up and down, the pair with the widest separation under every kind of color blindness and in grayscale.",
      "registryDependencies": [],
      "files": [],
      "categories": [
        "theme"
      ],
      "docs": "This one overwrites. It replaces your background, foreground, primary, border, radius and the rest of shadcn's variables, with one palette for light and another for dark, and sets every tradecn token per mode. Review it with --diff first. Components are untouched. It sets the typography tokens at their defaults and appends the three base rules (the numeric variant on :root and the numeric hooks, and the hyperlegible remap). It leaves your font stack alone, and its dark background is near-black, not black.",
      "cssVars": {
        "light": {
          "accent": "oklch(0.93 0.02 75)",
          "accent-foreground": "oklch(0.3 0.04 65)",
          "background": "oklch(0.99 0.004 85)",
          "border": "oklch(0.88 0.008 85)",
          "card": "oklch(0.97 0.005 85)",
          "card-foreground": "oklch(0.2 0.01 85)",
          "chart-1": "oklch(0.54 0.12 65)",
          "chart-2": "oklch(0.52 0.13 245)",
          "chart-3": "oklch(0.52 0.13 165)",
          "chart-4": "oklch(0.5 0.15 330)",
          "chart-5": "oklch(0.55 0.12 90)",
          "chart-6": "oklch(0.52 0.12 225)",
          "chart-7": "oklch(0.5 0.18 40)",
          "chart-8": "var(--color-foreground)",
          "destructive": "oklch(0.53 0.22 27)",
          "down": "oklch(0.56 0.19 45)",
          "down-soft": "oklch(0.56 0.19 45 / 18%)",
          "expiring": "oklch(0.53 0.2 350)",
          "expiring-soft": "oklch(0.53 0.2 350 / 18%)",
          "flat": "var(--color-muted-foreground)",
          "flat-soft": "oklch(0.55 0 0 / 14%)",
          "foreground": "oklch(0.2 0.01 85)",
          "input": "oklch(0.88 0.008 85)",
          "link-1": "oklch(0.53 0.2 25)",
          "link-2": "oklch(0.5 0.15 250)",
          "link-3": "oklch(0.5 0.13 150)",
          "link-4": "oklch(0.52 0.12 85)",
          "muted": "oklch(0.95 0.005 85)",
          "muted-foreground": "oklch(0.47 0.015 85)",
          "panel-active": "var(--color-primary)",
          "panel-drag-target": "var(--color-ring)",
          "panel-error": "var(--color-destructive)",
          "panel-sync": "oklch(0.5 0.12 220)",
          "popover": "oklch(0.985 0.004 85)",
          "popover-foreground": "oklch(0.2 0.01 85)",
          "primary": "oklch(0.54 0.12 65)",
          "primary-foreground": "oklch(0.99 0.01 85)",
          "radius": "0.25rem",
          "ring": "oklch(0.54 0.12 65)",
          "secondary": "oklch(0.95 0.006 85)",
          "secondary-foreground": "oklch(0.25 0.015 85)",
          "sidebar": "oklch(0.965 0.006 85)",
          "sidebar-accent": "oklch(0.92 0.02 75)",
          "sidebar-accent-foreground": "oklch(0.3 0.04 65)",
          "sidebar-border": "oklch(0.88 0.008 85)",
          "sidebar-foreground": "oklch(0.2 0.01 85)",
          "sidebar-primary": "oklch(0.54 0.12 65)",
          "sidebar-primary-foreground": "oklch(0.99 0.01 85)",
          "sidebar-ring": "oklch(0.54 0.12 65)",
          "stale": "oklch(0.52 0.13 75)",
          "stale-soft": "oklch(0.52 0.13 75 / 16%)",
          "tradecn-color-body-bg": "oklch(0.99 0.004 85)",
          "tradecn-color-body-fg": "oklch(0.2 0.01 85)",
          "tradecn-font-accessible": "'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-accessible-mono": "'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-mono": "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-numeric": "var(--tradecn-font-sans)",
          "tradecn-font-sans": "'Inter', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-weight-body": "450",
          "tradecn-font-weight-grid": "500",
          "tradecn-line-height-body": "1.45",
          "tradecn-line-height-grid": "1.35",
          "tradecn-numeric-variant": "lining-nums tabular-nums",
          "tradecn-text-size-body": "14px",
          "tradecn-text-size-grid": "13px",
          "tradecn-text-size-grid-min": "12px",
          "up": "oklch(0.45 0.13 245)",
          "up-soft": "oklch(0.45 0.13 245 / 18%)"
        },
        "dark": {
          "accent": "oklch(0.28 0.03 70)",
          "accent-foreground": "oklch(0.9 0.08 80)",
          "background": "oklch(0.14 0.005 85)",
          "border": "oklch(0.3 0.008 85)",
          "card": "oklch(0.19 0.006 85)",
          "card-foreground": "oklch(0.94 0.03 85)",
          "chart-1": "oklch(0.78 0.16 70)",
          "chart-2": "oklch(0.74 0.12 240)",
          "chart-3": "oklch(0.74 0.15 165)",
          "chart-4": "oklch(0.74 0.15 330)",
          "chart-5": "oklch(0.85 0.15 95)",
          "chart-6": "oklch(0.78 0.11 225)",
          "chart-7": "oklch(0.7 0.18 40)",
          "chart-8": "var(--color-foreground)",
          "destructive": "oklch(0.66 0.22 25)",
          "down": "oklch(0.78 0.18 45)",
          "down-soft": "oklch(0.78 0.18 45 / 18%)",
          "expiring": "oklch(0.74 0.17 350)",
          "expiring-soft": "oklch(0.74 0.17 350 / 18%)",
          "flat": "var(--color-muted-foreground)",
          "flat-soft": "oklch(0.7 0 0 / 14%)",
          "foreground": "oklch(0.94 0.03 85)",
          "input": "oklch(0.32 0.008 85)",
          "link-1": "oklch(0.7 0.19 25)",
          "link-2": "oklch(0.7 0.14 250)",
          "link-3": "oklch(0.74 0.14 150)",
          "link-4": "oklch(0.85 0.15 90)",
          "muted": "oklch(0.23 0.006 85)",
          "muted-foreground": "oklch(0.7 0.02 85)",
          "panel-active": "var(--color-primary)",
          "panel-drag-target": "var(--color-ring)",
          "panel-error": "var(--color-destructive)",
          "panel-sync": "oklch(0.78 0.12 220)",
          "popover": "oklch(0.21 0.006 85)",
          "popover-foreground": "oklch(0.94 0.03 85)",
          "primary": "oklch(0.78 0.16 70)",
          "primary-foreground": "oklch(0.16 0.02 70)",
          "ring": "oklch(0.7 0.15 70)",
          "secondary": "oklch(0.25 0.008 85)",
          "secondary-foreground": "oklch(0.92 0.02 85)",
          "sidebar": "oklch(0.11 0.005 85)",
          "sidebar-accent": "oklch(0.26 0.03 70)",
          "sidebar-accent-foreground": "oklch(0.9 0.08 80)",
          "sidebar-border": "oklch(0.3 0.008 85)",
          "sidebar-foreground": "oklch(0.94 0.03 85)",
          "sidebar-primary": "oklch(0.78 0.16 70)",
          "sidebar-primary-foreground": "oklch(0.16 0.02 70)",
          "sidebar-ring": "oklch(0.7 0.15 70)",
          "stale": "oklch(0.8 0.15 80)",
          "stale-soft": "oklch(0.8 0.15 80 / 16%)",
          "tradecn-color-body-bg": "oklch(0.14 0.005 85)",
          "tradecn-color-body-fg": "oklch(0.94 0.03 85)",
          "tradecn-font-accessible": "'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-accessible-mono": "'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-mono": "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-numeric": "var(--tradecn-font-sans)",
          "tradecn-font-sans": "'Inter', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-weight-body": "450",
          "tradecn-font-weight-grid": "500",
          "tradecn-line-height-body": "1.45",
          "tradecn-line-height-grid": "1.35",
          "tradecn-numeric-variant": "lining-nums tabular-nums",
          "tradecn-text-size-body": "14px",
          "tradecn-text-size-grid": "13px",
          "tradecn-text-size-grid-min": "12px",
          "up": "oklch(0.66 0.12 240)",
          "up-soft": "oklch(0.66 0.12 240 / 18%)"
        }
      },
      "css": {
        "@layer base": {
          ":root": {
            "font-variant-numeric": "var(--tradecn-numeric-variant)"
          },
          ".tradecn-num, [data-numeric]": {
            "font-variant-numeric": "var(--tradecn-numeric-variant)"
          }
        },
        ":root[data-accessibility=\"hyperlegible\"], [data-accessibility=\"hyperlegible\"]": {
          "--tradecn-font-sans": "var(--tradecn-font-accessible)",
          "--tradecn-font-mono": "var(--tradecn-font-accessible-mono)"
        }
      }
    },
    {
      "name": "tradecn-slate",
      "type": "registry:theme",
      "title": "Slate",
      "description": "A neutral look with a real light side and a real dark side: cool near-neutral surfaces, a calm blue primary, and the colorblind-safe bluish green and vermilion for up and down, every color held to 4.5 to 1 on its surface in both modes.",
      "registryDependencies": [],
      "files": [],
      "categories": [
        "theme"
      ],
      "docs": "This one overwrites. It replaces your background, foreground, primary, border, radius and the rest of shadcn's variables, with one palette for light and another for dark, and sets every tradecn token per mode. Review it with --diff first. Components are untouched. It sets the typography tokens at their defaults and appends the three base rules (the numeric variant on :root and the numeric hooks, and the hyperlegible remap).",
      "cssVars": {
        "light": {
          "accent": "oklch(0.93 0.012 245)",
          "accent-foreground": "oklch(0.25 0.03 245)",
          "background": "oklch(0.985 0.003 250)",
          "border": "oklch(0.88 0.008 250)",
          "card": "oklch(1 0 0)",
          "card-foreground": "oklch(0.2 0.02 250)",
          "chart-1": "oklch(0.5 0.15 245)",
          "chart-2": "oklch(0.58 0.15 60)",
          "chart-3": "oklch(0.52 0.13 165)",
          "chart-4": "oklch(0.5 0.15 330)",
          "chart-5": "oklch(0.58 0.12 90)",
          "chart-6": "oklch(0.52 0.12 225)",
          "chart-7": "oklch(0.5 0.18 40)",
          "chart-8": "var(--color-foreground)",
          "destructive": "oklch(0.53 0.22 27)",
          "down": "oklch(0.46 0.19 40)",
          "down-soft": "oklch(0.46 0.19 40 / 18%)",
          "expiring": "oklch(0.53 0.2 350)",
          "expiring-soft": "oklch(0.53 0.2 350 / 18%)",
          "flat": "var(--color-muted-foreground)",
          "flat-soft": "oklch(0.55 0 0 / 14%)",
          "foreground": "oklch(0.2 0.02 250)",
          "input": "oklch(0.88 0.008 250)",
          "link-1": "oklch(0.53 0.2 25)",
          "link-2": "oklch(0.5 0.15 250)",
          "link-3": "oklch(0.5 0.13 150)",
          "link-4": "oklch(0.52 0.12 85)",
          "muted": "oklch(0.95 0.006 250)",
          "muted-foreground": "oklch(0.46 0.02 250)",
          "panel-active": "var(--color-primary)",
          "panel-drag-target": "var(--color-ring)",
          "panel-error": "var(--color-destructive)",
          "panel-sync": "oklch(0.5 0.12 220)",
          "popover": "oklch(1 0 0)",
          "popover-foreground": "oklch(0.2 0.02 250)",
          "primary": "oklch(0.5 0.15 245)",
          "primary-foreground": "oklch(0.99 0.003 250)",
          "radius": "0.375rem",
          "ring": "oklch(0.5 0.15 245)",
          "secondary": "oklch(0.95 0.006 250)",
          "secondary-foreground": "oklch(0.25 0.02 250)",
          "sidebar": "oklch(0.965 0.005 250)",
          "sidebar-accent": "oklch(0.92 0.01 245)",
          "sidebar-accent-foreground": "oklch(0.25 0.03 245)",
          "sidebar-border": "oklch(0.88 0.008 250)",
          "sidebar-foreground": "oklch(0.2 0.02 250)",
          "sidebar-primary": "oklch(0.5 0.15 245)",
          "sidebar-primary-foreground": "oklch(0.99 0.003 250)",
          "sidebar-ring": "oklch(0.5 0.15 245)",
          "stale": "oklch(0.52 0.13 75)",
          "stale-soft": "oklch(0.52 0.13 75 / 16%)",
          "tradecn-color-body-bg": "oklch(0.985 0.003 250)",
          "tradecn-color-body-fg": "oklch(0.2 0.02 250)",
          "tradecn-font-accessible": "'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-accessible-mono": "'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-mono": "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-numeric": "var(--tradecn-font-sans)",
          "tradecn-font-sans": "'Inter', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-weight-body": "450",
          "tradecn-font-weight-grid": "500",
          "tradecn-line-height-body": "1.45",
          "tradecn-line-height-grid": "1.35",
          "tradecn-numeric-variant": "lining-nums tabular-nums",
          "tradecn-text-size-body": "14px",
          "tradecn-text-size-grid": "13px",
          "tradecn-text-size-grid-min": "12px",
          "up": "oklch(0.52 0.13 165)",
          "up-soft": "oklch(0.52 0.13 165 / 18%)"
        },
        "dark": {
          "accent": "oklch(0.29 0.02 245)",
          "accent-foreground": "oklch(0.95 0.008 250)",
          "background": "oklch(0.16 0.01 250)",
          "border": "oklch(0.3 0.012 250)",
          "card": "oklch(0.2 0.012 250)",
          "card-foreground": "oklch(0.95 0.008 250)",
          "chart-1": "oklch(0.74 0.12 240)",
          "chart-2": "oklch(0.78 0.15 65)",
          "chart-3": "oklch(0.74 0.15 165)",
          "chart-4": "oklch(0.74 0.15 330)",
          "chart-5": "oklch(0.85 0.13 90)",
          "chart-6": "oklch(0.78 0.11 225)",
          "chart-7": "oklch(0.7 0.18 40)",
          "chart-8": "var(--color-foreground)",
          "destructive": "oklch(0.7 0.19 22)",
          "down": "oklch(0.66 0.19 40)",
          "down-soft": "oklch(0.66 0.19 40 / 18%)",
          "expiring": "oklch(0.74 0.17 350)",
          "expiring-soft": "oklch(0.74 0.17 350 / 18%)",
          "flat": "var(--color-muted-foreground)",
          "flat-soft": "oklch(0.7 0 0 / 14%)",
          "foreground": "oklch(0.95 0.008 250)",
          "input": "oklch(0.32 0.012 250)",
          "link-1": "oklch(0.7 0.19 25)",
          "link-2": "oklch(0.7 0.14 250)",
          "link-3": "oklch(0.74 0.14 150)",
          "link-4": "oklch(0.85 0.15 90)",
          "muted": "oklch(0.24 0.01 250)",
          "muted-foreground": "oklch(0.72 0.015 250)",
          "panel-active": "var(--color-primary)",
          "panel-drag-target": "var(--color-ring)",
          "panel-error": "var(--color-destructive)",
          "panel-sync": "oklch(0.78 0.12 220)",
          "popover": "oklch(0.21 0.012 250)",
          "popover-foreground": "oklch(0.95 0.008 250)",
          "primary": "oklch(0.74 0.12 240)",
          "primary-foreground": "oklch(0.16 0.02 250)",
          "ring": "oklch(0.7 0.12 240)",
          "secondary": "oklch(0.26 0.012 250)",
          "secondary-foreground": "oklch(0.95 0.008 250)",
          "sidebar": "oklch(0.13 0.01 250)",
          "sidebar-accent": "oklch(0.24 0.012 250)",
          "sidebar-accent-foreground": "oklch(0.95 0.008 250)",
          "sidebar-border": "oklch(0.3 0.012 250)",
          "sidebar-foreground": "oklch(0.95 0.008 250)",
          "sidebar-primary": "oklch(0.74 0.12 240)",
          "sidebar-primary-foreground": "oklch(0.16 0.02 250)",
          "sidebar-ring": "oklch(0.7 0.12 240)",
          "stale": "oklch(0.8 0.15 80)",
          "stale-soft": "oklch(0.8 0.15 80 / 16%)",
          "tradecn-color-body-bg": "oklch(0.16 0.01 250)",
          "tradecn-color-body-fg": "oklch(0.95 0.008 250)",
          "tradecn-font-accessible": "'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-accessible-mono": "'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-mono": "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-numeric": "var(--tradecn-font-sans)",
          "tradecn-font-sans": "'Inter', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-weight-body": "450",
          "tradecn-font-weight-grid": "500",
          "tradecn-line-height-body": "1.45",
          "tradecn-line-height-grid": "1.35",
          "tradecn-numeric-variant": "lining-nums tabular-nums",
          "tradecn-text-size-body": "14px",
          "tradecn-text-size-grid": "13px",
          "tradecn-text-size-grid-min": "12px",
          "up": "oklch(0.77 0.14 165)",
          "up-soft": "oklch(0.77 0.14 165 / 18%)"
        }
      },
      "css": {
        "@layer base": {
          ":root": {
            "font-variant-numeric": "var(--tradecn-numeric-variant)"
          },
          ".tradecn-num, [data-numeric]": {
            "font-variant-numeric": "var(--tradecn-numeric-variant)"
          }
        },
        ":root[data-accessibility=\"hyperlegible\"], [data-accessibility=\"hyperlegible\"]": {
          "--tradecn-font-sans": "var(--tradecn-font-accessible)",
          "--tradecn-font-mono": "var(--tradecn-font-accessible-mono)"
        }
      }
    },
    {
      "name": "tradecn-slate-east",
      "type": "registry:theme",
      "title": "Slate East",
      "description": "Slate with the direction pair turned around, red for up and green for down, as screens in China, Japan, and Taiwan show it. The same colorblind-safe vermilion and bluish green, so it survives the same eyes.",
      "registryDependencies": [],
      "files": [],
      "categories": [
        "theme"
      ],
      "docs": "This one overwrites. It replaces your background, foreground, primary, border, radius and the rest of shadcn's variables, with one palette for light and another for dark, and sets every tradecn token per mode. Review it with --diff first. Components are untouched. It sets the typography tokens at their defaults and appends the three base rules (the numeric variant on :root and the numeric hooks, and the hyperlegible remap). It differs from tradecn-slate in four tokens only: up, down, up-soft, and down-soft. Install one or the other, not both.",
      "cssVars": {
        "light": {
          "accent": "oklch(0.93 0.012 245)",
          "accent-foreground": "oklch(0.25 0.03 245)",
          "background": "oklch(0.985 0.003 250)",
          "border": "oklch(0.88 0.008 250)",
          "card": "oklch(1 0 0)",
          "card-foreground": "oklch(0.2 0.02 250)",
          "chart-1": "oklch(0.5 0.15 245)",
          "chart-2": "oklch(0.58 0.15 60)",
          "chart-3": "oklch(0.52 0.13 165)",
          "chart-4": "oklch(0.5 0.15 330)",
          "chart-5": "oklch(0.58 0.12 90)",
          "chart-6": "oklch(0.52 0.12 225)",
          "chart-7": "oklch(0.5 0.18 40)",
          "chart-8": "var(--color-foreground)",
          "destructive": "oklch(0.53 0.22 27)",
          "down": "oklch(0.52 0.13 165)",
          "down-soft": "oklch(0.52 0.13 165 / 18%)",
          "expiring": "oklch(0.53 0.2 350)",
          "expiring-soft": "oklch(0.53 0.2 350 / 18%)",
          "flat": "var(--color-muted-foreground)",
          "flat-soft": "oklch(0.55 0 0 / 14%)",
          "foreground": "oklch(0.2 0.02 250)",
          "input": "oklch(0.88 0.008 250)",
          "link-1": "oklch(0.53 0.2 25)",
          "link-2": "oklch(0.5 0.15 250)",
          "link-3": "oklch(0.5 0.13 150)",
          "link-4": "oklch(0.52 0.12 85)",
          "muted": "oklch(0.95 0.006 250)",
          "muted-foreground": "oklch(0.46 0.02 250)",
          "panel-active": "var(--color-primary)",
          "panel-drag-target": "var(--color-ring)",
          "panel-error": "var(--color-destructive)",
          "panel-sync": "oklch(0.5 0.12 220)",
          "popover": "oklch(1 0 0)",
          "popover-foreground": "oklch(0.2 0.02 250)",
          "primary": "oklch(0.5 0.15 245)",
          "primary-foreground": "oklch(0.99 0.003 250)",
          "radius": "0.375rem",
          "ring": "oklch(0.5 0.15 245)",
          "secondary": "oklch(0.95 0.006 250)",
          "secondary-foreground": "oklch(0.25 0.02 250)",
          "sidebar": "oklch(0.965 0.005 250)",
          "sidebar-accent": "oklch(0.92 0.01 245)",
          "sidebar-accent-foreground": "oklch(0.25 0.03 245)",
          "sidebar-border": "oklch(0.88 0.008 250)",
          "sidebar-foreground": "oklch(0.2 0.02 250)",
          "sidebar-primary": "oklch(0.5 0.15 245)",
          "sidebar-primary-foreground": "oklch(0.99 0.003 250)",
          "sidebar-ring": "oklch(0.5 0.15 245)",
          "stale": "oklch(0.52 0.13 75)",
          "stale-soft": "oklch(0.52 0.13 75 / 16%)",
          "tradecn-color-body-bg": "oklch(0.985 0.003 250)",
          "tradecn-color-body-fg": "oklch(0.2 0.02 250)",
          "tradecn-font-accessible": "'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-accessible-mono": "'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-mono": "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-numeric": "var(--tradecn-font-sans)",
          "tradecn-font-sans": "'Inter', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-weight-body": "450",
          "tradecn-font-weight-grid": "500",
          "tradecn-line-height-body": "1.45",
          "tradecn-line-height-grid": "1.35",
          "tradecn-numeric-variant": "lining-nums tabular-nums",
          "tradecn-text-size-body": "14px",
          "tradecn-text-size-grid": "13px",
          "tradecn-text-size-grid-min": "12px",
          "up": "oklch(0.46 0.19 40)",
          "up-soft": "oklch(0.46 0.19 40 / 18%)"
        },
        "dark": {
          "accent": "oklch(0.29 0.02 245)",
          "accent-foreground": "oklch(0.95 0.008 250)",
          "background": "oklch(0.16 0.01 250)",
          "border": "oklch(0.3 0.012 250)",
          "card": "oklch(0.2 0.012 250)",
          "card-foreground": "oklch(0.95 0.008 250)",
          "chart-1": "oklch(0.74 0.12 240)",
          "chart-2": "oklch(0.78 0.15 65)",
          "chart-3": "oklch(0.74 0.15 165)",
          "chart-4": "oklch(0.74 0.15 330)",
          "chart-5": "oklch(0.85 0.13 90)",
          "chart-6": "oklch(0.78 0.11 225)",
          "chart-7": "oklch(0.7 0.18 40)",
          "chart-8": "var(--color-foreground)",
          "destructive": "oklch(0.7 0.19 22)",
          "down": "oklch(0.77 0.14 165)",
          "down-soft": "oklch(0.77 0.14 165 / 18%)",
          "expiring": "oklch(0.74 0.17 350)",
          "expiring-soft": "oklch(0.74 0.17 350 / 18%)",
          "flat": "var(--color-muted-foreground)",
          "flat-soft": "oklch(0.7 0 0 / 14%)",
          "foreground": "oklch(0.95 0.008 250)",
          "input": "oklch(0.32 0.012 250)",
          "link-1": "oklch(0.7 0.19 25)",
          "link-2": "oklch(0.7 0.14 250)",
          "link-3": "oklch(0.74 0.14 150)",
          "link-4": "oklch(0.85 0.15 90)",
          "muted": "oklch(0.24 0.01 250)",
          "muted-foreground": "oklch(0.72 0.015 250)",
          "panel-active": "var(--color-primary)",
          "panel-drag-target": "var(--color-ring)",
          "panel-error": "var(--color-destructive)",
          "panel-sync": "oklch(0.78 0.12 220)",
          "popover": "oklch(0.21 0.012 250)",
          "popover-foreground": "oklch(0.95 0.008 250)",
          "primary": "oklch(0.74 0.12 240)",
          "primary-foreground": "oklch(0.16 0.02 250)",
          "ring": "oklch(0.7 0.12 240)",
          "secondary": "oklch(0.26 0.012 250)",
          "secondary-foreground": "oklch(0.95 0.008 250)",
          "sidebar": "oklch(0.13 0.01 250)",
          "sidebar-accent": "oklch(0.24 0.012 250)",
          "sidebar-accent-foreground": "oklch(0.95 0.008 250)",
          "sidebar-border": "oklch(0.3 0.012 250)",
          "sidebar-foreground": "oklch(0.95 0.008 250)",
          "sidebar-primary": "oklch(0.74 0.12 240)",
          "sidebar-primary-foreground": "oklch(0.16 0.02 250)",
          "sidebar-ring": "oklch(0.7 0.12 240)",
          "stale": "oklch(0.8 0.15 80)",
          "stale-soft": "oklch(0.8 0.15 80 / 16%)",
          "tradecn-color-body-bg": "oklch(0.16 0.01 250)",
          "tradecn-color-body-fg": "oklch(0.95 0.008 250)",
          "tradecn-font-accessible": "'Atkinson Hyperlegible Next', 'Atkinson Hyperlegible', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-accessible-mono": "'Atkinson Hyperlegible Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-mono": "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
          "tradecn-font-numeric": "var(--tradecn-font-sans)",
          "tradecn-font-sans": "'Inter', ui-sans-serif, system-ui, sans-serif",
          "tradecn-font-weight-body": "450",
          "tradecn-font-weight-grid": "500",
          "tradecn-line-height-body": "1.45",
          "tradecn-line-height-grid": "1.35",
          "tradecn-numeric-variant": "lining-nums tabular-nums",
          "tradecn-text-size-body": "14px",
          "tradecn-text-size-grid": "13px",
          "tradecn-text-size-grid-min": "12px",
          "up": "oklch(0.66 0.19 40)",
          "up-soft": "oklch(0.66 0.19 40 / 18%)"
        }
      },
      "css": {
        "@layer base": {
          ":root": {
            "font-variant-numeric": "var(--tradecn-numeric-variant)"
          },
          ".tradecn-num, [data-numeric]": {
            "font-variant-numeric": "var(--tradecn-numeric-variant)"
          }
        },
        ":root[data-accessibility=\"hyperlegible\"], [data-accessibility=\"hyperlegible\"]": {
          "--tradecn-font-sans": "var(--tradecn-font-accessible)",
          "--tradecn-font-mono": "var(--tradecn-font-accessible-mono)"
        }
      }
    }
  ]
}
