diff --git a/apps/demo/src/index.css b/apps/demo/src/index.css index 2ecd63f1..518c41a1 100644 --- a/apps/demo/src/index.css +++ b/apps/demo/src/index.css @@ -61,35 +61,6 @@ --shadow-input-inset: inset 0 1px 2px rgba(0, 0, 0, 0.02); } -/* ═══════════════════════════════════════════════════════════════════ - Tabular digits — all numbers render in JetBrains Mono - ═══════════════════════════════════════════════════════════════════ */ - -@font-face { - font-family: "Digits"; - src: local("JetBrains Mono"), url("/fonts/JetBrainsMono-Regular.woff2") format("woff2"); - unicode-range: U+0030-0039, U+002E, U+002C, U+0025, U+002B, U+002D, U+003A; - font-weight: 400; - font-display: swap; - size-adjust: 88%; -} -@font-face { - font-family: "Digits"; - src: local("JetBrains Mono Medium"), url("/fonts/JetBrainsMono-Medium.woff2") format("woff2"); - unicode-range: U+0030-0039, U+002E, U+002C, U+0025, U+002B, U+002D, U+003A; - font-weight: 500; - font-display: swap; - size-adjust: 88%; -} -@font-face { - font-family: "Digits"; - src: local("JetBrains Mono Bold"), url("/fonts/JetBrainsMono-Bold.woff2") format("woff2"); - unicode-range: U+0030-0039, U+002E, U+002C, U+0025, U+002B, U+002D, U+003A; - font-weight: 600 900; - font-display: swap; - size-adjust: 88%; -} - /* ═══════════════════════════════════════════════════════════════════ Global resets ═══════════════════════════════════════════════════════════════════ */ diff --git a/apps/demo/src/pages/AvatarPage.tsx b/apps/demo/src/pages/AvatarPage.tsx index fd58d8b3..6ce1ca90 100644 --- a/apps/demo/src/pages/AvatarPage.tsx +++ b/apps/demo/src/pages/AvatarPage.tsx @@ -1,5 +1,4 @@ -import { Button } from "@nexu-design/ui-web"; -import { PageHeader, PageShell, SectionHeader } from "@nexu-design/ui-web"; +import { Button, PageHeader, PageShell, SectionHeader } from "@nexu-design/ui-web"; import { useState } from "react"; const SKIN_TONES = [ diff --git a/apps/demo/src/pages/ComponentsPage.tsx b/apps/demo/src/pages/ComponentsPage.tsx index 0af62dbf..c52161dd 100644 --- a/apps/demo/src/pages/ComponentsPage.tsx +++ b/apps/demo/src/pages/ComponentsPage.tsx @@ -1,6 +1,4 @@ -import { Button } from "@nexu-design/ui-web"; - -import { PageHeader, PageShell, SectionHeader } from "@nexu-design/ui-web"; +import { Button, PageHeader, PageShell, SectionHeader } from "@nexu-design/ui-web"; import { AlertTriangle, ArrowRight, diff --git a/apps/demo/src/pages/CopyPage.tsx b/apps/demo/src/pages/CopyPage.tsx index a333311f..65e5c25b 100644 --- a/apps/demo/src/pages/CopyPage.tsx +++ b/apps/demo/src/pages/CopyPage.tsx @@ -1,6 +1,4 @@ -import { Button } from "@nexu-design/ui-web"; - -import { PageHeader, PageShell, SectionHeader } from "@nexu-design/ui-web"; +import { Button, PageHeader, PageShell, SectionHeader } from "@nexu-design/ui-web"; function ChatBubble({ from, diff --git a/apps/demo/src/pages/MotionPage.tsx b/apps/demo/src/pages/MotionPage.tsx index f33899ee..4aba7eb7 100644 --- a/apps/demo/src/pages/MotionPage.tsx +++ b/apps/demo/src/pages/MotionPage.tsx @@ -1,5 +1,4 @@ -import { Button } from "@nexu-design/ui-web"; -import { PageHeader, PageShell, SectionHeader } from "@nexu-design/ui-web"; +import { Button, PageHeader, PageShell, SectionHeader } from "@nexu-design/ui-web"; import { useEffect, useState } from "react"; import NexuLoader, { NexuLoadingScreen } from "../components/NexuLoader"; diff --git a/apps/demo/src/pages/nexu/NexuAvatarDetailPage.tsx b/apps/demo/src/pages/nexu/NexuAvatarDetailPage.tsx index 1f8f1b5d..72ee7575 100644 --- a/apps/demo/src/pages/nexu/NexuAvatarDetailPage.tsx +++ b/apps/demo/src/pages/nexu/NexuAvatarDetailPage.tsx @@ -127,7 +127,7 @@ export default function NexuAvatarDetailPage() { {avatar.skills.map((skill) => ( - + {skill} ))} diff --git a/apps/demo/src/pages/nexu/NexuAvatarsPage.tsx b/apps/demo/src/pages/nexu/NexuAvatarsPage.tsx index 1e7f19a3..bcdcdabe 100644 --- a/apps/demo/src/pages/nexu/NexuAvatarsPage.tsx +++ b/apps/demo/src/pages/nexu/NexuAvatarsPage.tsx @@ -80,7 +80,7 @@ export default function NexuAvatarsPage() { role.status === "busy" ? "warning" : role.status === "waiting" - ? "brand" + ? "accent" : "success" } className="shrink-0 text-[10px]" diff --git a/apps/demo/src/pages/nexu/NexuDashboardPage.tsx b/apps/demo/src/pages/nexu/NexuDashboardPage.tsx index b3a70b5d..ddd76ada 100644 --- a/apps/demo/src/pages/nexu/NexuDashboardPage.tsx +++ b/apps/demo/src/pages/nexu/NexuDashboardPage.tsx @@ -152,7 +152,7 @@ export default function NexuDashboardPage() { a.status === "busy" ? "warning" : a.status === "waiting" - ? "brand" + ? "accent" : "success" } size="xs" diff --git a/apps/demo/src/pages/nexu/NexuLanding.tsx b/apps/demo/src/pages/nexu/NexuLanding.tsx index af380a56..9f168470 100644 --- a/apps/demo/src/pages/nexu/NexuLanding.tsx +++ b/apps/demo/src/pages/nexu/NexuLanding.tsx @@ -54,7 +54,7 @@ function FeatureCard({ {badge && ( - + {badge} )} @@ -150,7 +150,7 @@ export default function NexuLanding() { N nexu - + 分身监控管理 @@ -185,7 +185,7 @@ export default function NexuLanding() { />
- + 基于 Refly 能力 · 多数字员工,赛博工作室

diff --git a/apps/demo/src/pages/nexu/NexuProgressPage.tsx b/apps/demo/src/pages/nexu/NexuProgressPage.tsx index b5e987af..f66b1132 100644 --- a/apps/demo/src/pages/nexu/NexuProgressPage.tsx +++ b/apps/demo/src/pages/nexu/NexuProgressPage.tsx @@ -124,7 +124,7 @@ export default function NexuProgressPage() {

{run.status === "success" ? "成功" : "待审批"} diff --git a/apps/demo/src/pages/nexu/NexuSkillsKnowledgePage.tsx b/apps/demo/src/pages/nexu/NexuSkillsKnowledgePage.tsx index 09548959..13969692 100644 --- a/apps/demo/src/pages/nexu/NexuSkillsKnowledgePage.tsx +++ b/apps/demo/src/pages/nexu/NexuSkillsKnowledgePage.tsx @@ -59,7 +59,7 @@ export default function NexuSkillsKnowledgePage() {
{row.skills.map((s) => ( - + {s} diff --git a/apps/demo/src/pages/openclaw/BillingPage.tsx b/apps/demo/src/pages/openclaw/BillingPage.tsx index f580dcba..c69d81d3 100644 --- a/apps/demo/src/pages/openclaw/BillingPage.tsx +++ b/apps/demo/src/pages/openclaw/BillingPage.tsx @@ -121,7 +121,7 @@ export default function BillingPage() { value={(totalCredits - totalUsed).toLocaleString()} icon={Crown} tone="success" - trend={{ label: "Free plan", variant: "brand" }} + trend={{ label: "Free plan", variant: "accent" }} />

{channel.name}

{dep.title} {dep.source === "content" && ( - + Content )} diff --git a/apps/demo/src/pages/openclaw/OpenClawLanding.tsx b/apps/demo/src/pages/openclaw/OpenClawLanding.tsx index 3135dabb..d614c12d 100644 --- a/apps/demo/src/pages/openclaw/OpenClawLanding.tsx +++ b/apps/demo/src/pages/openclaw/OpenClawLanding.tsx @@ -253,7 +253,7 @@ function FeatureCard({
{badge && ( - + {badge} )} @@ -762,13 +762,13 @@ export default function OpenClawLanding() {
- + {c.tag}
{c.prompt}
{c.result}
- + {c.time}
diff --git a/apps/demo/src/pages/product/AutomationPage.tsx b/apps/demo/src/pages/product/AutomationPage.tsx index dcdd7597..26606d3c 100644 --- a/apps/demo/src/pages/product/AutomationPage.tsx +++ b/apps/demo/src/pages/product/AutomationPage.tsx @@ -4,11 +4,12 @@ import { AlertTitle, Button, FollowUpInput, - InspectorPanel, StatCard, ToggleGroup, ToggleGroupItem, } from "@nexu-design/ui-web"; +import type * as React from "react"; +import { InspectorPanel } from "./InspectorPanel"; import { AlertCircle, BarChart3, diff --git a/packages/ui-web/src/patterns/inspector-panel.tsx b/apps/demo/src/pages/product/InspectorPanel.tsx similarity index 71% rename from packages/ui-web/src/patterns/inspector-panel.tsx rename to apps/demo/src/pages/product/InspectorPanel.tsx index c5d5234c..73d752c9 100644 --- a/packages/ui-web/src/patterns/inspector-panel.tsx +++ b/apps/demo/src/pages/product/InspectorPanel.tsx @@ -1,6 +1,5 @@ import type * as React from "react"; -import { cn } from "../lib/cn"; import { DetailPanel, DetailPanelCloseButton, @@ -10,8 +9,7 @@ import { DetailPanelHeader, type DetailPanelProps, DetailPanelTitle, -} from "../primitives/detail-panel"; -import { PanelFooter } from "../primitives/panel-footer"; +} from "@nexu-design/ui-web"; export interface InspectorPanelProps extends Omit { title: React.ReactNode; @@ -54,16 +52,18 @@ export function InspectorPanel({ ...props }: InspectorPanelProps) { return ( - - + + {leading}
{badges ? ( -
+
{badges}
) : null} - {title} + + {title} + {description ? ( {description} @@ -71,10 +71,7 @@ export function InspectorPanel({ ) : null} {meta ? (
{meta}
@@ -84,7 +81,9 @@ export function InspectorPanel({ {children} {footer ? ( - {footer} +
+ {footer} +
) : null} ); diff --git a/apps/demo/src/pages/product/ProductDemoPage.tsx b/apps/demo/src/pages/product/ProductDemoPage.tsx index 0aec9685..3da79ac9 100644 --- a/apps/demo/src/pages/product/ProductDemoPage.tsx +++ b/apps/demo/src/pages/product/ProductDemoPage.tsx @@ -6,15 +6,12 @@ import { ActivityBarIndicator, ActivityBarItem, DetailPanel, - FileEditor, - FileTree, NavigationMenu, NavigationMenuButton, NavigationMenuItem, Sidebar, SidebarContent, SidebarHeader, - WorkspaceShell, } from "@nexu-design/ui-web"; import { Clock, @@ -34,7 +31,9 @@ import { useLocation, useNavigate } from "react-router-dom"; import AutomationPage from "./AutomationPage"; import CloneBuilderPage from "./CloneBuilderPage"; -import { CLONE_FILE_TREE, FOLDER_ICONS, type FileNode } from "./FileTree"; +import { FileEditor } from "./FileEditor"; +import { CLONE_FILE_TREE, FileTree, FOLDER_ICONS, type FileNode } from "./FileTree"; +import { WorkspaceShell } from "./WorkspaceShell"; import { ProductLayoutContext } from "./ProductLayoutContext"; import SessionsPage from "./SessionsPage"; import SkillsPage from "./SkillsPage"; @@ -288,7 +287,7 @@ export default function ProductDemoPage() { lastEditedBy={getFile(openFilePath)?.lastEditedBy} lastEditedAt={getFile(openFilePath)?.lastEditedAt} onClose={handleCloseFile} - onSave={(content) => saveFile(openFilePath, content, "human")} + onSave={(content: string) => saveFile(openFilePath, content, "human")} /> ) : null diff --git a/apps/demo/src/pages/product/ProductLayout.tsx b/apps/demo/src/pages/product/ProductLayout.tsx index 0780e648..f3642b7d 100644 --- a/apps/demo/src/pages/product/ProductLayout.tsx +++ b/apps/demo/src/pages/product/ProductLayout.tsx @@ -6,15 +6,12 @@ import { ActivityBarIndicator, ActivityBarItem, DetailPanel, - FileEditor, - FileTree, NavigationMenu, NavigationMenuButton, NavigationMenuItem, Sidebar, SidebarContent, SidebarHeader, - WorkspaceShell, } from "@nexu-design/ui-web"; import { Clock, @@ -31,7 +28,9 @@ import { import { type ReactNode, useCallback, useMemo, useState } from "react"; import { NavLink, useLocation, useNavigate } from "react-router-dom"; -import { CLONE_FILE_TREE, FOLDER_ICONS, FOLDER_ROUTES, type FileNode } from "./FileTree"; +import { FileEditor } from "./FileEditor"; +import { CLONE_FILE_TREE, FileTree, FOLDER_ICONS, FOLDER_ROUTES, type FileNode } from "./FileTree"; +import { WorkspaceShell } from "./WorkspaceShell"; import { ProductLayoutContext } from "./ProductLayoutContext"; import { getFile, saveFile } from "./fileStore"; diff --git a/packages/ui-web/src/patterns/workspace-shell.tsx b/apps/demo/src/pages/product/WorkspaceShell.tsx similarity index 50% rename from packages/ui-web/src/patterns/workspace-shell.tsx rename to apps/demo/src/pages/product/WorkspaceShell.tsx index 104d0f7b..be56e0c6 100644 --- a/packages/ui-web/src/patterns/workspace-shell.tsx +++ b/apps/demo/src/pages/product/WorkspaceShell.tsx @@ -1,7 +1,6 @@ import * as React from "react"; -import { cn } from "../lib/cn"; -import { ResizableHandle, ResizablePanel, SplitView } from "../primitives/split-view"; +import { ResizableHandle, ResizablePanel, SplitView } from "@nexu-design/ui-web"; export interface WorkspaceShellProps extends React.HTMLAttributes { activityBar?: React.ReactNode; @@ -21,38 +20,6 @@ export interface WorkspaceShellProps extends React.HTMLAttributes({ - value, - defaultValue, - onChange, -}: { - value: T | undefined; - defaultValue: T; - onChange?: (value: T) => void; -}) { - const [internalValue, setInternalValue] = React.useState(defaultValue); - const isControlled = value !== undefined; - const currentValue = isControlled ? value : internalValue; - - const setValue = React.useCallback( - (nextValue: React.SetStateAction) => { - const resolvedValue = - typeof nextValue === "function" - ? (nextValue as (previousValue: T) => T)(currentValue) - : nextValue; - - if (!isControlled) { - setInternalValue(resolvedValue); - } - - onChange?.(resolvedValue); - }, - [currentValue, isControlled, onChange], - ); - - return [currentValue, setValue] as const; -} - export function WorkspaceShell({ activityBar, sidebar, @@ -70,42 +37,55 @@ export function WorkspaceShell({ mainClassName, contentClassName, sidebarResizerClassName, - ...props + ...rest }: WorkspaceShellProps) { const [isDragging, setIsDragging] = React.useState(false); const resizeStartWidthRef = React.useRef(sidebarDefaultWidth); - const [sidebarCollapsed, setSidebarCollapsed] = useControllableState({ - value: sidebarCollapsedProp, - defaultValue: defaultSidebarCollapsed, - onChange: onSidebarCollapsedChange, - }); - const [sidebarWidth, setSidebarWidth] = useControllableState({ - value: sidebarWidthProp, - defaultValue: sidebarDefaultWidth, - onChange: onSidebarWidthChange, - }); + const [internalCollapsed, setInternalCollapsed] = React.useState(defaultSidebarCollapsed); + const [internalWidth, setInternalWidth] = React.useState(sidebarDefaultWidth); + + const sidebarCollapsed = + sidebarCollapsedProp !== undefined ? sidebarCollapsedProp : internalCollapsed; + const setSidebarCollapsed = React.useCallback( + (next: boolean) => { + onSidebarCollapsedChange?.(next); + if (sidebarCollapsedProp === undefined) { + setInternalCollapsed(next); + } + }, + [onSidebarCollapsedChange, sidebarCollapsedProp], + ); + + const sidebarWidth = sidebarWidthProp !== undefined ? sidebarWidthProp : internalWidth; + const setSidebarWidth = React.useCallback( + (next: number) => { + const clamped = Math.max(sidebarMinWidth, Math.min(sidebarMaxWidth, next)); + onSidebarWidthChange?.(clamped); + if (sidebarWidthProp === undefined) { + setInternalWidth(clamped); + } + }, + [sidebarMaxWidth, sidebarMinWidth, onSidebarWidthChange, sidebarWidthProp], + ); const handleResize = React.useCallback( (offset: number) => { const nextWidth = resizeStartWidthRef.current + offset; - setSidebarWidth(Math.max(sidebarMinWidth, Math.min(sidebarMaxWidth, nextWidth))); + setSidebarWidth(nextWidth); }, - [setSidebarWidth, sidebarMaxWidth, sidebarMinWidth], + [setSidebarWidth], ); React.useEffect(() => { - if (!sidebar) { - setSidebarCollapsed(true); - } - }, [setSidebarCollapsed, sidebar]); + if (!sidebar) setSidebarCollapsed(true); + }, [sidebar, setSidebarCollapsed]); return ( {activityBar} - {sidebar && !sidebarCollapsed ? ( <> @@ -113,13 +93,7 @@ export function WorkspaceShell({ { resizeStartWidthRef.current = sidebarWidth; setIsDragging(true); @@ -129,10 +103,9 @@ export function WorkspaceShell({ /> ) : null} - -
-
{children}
+
+
{children}
{detailPanel} diff --git a/apps/storybook/.storybook/main.ts b/apps/storybook/.storybook/main.ts index 0ec1770c..9fa9b8fe 100644 --- a/apps/storybook/.storybook/main.ts +++ b/apps/storybook/.storybook/main.ts @@ -6,6 +6,7 @@ import tailwindcss from "@tailwindcss/vite"; const config: StorybookConfig = { stories: ["../src/**/*.mdx", "../src/**/*.stories.@(ts|tsx)"], addons: ["@storybook/addon-docs"], + staticDirs: ["../public"], framework: { name: "@storybook/react-vite", diff --git a/apps/storybook/public/fonts/JetBrainsMono-Bold.woff2 b/apps/storybook/public/fonts/JetBrainsMono-Bold.woff2 new file mode 100644 index 00000000..4917f434 Binary files /dev/null and b/apps/storybook/public/fonts/JetBrainsMono-Bold.woff2 differ diff --git a/apps/storybook/public/fonts/JetBrainsMono-Medium.woff2 b/apps/storybook/public/fonts/JetBrainsMono-Medium.woff2 new file mode 100644 index 00000000..669d04cd Binary files /dev/null and b/apps/storybook/public/fonts/JetBrainsMono-Medium.woff2 differ diff --git a/apps/storybook/public/fonts/JetBrainsMono-Regular.woff2 b/apps/storybook/public/fonts/JetBrainsMono-Regular.woff2 new file mode 100644 index 00000000..40da4276 Binary files /dev/null and b/apps/storybook/public/fonts/JetBrainsMono-Regular.woff2 differ diff --git a/apps/storybook/public/logos/airtable.svg b/apps/storybook/public/logos/airtable.svg new file mode 100644 index 00000000..adb9cf19 --- /dev/null +++ b/apps/storybook/public/logos/airtable.svg @@ -0,0 +1 @@ +Airtable \ No newline at end of file diff --git a/apps/storybook/public/logos/asana.svg b/apps/storybook/public/logos/asana.svg new file mode 100644 index 00000000..fcc1674f --- /dev/null +++ b/apps/storybook/public/logos/asana.svg @@ -0,0 +1 @@ +Asana \ No newline at end of file diff --git a/apps/storybook/public/logos/banana.svg b/apps/storybook/public/logos/banana.svg new file mode 100644 index 00000000..104d7dc2 --- /dev/null +++ b/apps/storybook/public/logos/banana.svg @@ -0,0 +1 @@ + diff --git a/apps/storybook/public/logos/brave.svg b/apps/storybook/public/logos/brave.svg new file mode 100644 index 00000000..5ca62ab4 --- /dev/null +++ b/apps/storybook/public/logos/brave.svg @@ -0,0 +1 @@ +Brave \ No newline at end of file diff --git a/apps/storybook/public/logos/claude.svg b/apps/storybook/public/logos/claude.svg new file mode 100644 index 00000000..3f22a8f8 --- /dev/null +++ b/apps/storybook/public/logos/claude.svg @@ -0,0 +1 @@ +Claude \ No newline at end of file diff --git a/apps/storybook/public/logos/clawhub.svg b/apps/storybook/public/logos/clawhub.svg new file mode 100644 index 00000000..81457db1 --- /dev/null +++ b/apps/storybook/public/logos/clawhub.svg @@ -0,0 +1 @@ + diff --git a/apps/storybook/public/logos/facebook.svg b/apps/storybook/public/logos/facebook.svg new file mode 100644 index 00000000..9c2d5a8a --- /dev/null +++ b/apps/storybook/public/logos/facebook.svg @@ -0,0 +1 @@ +Facebook \ No newline at end of file diff --git a/apps/storybook/public/logos/fish-audio.svg b/apps/storybook/public/logos/fish-audio.svg new file mode 100644 index 00000000..b805cdfc --- /dev/null +++ b/apps/storybook/public/logos/fish-audio.svg @@ -0,0 +1 @@ +Fish Audio \ No newline at end of file diff --git a/apps/storybook/public/logos/github.svg b/apps/storybook/public/logos/github.svg new file mode 100644 index 00000000..81920ca3 --- /dev/null +++ b/apps/storybook/public/logos/github.svg @@ -0,0 +1 @@ +GitHub \ No newline at end of file diff --git a/apps/storybook/public/logos/gitlab.svg b/apps/storybook/public/logos/gitlab.svg new file mode 100644 index 00000000..509fa7f4 --- /dev/null +++ b/apps/storybook/public/logos/gitlab.svg @@ -0,0 +1 @@ +GitLab \ No newline at end of file diff --git a/apps/storybook/public/logos/gmail.svg b/apps/storybook/public/logos/gmail.svg new file mode 100644 index 00000000..8a5e9b55 --- /dev/null +++ b/apps/storybook/public/logos/gmail.svg @@ -0,0 +1 @@ +Gmail \ No newline at end of file diff --git a/apps/storybook/public/logos/google-analytics.svg b/apps/storybook/public/logos/google-analytics.svg new file mode 100644 index 00000000..8b68c100 --- /dev/null +++ b/apps/storybook/public/logos/google-analytics.svg @@ -0,0 +1 @@ +Google Analytics \ No newline at end of file diff --git a/apps/storybook/public/logos/google-calendar.svg b/apps/storybook/public/logos/google-calendar.svg new file mode 100644 index 00000000..91f0e231 --- /dev/null +++ b/apps/storybook/public/logos/google-calendar.svg @@ -0,0 +1 @@ +Google Calendar \ No newline at end of file diff --git a/apps/storybook/public/logos/google-docs.svg b/apps/storybook/public/logos/google-docs.svg new file mode 100644 index 00000000..12077819 --- /dev/null +++ b/apps/storybook/public/logos/google-docs.svg @@ -0,0 +1 @@ +Google Docs \ No newline at end of file diff --git a/apps/storybook/public/logos/google-drive.svg b/apps/storybook/public/logos/google-drive.svg new file mode 100644 index 00000000..7263ef31 --- /dev/null +++ b/apps/storybook/public/logos/google-drive.svg @@ -0,0 +1 @@ +Google Drive \ No newline at end of file diff --git a/apps/storybook/public/logos/google-maps.svg b/apps/storybook/public/logos/google-maps.svg new file mode 100644 index 00000000..2c928cd7 --- /dev/null +++ b/apps/storybook/public/logos/google-maps.svg @@ -0,0 +1 @@ +Google Maps \ No newline at end of file diff --git a/apps/storybook/public/logos/google-sheets.svg b/apps/storybook/public/logos/google-sheets.svg new file mode 100644 index 00000000..3dce7189 --- /dev/null +++ b/apps/storybook/public/logos/google-sheets.svg @@ -0,0 +1 @@ +Google Sheets \ No newline at end of file diff --git a/apps/storybook/public/logos/hubspot.svg b/apps/storybook/public/logos/hubspot.svg new file mode 100644 index 00000000..1b9ddd2b --- /dev/null +++ b/apps/storybook/public/logos/hubspot.svg @@ -0,0 +1 @@ +HubSpot \ No newline at end of file diff --git a/apps/storybook/public/logos/instagram.svg b/apps/storybook/public/logos/instagram.svg new file mode 100644 index 00000000..6781d37b --- /dev/null +++ b/apps/storybook/public/logos/instagram.svg @@ -0,0 +1 @@ +Instagram \ No newline at end of file diff --git a/apps/storybook/public/logos/jina.svg b/apps/storybook/public/logos/jina.svg new file mode 100644 index 00000000..f91e5c99 --- /dev/null +++ b/apps/storybook/public/logos/jina.svg @@ -0,0 +1 @@ +Jina diff --git a/apps/storybook/public/logos/kling.svg b/apps/storybook/public/logos/kling.svg new file mode 100644 index 00000000..b5839b5e --- /dev/null +++ b/apps/storybook/public/logos/kling.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/storybook/public/logos/liblibtv.svg b/apps/storybook/public/logos/liblibtv.svg new file mode 100644 index 00000000..9263b5da --- /dev/null +++ b/apps/storybook/public/logos/liblibtv.svg @@ -0,0 +1 @@ + diff --git a/apps/storybook/public/logos/linear.svg b/apps/storybook/public/logos/linear.svg new file mode 100644 index 00000000..9ac44813 --- /dev/null +++ b/apps/storybook/public/logos/linear.svg @@ -0,0 +1 @@ +Linear \ No newline at end of file diff --git a/apps/storybook/public/logos/listenhub.svg b/apps/storybook/public/logos/listenhub.svg new file mode 100644 index 00000000..2b38d82a --- /dev/null +++ b/apps/storybook/public/logos/listenhub.svg @@ -0,0 +1 @@ +ListenHub \ No newline at end of file diff --git a/apps/storybook/public/logos/microsoft-teams.svg b/apps/storybook/public/logos/microsoft-teams.svg new file mode 100644 index 00000000..8ce9bdee --- /dev/null +++ b/apps/storybook/public/logos/microsoft-teams.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/apps/storybook/public/logos/nano-banana.svg b/apps/storybook/public/logos/nano-banana.svg new file mode 100644 index 00000000..e687e57d --- /dev/null +++ b/apps/storybook/public/logos/nano-banana.svg @@ -0,0 +1 @@ +NanoBanana \ No newline at end of file diff --git a/apps/storybook/public/logos/notion.svg b/apps/storybook/public/logos/notion.svg new file mode 100644 index 00000000..69afef32 --- /dev/null +++ b/apps/storybook/public/logos/notion.svg @@ -0,0 +1 @@ +Notion \ No newline at end of file diff --git a/apps/storybook/public/logos/outlook.svg b/apps/storybook/public/logos/outlook.svg new file mode 100644 index 00000000..248d970f --- /dev/null +++ b/apps/storybook/public/logos/outlook.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/apps/storybook/public/logos/perplexity.svg b/apps/storybook/public/logos/perplexity.svg new file mode 100644 index 00000000..42968a82 --- /dev/null +++ b/apps/storybook/public/logos/perplexity.svg @@ -0,0 +1 @@ +Perplexity \ No newline at end of file diff --git a/apps/storybook/public/logos/reddit.svg b/apps/storybook/public/logos/reddit.svg new file mode 100644 index 00000000..34c37c0f --- /dev/null +++ b/apps/storybook/public/logos/reddit.svg @@ -0,0 +1 @@ +Reddit \ No newline at end of file diff --git a/apps/storybook/public/logos/slack.svg b/apps/storybook/public/logos/slack.svg new file mode 100644 index 00000000..a7f57b52 --- /dev/null +++ b/apps/storybook/public/logos/slack.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/apps/storybook/public/logos/stripe.svg b/apps/storybook/public/logos/stripe.svg new file mode 100644 index 00000000..48272a2c --- /dev/null +++ b/apps/storybook/public/logos/stripe.svg @@ -0,0 +1 @@ +Stripe \ No newline at end of file diff --git a/apps/storybook/public/logos/trello.svg b/apps/storybook/public/logos/trello.svg new file mode 100644 index 00000000..d6d50d86 --- /dev/null +++ b/apps/storybook/public/logos/trello.svg @@ -0,0 +1 @@ +Trello \ No newline at end of file diff --git a/apps/storybook/public/logos/volcengine.svg b/apps/storybook/public/logos/volcengine.svg new file mode 100644 index 00000000..f7b62661 --- /dev/null +++ b/apps/storybook/public/logos/volcengine.svg @@ -0,0 +1 @@ +Volcengine diff --git a/apps/storybook/public/logos/x-twitter.svg b/apps/storybook/public/logos/x-twitter.svg new file mode 100644 index 00000000..449fc9f5 --- /dev/null +++ b/apps/storybook/public/logos/x-twitter.svg @@ -0,0 +1 @@ +X \ No newline at end of file diff --git a/apps/storybook/public/logos/youtube.svg b/apps/storybook/public/logos/youtube.svg new file mode 100644 index 00000000..9306b8e6 --- /dev/null +++ b/apps/storybook/public/logos/youtube.svg @@ -0,0 +1 @@ +YouTube \ No newline at end of file diff --git a/apps/storybook/public/logos/zoom.svg b/apps/storybook/public/logos/zoom.svg new file mode 100644 index 00000000..28828a36 --- /dev/null +++ b/apps/storybook/public/logos/zoom.svg @@ -0,0 +1 @@ +Zoom \ No newline at end of file diff --git a/apps/storybook/src/stories/activity-bar.stories.tsx b/apps/storybook/src/stories/activity-bar.stories.tsx index d8ac1324..846229ac 100644 --- a/apps/storybook/src/stories/activity-bar.stories.tsx +++ b/apps/storybook/src/stories/activity-bar.stories.tsx @@ -12,7 +12,7 @@ import { } from "@nexu-design/ui-web"; const meta = { - title: "Primitives/ActivityBar", + title: "Reserved/ActivityBar", component: ActivityBar, tags: ["autodocs"], } satisfies Meta; diff --git a/apps/storybook/src/stories/auth-shell.stories.tsx b/apps/storybook/src/stories/auth-shell.stories.tsx index 323cf3d1..ff3e1cd6 100644 --- a/apps/storybook/src/stories/auth-shell.stories.tsx +++ b/apps/storybook/src/stories/auth-shell.stories.tsx @@ -66,8 +66,8 @@ function StoryRail({ tone = "dark" }: { tone?: "light" | "dark" }) { href="https://github.com/refly-ai/nexu" className={ isDark - ? "inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/[0.04] px-4 py-3 text-sm text-white/82" - : "inline-flex items-center gap-2 rounded-full border border-border bg-background/80 px-4 py-3 text-sm text-text-secondary" + ? "inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/[0.04] px-4 py-3 text-lg text-white/82" + : "inline-flex items-center gap-2 rounded-full border border-border bg-background/80 px-4 py-3 text-lg text-text-secondary" } > GitHub @@ -101,8 +101,8 @@ function StoryRail({ tone = "dark" }: { tone?: "light" | "dark" }) {

{item.text} @@ -122,7 +122,7 @@ export const Login: Story = {

Create account

-

Choose how you want to continue.

+

Choose how you want to continue.

@@ -132,7 +132,7 @@ export const Login: Story = {
- or + or
@@ -161,7 +161,7 @@ export const LightLogin: Story = {

Create account

-

Choose how you want to continue.

+

Choose how you want to continue.

@@ -171,7 +171,7 @@ export const LightLogin: Story = {
- or + or
@@ -202,8 +202,8 @@ export const Onboarding: Story = {
-

Tell us how you work

-

+

Tell us how you work

+

Keep the shell stable while onboarding steps swap inside the content slot.

@@ -212,7 +212,7 @@ export const Onboarding: Story = {
), diff --git a/apps/storybook/src/stories/badge.stories.tsx b/apps/storybook/src/stories/badge.stories.tsx index 869fabfa..d84e800a 100644 --- a/apps/storybook/src/stories/badge.stories.tsx +++ b/apps/storybook/src/stories/badge.stories.tsx @@ -1,4 +1,5 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; +import { AlertCircle, CheckCircle2, CircleDot, Clock, Info } from "lucide-react"; import { Badge } from "@nexu-design/ui-web"; @@ -23,3 +24,25 @@ export const Variants: Story = {
), }; + +export const WithIcons: Story = { + render: () => ( +
+ + Connected + + + Pending + + + Failed + + + Info + + + Running + +
+ ), +}; diff --git a/apps/storybook/src/stories/button.stories.tsx b/apps/storybook/src/stories/button.stories.tsx index d85dcd89..a2027862 100644 --- a/apps/storybook/src/stories/button.stories.tsx +++ b/apps/storybook/src/stories/button.stories.tsx @@ -1,4 +1,5 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; +import { ArrowUpRight } from "lucide-react"; import { Button } from "@nexu-design/ui-web"; @@ -26,7 +27,9 @@ export const Variants: Story = { - +
), }; @@ -43,6 +46,67 @@ export const WithIcons: Story = { ), }; +export const Disabled: Story = { + render: () => ( +
+ + + + + +
+ ), +}; + +export const Sizes: Story = { + render: () => ( +
+ + + + + + + +
+ ), +}; + +export const SizesOutline: Story = { + name: "Sizes (Outline)", + render: () => ( +
+ + + + + +
+ ), +}; + export const Loading: Story = { args: { loading: true, diff --git a/apps/storybook/src/stories/card.stories.tsx b/apps/storybook/src/stories/card.stories.tsx index 63a04db4..8c900f9e 100644 --- a/apps/storybook/src/stories/card.stories.tsx +++ b/apps/storybook/src/stories/card.stories.tsx @@ -37,3 +37,34 @@ export const Default: Story = { ), }; + +export const Variants: Story = { + render: () => ( +
+ + + Default + Hover to see lift effect. + + + + + Interactive + Clickable card with bg + lift hover. + + + + + Static + No hover effect — for panels and feeds. + + + + + Muted + Subtle background with lift hover. + + +
+ ), +}; diff --git a/apps/storybook/src/stories/collapsible.stories.tsx b/apps/storybook/src/stories/collapsible.stories.tsx index 25d610be..87f040a6 100644 --- a/apps/storybook/src/stories/collapsible.stories.tsx +++ b/apps/storybook/src/stories/collapsible.stories.tsx @@ -14,16 +14,16 @@ export const Default: Story = { render: () => (
-
-
-
Escalation policy
-
Expand to review the fallback path.
+
+
+
Escalation policy
+
Expand to review the fallback path.
- + Details
- + If the owner does not respond, the issue escalates to the on-call lead. diff --git a/apps/storybook/src/stories/conversation-entities.stories.tsx b/apps/storybook/src/stories/conversation-entities.stories.tsx deleted file mode 100644 index 5302f615..00000000 --- a/apps/storybook/src/stories/conversation-entities.stories.tsx +++ /dev/null @@ -1,144 +0,0 @@ -import type { Meta, StoryObj } from "@storybook/react-vite"; -import { Bot, Layers3, Sparkles, User } from "lucide-react"; - -import { - Badge, - Button, - ConversationMessage, - EntityCard, - EntityCardContent, - EntityCardDescription, - EntityCardFooter, - EntityCardHeader, - EntityCardMedia, - EntityCardMeta, - EntityCardTitle, - StatsBar, - Stepper, - StepperItem, - StepperSeparator, -} from "@nexu-design/ui-web"; - -const meta = { - title: "Scenarios/Conversation Entities", - tags: ["autodocs"], -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -export const TeamStatusThread: Story = { - render: () => ( -
- } - meta="Just now" - actions={ - - } - > - I summarized the new support issues and drafted the follow-up actions. - - } meta="1 min ago" variant="user"> - Send the recap to the revenue team and flag anything urgent. - - Workflow completed successfully. -
- ), -}; - -export const LaunchStepperFlow: Story = { - render: () => ( -
- - - - - - - - - - - - -
- ), -}; - -export const WorkspaceEntityCards: Story = { - render: () => ( -
- - - - - -
- Content pipeline - - Automated drafting and approvals for weekly campaigns. - - 12 active flows -
-
- - Built for teams that need faster handoff from research to publish-ready content. - - - Healthy - -
- - - - - -
- Escalation workspace - - Routes urgent issues to the right owner automatically. - - 4 connected systems -
-
- - - -
-
- ), -}; - -export const OperationalStatsBar: Story = { - render: () => ( -
- -
- ), -}; diff --git a/apps/storybook/src/stories/conversation-message.stories.tsx b/apps/storybook/src/stories/conversation-message.stories.tsx index 93c483b5..1fb5d5f0 100644 --- a/apps/storybook/src/stories/conversation-message.stories.tsx +++ b/apps/storybook/src/stories/conversation-message.stories.tsx @@ -1,7 +1,7 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import { Bot, User } from "lucide-react"; +import { ArrowUpRight, Bot, User } from "lucide-react"; -import { Button, ConversationMessage, ConversationMessageStatusIcon } from "@nexu-design/ui-web"; +import { Avatar, AvatarFallback, Button, ConversationMessage } from "@nexu-design/ui-web"; const meta = { title: "Primitives/ConversationMessage", @@ -12,25 +12,37 @@ const meta = { export default meta; type Story = StoryObj; +const botAvatar = ( + + + + + +); + +const userAvatar = ( + + + + + +); + export const Default: Story = { render: () => (
- } meta="Just now"> + I summarized the latest support requests and drafted the follow-up plan. - } meta="1 min ago" variant="user"> + Please send the recap to the ops team and flag anything urgent. - - - Workflow completed successfully. - } - meta="Optional action" + avatar={botAvatar} + meta="Just now" actions={ } > @@ -39,3 +51,59 @@ export const Default: Story = {
), }; + +export const MultiTurnConversation: Story = { + render: () => ( +
+ + Help me draft a weekly update for the team. + + + Sure! Here's a draft based on this week's activity: + {"\n\n"} + 1. Closed 12 support tickets{"\n"} + 2. Shipped the new onboarding flow{"\n"} + 3. Resolved the billing sync issue + + + Looks good. Add a note about the upcoming maintenance window on Friday. + + + Done — I added a section about the Friday maintenance window. Want me to send it to Slack + now or save it as a draft? + +
+ ), +}; + +export const LongMessage: Story = { + render: () => ( +
+ + I reviewed the latest batch of support requests and here's what I found: + {"\n\n"} + There are three recurring themes this week. First, several customers are hitting a timeout + error when exporting large datasets — this appears to be related to the new pagination logic + we shipped on Tuesday. Second, the onboarding wizard is dropping users at step 3 because the + email verification link expires too quickly. Third, two enterprise accounts reported that + their SSO integration stopped working after we updated the SAML library. + {"\n\n"} + I've already created tickets for each issue and assigned them to the relevant teams. Want me + to escalate any of these? + +
+ ), +}; + +export const SystemMessage: Story = { + render: () => ( +
+ + This conversation was transferred from the support queue. Previous context has been loaded. + + + I see the previous context. How can I help you today? + +
+ ), +}; diff --git a/apps/storybook/src/stories/data-table.stories.tsx b/apps/storybook/src/stories/data-table.stories.tsx index 00de50d9..8b2ac0de 100644 --- a/apps/storybook/src/stories/data-table.stories.tsx +++ b/apps/storybook/src/stories/data-table.stories.tsx @@ -1,4 +1,5 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; +import { ArrowUpRight } from "lucide-react"; import { Badge, @@ -55,7 +56,7 @@ export const Default: Story = { Needs review - 2 min ago + 2 min ago Revenue agent @@ -63,14 +64,14 @@ export const Default: Story = { Ready - 12 min ago + 12 min ago Showing 2 of 18 approvals diff --git a/apps/storybook/src/stories/desktop-shell.stories.tsx b/apps/storybook/src/stories/desktop-shell.stories.tsx deleted file mode 100644 index 7bae5c42..00000000 --- a/apps/storybook/src/stories/desktop-shell.stories.tsx +++ /dev/null @@ -1,167 +0,0 @@ -import type { Meta, StoryObj } from "@storybook/react-vite"; -import { Bell, CreditCard, LayoutGrid, MessageSquare, Search, Settings } from "lucide-react"; - -import { - ActivityBar, - ActivityBarContent, - ActivityBarFooter, - ActivityBarHeader, - ActivityBarIndicator, - ActivityBarItem, - Badge, - Button, - InspectorPanel, - NavigationMenu, - NavigationMenuButton, - NavigationMenuItem, - NavigationMenuLabel, - NavigationMenuList, - ResizableHandle, - ResizablePanel, - Sidebar, - SidebarContent, - SidebarFooter, - SidebarHeader, - SplitView, -} from "@nexu-design/ui-web"; - -const meta = { - title: "Scenarios/Desktop Shell", - tags: ["autodocs"], -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -export const WorkspaceSplitView: Story = { - render: () => ( -
- - -
Navigation
-
- - -
- Main workspace content -
-
- - -
Details
-
-
-
- ), -}; - -export const WorkspaceSidebar: Story = { - render: () => ( -
- - -
Nexu workspace
-
Revenue operations
-
- - - Main - - - - - Dashboard - - - - - - Channels - - - - - - Approvals - - - - Workspace - - - - - Search - - - - - - Settings - - - - - - - 3 teammates online - -
-
- ), -}; - -export const CompactActivityBar: Story = { - render: () => ( -
- - - - - - - - - - - - - - - - - - - - - - -
- ), -}; - -export const ApprovalDetailPanel: Story = { - render: () => ( -
- -
-
- The workflow is waiting for finance approval because spend exceeds the daily threshold. -
-
- Current status - Pending review -
-
-
-
- ), -}; diff --git a/apps/storybook/src/stories/detail-panel.stories.tsx b/apps/storybook/src/stories/detail-panel.stories.tsx index 40470439..4595218b 100644 --- a/apps/storybook/src/stories/detail-panel.stories.tsx +++ b/apps/storybook/src/stories/detail-panel.stories.tsx @@ -1,6 +1,7 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; import { + Badge, DetailPanel, DetailPanelCloseButton, DetailPanelContent, @@ -20,7 +21,7 @@ type Story = StoryObj; export const Default: Story = { render: () => ( -
+
@@ -31,15 +32,13 @@ export const Default: Story = {
- -
+ +
The workflow is waiting for finance approval because spend exceeds the daily threshold.
-
+
Current status - - Pending review - + Pending review
diff --git a/apps/storybook/src/stories/entity-card.stories.tsx b/apps/storybook/src/stories/entity-card.stories.tsx index bc3ca312..a6fbfe50 100644 --- a/apps/storybook/src/stories/entity-card.stories.tsx +++ b/apps/storybook/src/stories/entity-card.stories.tsx @@ -1,8 +1,7 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import { Layers3, Sparkles } from "lucide-react"; +import { Server, Zap } from "lucide-react"; import { - Badge, Button, EntityCard, EntityCardContent, @@ -10,6 +9,8 @@ import { EntityCardFooter, EntityCardHeader, EntityCardMedia, + EntityCardMediaFallback, + EntityCardMediaImage, EntityCardMeta, EntityCardTitle, } from "@nexu-design/ui-web"; @@ -23,46 +24,154 @@ const meta = { export default meta; type Story = StoryObj; -export const Default: Story = { +export const SkillCards: Story = { render: () => ( -
+
- +
- Content pipeline - - Automated drafting and approvals for weekly campaigns. - - 12 active flows + Slack Relay + latest
- Built for teams that need a faster handoff from research to publish-ready content. + + Route messages, trigger workflows, and manage channels from Slack. + - - Healthy + + + +
+ + + + + + +
+ Git Bridge + v2.1.0 +
+
+ + + Sync repos, automate PR reviews, and manage branches across GitHub and GitLab. + + + + + +
+ + + + + + +
+ Notion Sync + v1.3.0 +
+
+ + + Sync pages, databases, and wikis between Notion and your workspace. + + + +
- + - +
- Escalation workspace - - Routes urgent issues to the right owner automatically. - - 4 connected systems + Gmail Agent + latest
- + + + Read, draft, and send emails. Manage labels and automate inbox workflows. + + + + + +
+ + {/* Icon fallback — no third-party logo */} + + + + + + + +
+ Gog CLI + latest +
+
+ + + Google Workspace CLI for Gmail, Calendar, Drive, Contacts, Sheets, and more. + + + + +
+ + + + + + + + +
+ Infra Monitor + v1.4.2 +
+
+ + + Real-time alerts and dashboards for cloud infrastructure health. + + + +
diff --git a/apps/storybook/src/stories/feedback.stories.tsx b/apps/storybook/src/stories/feedback.stories.tsx index b8bfd97f..2fd0d595 100644 --- a/apps/storybook/src/stories/feedback.stories.tsx +++ b/apps/storybook/src/stories/feedback.stories.tsx @@ -13,7 +13,7 @@ type Story = StoryObj; export const Default: Story = { render: () => (
-
+
Loading session list…
diff --git a/apps/storybook/src/stories/file-editor.stories.tsx b/apps/storybook/src/stories/file-editor.stories.tsx deleted file mode 100644 index 9e558ce6..00000000 --- a/apps/storybook/src/stories/file-editor.stories.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import type { Meta, StoryObj } from "@storybook/react-vite"; - -import { FileEditor } from "@nexu-design/ui-web"; - -const markdown = `# Universal agent v3 - -- filesystem-first memory -- skill-native execution -- proactive workflows`; - -const meta = { - title: "Patterns/FileEditor", - component: FileEditor, - tags: ["autodocs"], - args: { - filePath: "artifacts/prds/universal-agent-v3.md", - initialContent: markdown, - fileType: "markdown", - }, -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -export const MarkdownPreview: Story = { - render: () => ( -
- -
- ), -}; - -export const BinaryPlaceholder: Story = { - render: () => ( -
- -
- ), -}; - -export const Compact: Story = { - render: () => ( -
- -
- ), -}; diff --git a/apps/storybook/src/stories/file-tree.stories.tsx b/apps/storybook/src/stories/file-tree.stories.tsx deleted file mode 100644 index a51fa7e8..00000000 --- a/apps/storybook/src/stories/file-tree.stories.tsx +++ /dev/null @@ -1,103 +0,0 @@ -import type { Meta, StoryObj } from "@storybook/react-vite"; -import { Brain, Clock, Database, FileText, Sparkles, Terminal, Users, Wrench } from "lucide-react"; - -import { FileTree } from "@nexu-design/ui-web"; - -const tree = [ - { - name: ".soul", - type: "folder" as const, - icon: , - children: [ - { name: "identity.md", type: "file" as const }, - { name: "persona.md", type: "file" as const }, - ], - }, - { - name: "contacts", - type: "folder" as const, - icon: , - children: [{ name: "founders.md", type: "file" as const, modified: true }], - }, - { - name: "memory", - type: "folder" as const, - icon: , - children: [ - { - name: "decisions", - type: "folder" as const, - children: [ - { name: "2026-02-22-agent-native-team.md", type: "file" as const, active: true }, - ], - }, - ], - }, - { - name: "knowledge", - type: "folder" as const, - icon: , - children: [{ name: "architecture.md", type: "file" as const }], - }, - { - name: "artifacts", - type: "folder" as const, - icon: , - children: [{ name: "universal-agent-v3.md", type: "file" as const, isNew: true }], - }, - { - name: "sessions", - type: "folder" as const, - icon: , - children: [{ name: "thread.jsonl", type: "file" as const }], - }, - { - name: "automation", - type: "folder" as const, - icon: , - children: [{ name: "morning-digest.yaml", type: "file" as const }], - }, - { - name: "skills", - type: "folder" as const, - icon: , - children: [ - { - name: "memory-notes", - type: "folder" as const, - children: [{ name: "SKILL.md", type: "file" as const }], - }, - ], - }, -]; - -const meta = { - title: "Patterns/FileTree", - component: FileTree, - tags: ["autodocs"], - args: { - tree, - }, -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - render: () => ( -
- - 12 files - 1 modified · 1 new -
- } - /> -
- ), -}; diff --git a/apps/storybook/src/stories/filter-pills.stories.tsx b/apps/storybook/src/stories/filter-pills.stories.tsx new file mode 100644 index 00000000..d2d1a390 --- /dev/null +++ b/apps/storybook/src/stories/filter-pills.stories.tsx @@ -0,0 +1,75 @@ +import type { Meta, StoryObj } from "@storybook/react-vite"; +import { useState } from "react"; +import { Bot, Globe, Zap } from "lucide-react"; + +import { FilterPills, type FilterPillItem } from "@nexu-design/ui-web"; + +const categoryItems: FilterPillItem[] = [ + { id: "all", label: "All", count: 38 }, + { id: "social", label: "Social", count: 12, emoji: "💬" }, + { id: "coding", label: "Coding", count: 8, emoji: "🧑‍💻" }, + { id: "writing", label: "Writing", count: 6, emoji: "✍️" }, + { id: "research", label: "Research", count: 5 }, + { id: "data", label: "Data", count: 7 }, +]; + +const segmentItems: FilterPillItem[] = [ + { id: "marketplace", label: "Marketplace" }, + { id: "yours", label: "Yours" }, + { id: "running", label: "Running" }, +]; + +const iconItems: FilterPillItem[] = [ + { id: "agents", label: "Agents", icon: Bot }, + { id: "channels", label: "Channels", icon: Globe }, + { id: "skills", label: "Skills", icon: Zap }, +]; + +const meta = { + title: "Primitives/FilterPills", + component: FilterPills, + tags: ["autodocs"], + args: { + items: categoryItems, + value: "all", + onChange: () => {}, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +function PillDemo({ + items, + variant, + size, +}: { + items: FilterPillItem[]; + variant?: "pill" | "segment"; + size?: "sm" | "md"; +}) { + const [value, setValue] = useState(items[0].id); + return ( + + ); +} + +export const Default: Story = { + render: () => , +}; + +export const SegmentVariant: Story = { + render: () => , +}; + +export const MediumSize: Story = { + render: () => , +}; + +export const WithIcons: Story = { + render: () => , +}; + +export const SegmentMedium: Story = { + render: () => , +}; diff --git a/apps/storybook/src/stories/icons.stories.tsx b/apps/storybook/src/stories/icons.stories.tsx index c059295c..59daabc2 100644 --- a/apps/storybook/src/stories/icons.stories.tsx +++ b/apps/storybook/src/stories/icons.stories.tsx @@ -24,7 +24,7 @@ const providerIds = [ "siliconflow", ] as const; -const platformIds = ["slack", "feishu", "discord", "telegram"] as const; +const platformIds = ["slack", "feishu", "discord", "telegram", "wechat"] as const; const brandIds = ["nexu", "github"] as const; function IconGrid({ @@ -49,7 +49,7 @@ function IconCard({ label, children }: { label: string; children: ReactNode }) {
{children}
-
{label}
+
{label}
); diff --git a/apps/storybook/src/stories/input.stories.tsx b/apps/storybook/src/stories/input.stories.tsx index bfe0ac62..2e842c5c 100644 --- a/apps/storybook/src/stories/input.stories.tsx +++ b/apps/storybook/src/stories/input.stories.tsx @@ -1,6 +1,7 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; import { Input } from "@nexu-design/ui-web"; +import { Search } from "lucide-react"; const meta = { title: "Primitives/Input", @@ -18,7 +19,7 @@ export const Default: Story = {}; export const WithLeadingIcon: Story = { args: { - leadingIcon: , + leadingIcon: , placeholder: "Search skills", }, }; diff --git a/apps/storybook/src/stories/inspector-panel.stories.tsx b/apps/storybook/src/stories/inspector-panel.stories.tsx deleted file mode 100644 index 27e4c4b2..00000000 --- a/apps/storybook/src/stories/inspector-panel.stories.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import type { Meta, StoryObj } from "@storybook/react-vite"; -import { ArrowRight, ExternalLink, MessageSquare } from "lucide-react"; - -import { Button, FollowUpInput, InspectorPanel, PanelFooterActions } from "@nexu-design/ui-web"; - -const meta = { - title: "Patterns/InspectorPanel", - component: InspectorPanel, - tags: ["autodocs"], -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - args: { - title: "Campaign approval", - }, - render: () => ( -
- - -
- } - badges={ - <> - - Approval - - - Pending review - - - } - closeButtonProps={{ srLabel: "Close details" }} - footer={ -
- - - - - -
- } - > -
-
- Finance approval is required because today's projected spend exceeds the daily cap. -
-
- Suggested next step: reduce the audience size or split the rollout into two phases. -
-
- -
- ), -}; diff --git a/apps/storybook/src/stories/interactive-row.stories.tsx b/apps/storybook/src/stories/interactive-row.stories.tsx index 514b4878..fef606e6 100644 --- a/apps/storybook/src/stories/interactive-row.stories.tsx +++ b/apps/storybook/src/stories/interactive-row.stories.tsx @@ -1,14 +1,53 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import { Circle, MoreHorizontal, Sparkles } from "lucide-react"; +import { Check, MoreHorizontal, Sparkles } from "lucide-react"; import { Badge, + Button, InteractiveRow, InteractiveRowContent, InteractiveRowLeading, InteractiveRowTrailing, + StatusDot, } from "@nexu-design/ui-web"; +function GitHubLogo({ size = 18 }: { size?: number }) { + return ( + + + + ); +} + +function XiaohongshuLogo({ size = 18 }: { size?: number }) { + return ( + + + + ); +} + +function CalendarIcon({ size = 18 }: { size?: number }) { + return ( + + + + + + + + ); +} + const meta = { title: "Primitives/InteractiveRow", component: InteractiveRow, @@ -28,8 +67,8 @@ export const Default: Story = {
-
Summarize customer interviews
-
Content ops • Updated 6 minutes ago
+
Summarize customer interviews
+
Content ops • Updated 6 minutes ago
Ready @@ -37,15 +76,13 @@ export const Default: Story = { -
- -
+
-
+
Publish weekly automation recap
-
Marketing • Due in 30 minutes
+
Marketing • Due in 30 minutes
@@ -54,3 +91,61 @@ export const Default: Story = {
), }; + +export const WithButton: Story = { + render: () => ( +
+ + + + + + Daily check-in + + +100 credits + + + + + + + + + + + + + Star us + + + 300 credits + + + + + + + + + + + + + Post on 小红书 + + +200 credits + + + + + + +
+ ), +}; diff --git a/apps/storybook/src/stories/label.stories.tsx b/apps/storybook/src/stories/label.stories.tsx index a4907b0b..7cc54cee 100644 --- a/apps/storybook/src/stories/label.stories.tsx +++ b/apps/storybook/src/stories/label.stories.tsx @@ -1,20 +1,27 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import { Checkbox, Label } from "@nexu-design/ui-web"; +import { Input, Label } from "@nexu-design/ui-web"; const meta = { title: "Primitives/Label", + component: Label, tags: ["autodocs"], -} satisfies Meta; +} satisfies Meta; export default meta; type Story = StoryObj; export const Default: Story = { render: () => ( -
- - +
+
+ + +
+
+ + +
), }; diff --git a/apps/storybook/src/stories/layout-surfaces.stories.tsx b/apps/storybook/src/stories/layout-surfaces.stories.tsx deleted file mode 100644 index edb7a7b8..00000000 --- a/apps/storybook/src/stories/layout-surfaces.stories.tsx +++ /dev/null @@ -1,217 +0,0 @@ -import type { Meta, StoryObj } from "@storybook/react-vite"; -import { ChevronDown, ChevronsUpDown, Circle, MoreHorizontal, Sparkles } from "lucide-react"; - -import { - Accordion, - AccordionContent, - AccordionItem, - AccordionTrigger, - Badge, - Button, - Collapsible, - CollapsibleContent, - CollapsibleTrigger, - DataTable, - DataTableDescription, - DataTableFooter, - DataTableHeader, - DataTableTitle, - InteractiveRow, - InteractiveRowContent, - InteractiveRowLeading, - InteractiveRowTrailing, - PanelFooter, - PanelFooterActions, - PanelFooterMeta, - ScrollArea, - ScrollBar, - Table, - TableBody, - TableCell, - TableHead, - TableHeader, - TableRow, -} from "@nexu-design/ui-web"; - -const meta = { - title: "Scenarios/Layout Surfaces", - tags: ["autodocs"], -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -export const AuditLogSurface: Story = { - render: () => ( -
- -
- {Array.from({ length: 12 }, (_, index) => { - const label = `Audit log entry #${index + 1}`; - - return ( -
- {label} -
- ); - })} -
- -
-
- ), -}; - -export const WorkspaceAccordion: Story = { - render: () => ( -
- - - Connected sources - - Slack, Linear, and HubSpot are synced to this workspace. - - - - Running automations - - Eight automations are active with a 96% success rate this week. - - - -
- ), -}; - -export const EscalationPolicyDetails: Story = { - render: () => ( -
- -
-
-
Escalation policy
-
- Expand to review the configured fallback path. -
-
- - Details - - -
- - If the owner does not respond within 15 minutes, the issue escalates to the on-call lead. - -
-
- ), -}; - -export const PublishPanelFooter: Story = { - render: () => ( -
-
- Review the changes before publishing. -
- - Last saved 2 minutes ago - - - - - -
- ), -}; - -export const WorkflowQueueRows: Story = { - render: () => ( -
- - -
- -
-
- -
Summarize customer interviews
-
Content ops • Updated 6 minutes ago
-
- - Ready - -
- - -
- -
-
- -
- Publish weekly automation recap -
-
Marketing • Due in 30 minutes
-
- - - -
-
- ), -}; - -export const ApprovalQueueTable: Story = { - render: () => ( - - -
- Approval queue - - Representative dense table view for operator workflows. - -
- -
- - - - Owner - Task - Status - Updated - - - - - Ops copilot - Review refund exception - - Needs review - - 2 min ago - - - Revenue agent - Update invoice follow-up - - Ready - - 12 min ago - - -
- - Showing 2 of 18 approvals - - -
- ), -}; diff --git a/apps/storybook/src/stories/nav-item.stories.tsx b/apps/storybook/src/stories/nav-item.stories.tsx new file mode 100644 index 00000000..8dd426e1 --- /dev/null +++ b/apps/storybook/src/stories/nav-item.stories.tsx @@ -0,0 +1,66 @@ +import type { Meta, StoryObj } from "@storybook/react-vite"; +import { useState } from "react"; +import { Home, MessageSquare, Settings, Star, Users, Zap } from "lucide-react"; + +import { Badge, NavItem } from "@nexu-design/ui-web"; + +const meta = { + title: "Primitives/NavItem", + component: NavItem, + tags: ["autodocs"], +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +const navItems = [ + { id: "home", label: "Home", icon: Home }, + { id: "conversations", label: "Conversations", icon: MessageSquare, count: 3 }, + { id: "skills", label: "Skills", icon: Zap }, + { id: "team", label: "Team", icon: Users }, + { id: "rewards", label: "Rewards", icon: Star }, + { id: "settings", label: "Settings", icon: Settings }, +]; + +function NavDemo({ density }: { density?: "default" | "compact" }) { + const [active, setActive] = useState("home"); + return ( +
+
+ {navItems.map((item) => ( + } + label={item.label} + active={active === item.id} + density={density} + onClick={() => setActive(item.id)} + trailing={item.count ? {item.count} : undefined} + /> + ))} +
+
+ ); +} + +export const Default: Story = { + render: () => , +}; + +export const Compact: Story = { + render: () => , +}; + +export const SingleItem: Story = { + render: () => ( +
+ } label="Active item" active /> + } label="Inactive item" /> + } + label="With trailing" + trailing={5} + /> +
+ ), +}; diff --git a/apps/storybook/src/stories/page-shell.stories.tsx b/apps/storybook/src/stories/page-shell.stories.tsx index dd22bfc2..438b5830 100644 --- a/apps/storybook/src/stories/page-shell.stories.tsx +++ b/apps/storybook/src/stories/page-shell.stories.tsx @@ -1,6 +1,8 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import { Button, PageHeader, PageShell, SectionHeader } from "@nexu-design/ui-web"; +import { ArrowUpRight } from "lucide-react"; + +import { Button, PageHeader, PageShell } from "@nexu-design/ui-web"; const meta = { title: "Patterns/PageShell", @@ -17,30 +19,32 @@ export const Default: Story = { Open tokens} + actions={ + + } />
- +
+

Typography

+

+ Reference scales for headings, body copy, and captions. +

+
Use this shell to compose page-level docs and design system overview screens.
- - View tokens - - } - className="mb-4 border-b border-border-subtle pb-2" - /> +
+

Color

+ +
Surface
Text
diff --git a/apps/storybook/src/stories/popover.stories.tsx b/apps/storybook/src/stories/popover.stories.tsx index 5e37c95c..fd1a3fc5 100644 --- a/apps/storybook/src/stories/popover.stories.tsx +++ b/apps/storybook/src/stories/popover.stories.tsx @@ -17,7 +17,7 @@ export const Default: Story = { -
Connected workspace
+
Connected workspace

Switch active workspace or review status.

diff --git a/apps/storybook/src/stories/pricing-card.stories.tsx b/apps/storybook/src/stories/pricing-card.stories.tsx index 1bdec44f..13dde475 100644 --- a/apps/storybook/src/stories/pricing-card.stories.tsx +++ b/apps/storybook/src/stories/pricing-card.stories.tsx @@ -29,7 +29,7 @@ export const Comparison: Story = { features: [], }, render: () => ( -
+
(
-
Default
+
Default
-
Accent
+
Accent
-
Success
+
Success
-
Warning
+
Warning
diff --git a/apps/storybook/src/stories/prose.stories.tsx b/apps/storybook/src/stories/prose.stories.tsx new file mode 100644 index 00000000..b2508eda --- /dev/null +++ b/apps/storybook/src/stories/prose.stories.tsx @@ -0,0 +1,56 @@ +import type { Meta, StoryObj } from "@storybook/react-vite"; + +import { Prose } from "@nexu-design/ui-web"; + +const meta = { + title: "Primitives/Prose", + component: Prose, + tags: ["autodocs"], +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +const sampleHTML = ` +

Markdown Preview

+

This is a prose component that provides consistent styling for rendered markdown content, including emphasis, links, and inline code.

+

Lists

+
    +
  • First item
  • +
  • Second item with bold
  • +
  • Third item
  • +
+

Code Block

+
const greeting = 'Hello, world!'
+console.log(greeting)
+

Blockquote

+
+

Design is not just what it looks like. Design is how it works.

+
+

Table

+ + + + + + + + +
TokenValueUsage
surface-0#fafafaPage background
surface-1#ffffffCard background
+
+

End of preview.

+`; + +export const Default: Story = { + render: () => ( + // biome-ignore lint/security/noDangerouslySetInnerHtml: Prose stories intentionally render pre-defined HTML to demonstrate markdown styling + + ), +}; + +export const Compact: Story = { + render: () => ( + // biome-ignore lint/security/noDangerouslySetInnerHtml: Prose stories intentionally render pre-defined HTML to demonstrate markdown styling + + ), +}; diff --git a/apps/storybook/src/stories/scroll-area.stories.tsx b/apps/storybook/src/stories/scroll-area.stories.tsx index bb2eafcf..a81d16fc 100644 --- a/apps/storybook/src/stories/scroll-area.stories.tsx +++ b/apps/storybook/src/stories/scroll-area.stories.tsx @@ -1,6 +1,6 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import { ScrollArea, ScrollBar } from "@nexu-design/ui-web"; +import { ScrollArea } from "@nexu-design/ui-web"; const meta = { title: "Primitives/ScrollArea", @@ -28,7 +28,6 @@ export const Default: Story = { ); })}
-
), diff --git a/apps/storybook/src/stories/section-header.stories.tsx b/apps/storybook/src/stories/section-header.stories.tsx index d4a3d95e..e358547d 100644 --- a/apps/storybook/src/stories/section-header.stories.tsx +++ b/apps/storybook/src/stories/section-header.stories.tsx @@ -15,13 +15,28 @@ export default meta; type Story = StoryObj; export const Default: Story = { + args: { + title: "Color Tokens", + description: "Design tokens used across the system for consistent color usage.", + }, +}; + +export const WithAction: Story = { render: () => ( -
- Reconnect} - /> -
+ + View all + + } + /> ), }; + +export const TitleOnly: Story = { + args: { + title: "Typography", + }, +}; diff --git a/apps/storybook/src/stories/selection-navigation.stories.tsx b/apps/storybook/src/stories/selection-navigation.stories.tsx deleted file mode 100644 index 3e5678c3..00000000 --- a/apps/storybook/src/stories/selection-navigation.stories.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import type { Meta, StoryObj } from "@storybook/react-vite"; - -import { - Breadcrumb, - BreadcrumbItem, - BreadcrumbLink, - BreadcrumbList, - BreadcrumbPage, - BreadcrumbSeparator, - Combobox, - ComboboxContent, - ComboboxInput, - ComboboxItem, - ComboboxTrigger, - TagGroup, - TagGroupItem, -} from "@nexu-design/ui-web"; - -const meta = { - title: "Scenarios/Selection Navigation", - tags: ["autodocs"], -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -export const SkillPicker: Story = { - render: () => ( -
- - Select skill - - -
- - Alpha research - - - Beta ops - - - Gamma automation - -
-
-
-
- ), -}; - -export const WorkspaceBreadcrumbs: Story = { - render: () => ( - - - - Workspace - - - - Components - - - - Combobox - - - - ), -}; - -export const RoutingTags: Story = { - render: () => ( - - AI - Slack - Healthy - Needs review - - ), -}; diff --git a/apps/storybook/src/stories/settings-section.stories.tsx b/apps/storybook/src/stories/settings-section.stories.tsx deleted file mode 100644 index b57c82d3..00000000 --- a/apps/storybook/src/stories/settings-section.stories.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import type { Meta, StoryObj } from "@storybook/react-vite"; - -import { Button, FormField, Input, SettingsSection } from "@nexu-design/ui-web"; - -const meta = { - title: "Patterns/SettingsSection", - component: SettingsSection, - tags: ["autodocs"], - args: { - title: "Settings", - children: null, - }, -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - render: () => ( -
- Rotate} - > - - - - -
- ), -}; diff --git a/apps/storybook/src/stories/sidebar.stories.tsx b/apps/storybook/src/stories/sidebar.stories.tsx index 3093061f..03dd2941 100644 --- a/apps/storybook/src/stories/sidebar.stories.tsx +++ b/apps/storybook/src/stories/sidebar.stories.tsx @@ -1,12 +1,22 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import { Bell, LayoutGrid, MessageSquare, Search, Settings } from "lucide-react"; +import { + ChevronUp, + CircleHelp, + Globe, + Home, + PanelLeftClose, + Settings, + Sparkles, +} from "lucide-react"; import { + GitHubIcon, NavigationMenu, NavigationMenuButton, NavigationMenuItem, - NavigationMenuLabel, NavigationMenuList, + NexuLogoIcon, + PlatformLogo, Sidebar, SidebarContent, SidebarFooter, @@ -22,56 +32,152 @@ const meta = { export default meta; type Story = StoryObj; +const sessions = [ + { + id: "1", + title: "Customer onboarding flow", + platform: "slack" as const, + time: "2m ago", + }, + { + id: "2", + title: "Weekly report automation", + platform: "feishu" as const, + time: "15m ago", + }, + { + id: "3", + title: "Team standup bot", + platform: "discord" as const, + time: "1h ago", + }, + { + id: "4", + title: "Support ticket triage", + platform: "wechat" as const, + time: "3h ago", + }, +]; + export const Default: Story = { render: () => ( -
+
- -
Nexu workspace
-
Revenue operations
+ + +
+
Nexu
+
OpenClaw Desktop
+
+
- - - Main - - - - - Dashboard - - - - - - Channels - - - - - - Approvals - - - - Workspace - - - - - Search - - - - - - Settings - - - - + + +
+ + + + + + Home + + + + + + Skills + 3 + + + + + + Settings + + + + +
+ +
+
+ Conversations +
+
+ {sessions.map((s) => ( + + ))} +
+
- - 3 teammates online + +
+
+ + + + +
+ +
+ + +
diff --git a/apps/storybook/src/stories/skill-marketplace-card.stories.tsx b/apps/storybook/src/stories/skill-marketplace-card.stories.tsx new file mode 100644 index 00000000..f675cfd4 --- /dev/null +++ b/apps/storybook/src/stories/skill-marketplace-card.stories.tsx @@ -0,0 +1,154 @@ +import type { Meta, StoryObj } from "@storybook/react-vite"; +import { Bot, Code, FileSearch, Globe, MessageSquare, PenTool } from "lucide-react"; + +import { Button, SkillMarketplaceCard } from "@nexu-design/ui-web"; + +const meta = { + title: "Patterns/SkillMarketplaceCard", + component: SkillMarketplaceCard, + tags: ["autodocs"], + args: { + name: "Web Search", + description: "Search the web for real-time information and return summarized results.", + icon: Globe, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + categoryLabel: "Research", + footer: ( + + ), + }, + decorators: [ + (Story) => ( +
+ +
+ ), + ], +}; + +export const WithLogo: Story = { + args: { + name: "GitHub Copilot", + description: + "AI pair programming that helps you write code faster with contextual suggestions.", + categoryLabel: "Coding", + logo: "https://github.githubassets.com/favicons/favicon-dark.svg", + icon: Code, + footer: ( + + ), + }, + decorators: [ + (Story) => ( +
+ +
+ ), + ], +}; + +export const Dimmed: Story = { + args: { + name: "Content Writer", + description: "Generate high-quality articles, blog posts, and marketing copy.", + categoryLabel: "Writing", + icon: PenTool, + dimmed: true, + footer: ( + + ), + }, + decorators: [ + (Story) => ( +
+ +
+ ), + ], +}; + +export const Grid: Story = { + render: () => ( +
+ + Install + + } + /> + + Install + + } + /> + + Install + + } + /> + + Install + + } + /> + + Installed + + } + /> + + Install + + } + /> +
+ ), +}; diff --git a/apps/storybook/src/stories/sonner.stories.tsx b/apps/storybook/src/stories/sonner.stories.tsx index fe429fa0..f568b760 100644 --- a/apps/storybook/src/stories/sonner.stories.tsx +++ b/apps/storybook/src/stories/sonner.stories.tsx @@ -15,7 +15,7 @@ type Story = StoryObj; export const Default: Story = { render: () => (
- +