mirror of
https://github.com/Dvorinka/Dash.git
synced 2026-10-29 08:04:58 +00:00
feat: phase 3 renderers & ergonomics — index/console, ⌘K palette, icon suggest
- Index renderer (editorial): serif masthead + section heads, numbered full-width rows, live stat strip (time/services/up/down/widgets) - Console renderer (terminal): $ dash status banner, bordered stat panels, ## section heads, › prompt rows, [ up ]/[ down ] tags - Renderer contract gains index prop for row/section numbering - Settings: 2x2 renderer radio grid with per-renderer descriptions - ⌘K/Ctrl+K// palette: actions + service endpoints, arrow-key nav, zero-dep implementation on the existing Dialog primitive - Icon picker: dashboard-icons slug suggestion chip + live preview - Fonts: Instrument Serif, JetBrains Mono (fontsource) - Fix: global :focus-visible rule was unlayered and silently overrode all focus-visible: utilities since phase 1 — moved into @layer base Verified: typecheck/lint/build clean, dnd + collapse + popover in both new renderers, palette filter + execute, icon suggest resolves real CDN assets, 360px + light theme, docker image 43.9 MB serving board + widget types. Generated with [Devin](https://devin.ai) Authored By: TDvorak <[email protected]> Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
8afc7df53b
commit
55fb57456e
16 files changed
+803
-51
No files matched your search
@@ -1,10 +1,11 @@
|
||||
import { useState } from "react";
|
||||
import { LayoutGrid, Moon, Plus, Settings, Sun } from "lucide-react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { LayoutGrid, Moon, Plus, Search, Settings, Sun } from "lucide-react";
|
||||
import { BoardProvider, useBoard } from "@/board/store";
|
||||
import { Board } from "@/board/Board";
|
||||
import { ServiceDialog } from "@/components/ServiceDialog";
|
||||
import { WidgetDialog } from "@/components/WidgetDialog";
|
||||
import { SettingsDialog } from "@/components/SettingsDialog";
|
||||
import { CommandPalette } from "@/components/CommandPalette";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import type { Item } from "@/types";
|
||||
|
||||
@@ -19,10 +20,28 @@ export default function App() {
|
||||
function Shell() {
|
||||
const board = useBoard();
|
||||
const [settingsOpen, setSettingsOpen] = useState(false);
|
||||
const [paletteOpen, setPaletteOpen] = useState(false);
|
||||
const [svcOpen, setSvcOpen] = useState(false);
|
||||
const [wdgOpen, setWdgOpen] = useState(false);
|
||||
const [editing, setEditing] = useState<Item | undefined>();
|
||||
|
||||
// ⌘K / Ctrl+K opens the palette; / does too when not typing in a field.
|
||||
useEffect(() => {
|
||||
const onKey = (e: KeyboardEvent) => {
|
||||
const typing = e.target instanceof HTMLElement
|
||||
&& (e.target.tagName === "INPUT" || e.target.tagName === "TEXTAREA" || e.target.isContentEditable);
|
||||
if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "k") {
|
||||
e.preventDefault();
|
||||
setPaletteOpen((o) => !o);
|
||||
} else if (e.key === "/" && !typing && !e.metaKey && !e.ctrlKey) {
|
||||
e.preventDefault();
|
||||
setPaletteOpen(true);
|
||||
}
|
||||
};
|
||||
window.addEventListener("keydown", onKey);
|
||||
return () => window.removeEventListener("keydown", onKey);
|
||||
}, []);
|
||||
|
||||
function openEditor(it: Item) {
|
||||
setEditing(it);
|
||||
if (it.kind === "widget") setWdgOpen(true);
|
||||
@@ -42,6 +61,14 @@ function Shell() {
|
||||
Dash
|
||||
</div>
|
||||
<div className="ml-auto flex items-center gap-2">
|
||||
<Button
|
||||
variant="outline" aria-label="Search (⌘K)"
|
||||
onClick={() => setPaletteOpen(true)}
|
||||
className="gap-1.5 text-text-faint"
|
||||
>
|
||||
<Search size={14} strokeWidth={1.8} />
|
||||
<kbd className="hidden font-mono text-[10px] min-[620px]:inline">⌘K</kbd>
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline" size="icon" aria-label="Settings"
|
||||
onClick={() => setSettingsOpen(true)}
|
||||
@@ -73,6 +100,13 @@ function Shell() {
|
||||
<ServiceDialog open={svcOpen} onOpenChange={setSvcOpen} item={editing?.kind === "service" ? editing : undefined} />
|
||||
<WidgetDialog open={wdgOpen} onOpenChange={setWdgOpen} item={editing?.kind === "widget" ? editing : undefined} />
|
||||
<SettingsDialog open={settingsOpen} onOpenChange={setSettingsOpen} />
|
||||
<CommandPalette
|
||||
open={paletteOpen}
|
||||
onOpenChange={setPaletteOpen}
|
||||
onAddService={() => { setEditing(undefined); setSvcOpen(true); }}
|
||||
onAddWidget={() => { setEditing(undefined); setWdgOpen(true); }}
|
||||
onSettings={() => setSettingsOpen(true)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user