From e08d2bd5e5758edba756a09e410064fffd3c1ce7 Mon Sep 17 00:00:00 2001
From: Amr Mohammed El-Sheraey <141947355+AmrMsCLL@users.noreply.github.com>
Date: Thu, 1 Oct 2026 23:13:27 +0300
Subject: [PATCH] feat: add the components Strata's foundation needs
Adds Tabs, TagChip with a fixed set of tag colours, Avatar with a presence dot, CodeInput for
one-time codes, Sidebar, and CommandPalette with a Ctrl+K and Cmd+K shortcut hook. AppShell
takes an optional sidebar that sits beside the page on wider screens. The search icon joins the
icon set, and the preview shows every new component.
---
preview/main.tsx | 77 +++++++++-
src/components/AppShell.tsx | 17 ++-
src/components/Avatar.tsx | 16 ++
src/components/CodeInput.tsx | 25 ++++
src/components/CommandPalette.tsx | 161 ++++++++++++++++++++
src/components/Icon.tsx | 1 +
src/components/Sidebar.tsx | 50 +++++++
src/components/Tabs.tsx | 25 ++++
src/components/TagChip.tsx | 41 ++++++
src/index.ts | 11 ++
src/theme.css | 236 ++++++++++++++++++++++++++++++
test/workspace.spec.tsx | 174 ++++++++++++++++++++++
12 files changed, 829 insertions(+), 5 deletions(-)
create mode 100644 src/components/Avatar.tsx
create mode 100644 src/components/CodeInput.tsx
create mode 100644 src/components/CommandPalette.tsx
create mode 100644 src/components/Sidebar.tsx
create mode 100644 src/components/Tabs.tsx
create mode 100644 src/components/TagChip.tsx
create mode 100644 test/workspace.spec.tsx
diff --git a/preview/main.tsx b/preview/main.tsx
index a37daa5..d92b33c 100644
--- a/preview/main.tsx
+++ b/preview/main.tsx
@@ -1,7 +1,10 @@
-import { StrictMode, useState, type ReactNode } from 'react';
+import { StrictMode, useCallback, useState, type ReactNode } from 'react';
import { createRoot } from 'react-dom/client';
import {
AppShell,
+ Avatar,
+ CodeInput,
+ CommandPalette,
ConfirmDialog,
Dialog,
EmptyState,
@@ -14,13 +17,19 @@ import {
OutageGate,
Pagination,
Popover,
+ Sidebar,
SmoothImage,
SnackbarProvider,
+ Tabs,
+ TagChip,
+ tagColors,
TextAreaField,
TextField,
Toggle,
timeAgo,
+ useCommandShortcut,
useSnackbar,
+ type CommandItem,
type IconName,
} from '../src/index.ts';
import './styles.css';
@@ -38,7 +47,13 @@ const colors = [
['wip', 'bg-wip'],
['dormant', 'bg-dormant'],
];
-const icons: IconName[] = ['bell', 'compass', 'filter', 'gamepad', 'home', 'library', 'user'];
+const icons: IconName[] = ['bell', 'compass', 'filter', 'gamepad', 'home', 'library', 'search', 'user'];
+const commands: CommandItem[] = [
+ { group: 'Go to', href: '#workspace', id: 'notes', label: 'Notes', hint: 'g n' },
+ { group: 'Go to', href: '#workspace', id: 'agenda', label: 'Agenda', hint: 'g a' },
+ { group: 'Actions', id: 'task', label: 'New task', onSelect: () => {} },
+ { group: 'Actions', id: 'note', label: 'New note', onSelect: () => {} },
+];
const outageListeners = new Set<() => void>();
function Section({ children, title }: { children: ReactNode; title: string }) {
@@ -65,6 +80,10 @@ function SnackbarDemo() {
function Preview() {
const [dialog, setDialog] = useState<'plain' | 'confirm' | null>(null);
+ const [palette, setPalette] = useState(false);
+ const [query, setQuery] = useState('');
+ const openPalette = useCallback(() => setPalette(true), []);
+ useCommandShortcut(openPalette);
const [checked, setChecked] = useState(true);
const [page, setPage] = useState(3);
@@ -240,6 +259,60 @@ function Preview() {
)}
+
+
+
+ {tagColors.map((color) => (
+
+ ))}
+
+ {}} />
+
+
+
+
+
+
+
+
+
+ {palette && (
+ command.label.toLowerCase().includes(query.toLowerCase()))}
+ onClose={() => {
+ setPalette(false);
+ setQuery('');
+ }}
+ onQueryChange={setQuery}
+ query={query}
+ />
+ )}
+
+
diff --git a/src/components/AppShell.tsx b/src/components/AppShell.tsx
index d0680d8..0a0cd6e 100644
--- a/src/components/AppShell.tsx
+++ b/src/components/AppShell.tsx
@@ -21,6 +21,7 @@ export function AppShell({
children,
footer,
nav,
+ sidebar,
tabs,
}: {
actions?: ReactNode;
@@ -29,6 +30,7 @@ export function AppShell({
children: ReactNode;
footer?: ReactNode;
nav: ShellNavItem[];
+ sidebar?: ReactNode;
tabs: ShellTab[];
}) {
const Link = useLink();
@@ -62,9 +64,18 @@ export function AppShell({
{alert}
-
- {children}
-
+ {sidebar ? (
+
+ ) : (
+
+ {children}
+
+ )}
{footer}