@ciridae/ui 1.0.3 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chat/conversation-source.d.ts +15 -1
- package/dist/chat/describe-tool-call.d.ts +3 -3
- package/dist/chat/describe-tool-call.js +3 -3
- package/dist/chat/message-text.js +2 -2
- package/dist/ciridae.css +16 -4
- package/dist/components/activity-feed.d.ts +1 -0
- package/dist/components/activity-feed.js +1 -0
- package/dist/components/app-frame.d.ts +13 -1
- package/dist/components/app-frame.js +45 -3
- package/dist/components/app-navigation.d.ts +28 -1
- package/dist/components/app-navigation.js +105 -14
- package/dist/components/badge.d.ts +29 -4
- package/dist/components/badge.js +134 -15
- package/dist/components/bulk-action-bar.js +3 -3
- package/dist/components/button.d.ts +10 -3
- package/dist/components/button.js +77 -44
- package/dist/components/calendar.js +44 -17
- package/dist/components/centered-page.js +2 -2
- package/dist/components/chart.d.ts +1 -1
- package/dist/components/chart.js +16 -11
- package/dist/components/chat-client-source.js +47 -6
- package/dist/components/chat-sheet-controller.d.ts +1 -0
- package/dist/components/chat-sheet-controller.js +1 -0
- package/dist/components/chat-sheet.d.ts +10 -2
- package/dist/components/chat-sheet.js +76 -39
- package/dist/components/chat.d.ts +90 -14
- package/dist/components/chat.js +402 -101
- package/dist/components/checkbox-item.d.ts +5 -2
- package/dist/components/checkbox-item.js +9 -5
- package/dist/components/chip.d.ts +5 -0
- package/dist/components/chip.js +27 -1
- package/dist/components/column-settings.js +1 -1
- package/dist/components/command-menu.d.ts +5 -1
- package/dist/components/command-menu.js +98 -32
- package/dist/components/command-palette.d.ts +6 -2
- package/dist/components/command-palette.js +20 -10
- package/dist/components/data-display.d.ts +35 -4
- package/dist/components/data-display.js +138 -19
- package/dist/components/data-grid.d.ts +14 -2
- package/dist/components/data-grid.js +61 -26
- package/dist/components/date-range-picker.js +9 -20
- package/dist/components/drawer.d.ts +5 -0
- package/dist/components/drawer.js +91 -54
- package/dist/components/editable-field.d.ts +12 -3
- package/dist/components/editable-field.js +57 -27
- package/dist/components/email-input.js +6 -5
- package/dist/components/environment-frame.d.ts +1 -1
- package/dist/components/environment-frame.js +37 -6
- package/dist/components/filter-bar.js +39 -28
- package/dist/components/form-controls.d.ts +9 -0
- package/dist/components/form-controls.js +46 -22
- package/dist/components/form-layout.d.ts +7 -3
- package/dist/components/form-layout.js +68 -23
- package/dist/components/glass-menu.d.ts +3 -0
- package/dist/components/glass-menu.js +63 -0
- package/dist/components/gradient-edge.d.ts +9 -0
- package/dist/components/gradient-edge.js +31 -0
- package/dist/components/input.d.ts +4 -2
- package/dist/components/input.js +26 -15
- package/dist/components/keycap.d.ts +9 -5
- package/dist/components/keycap.js +57 -19
- package/dist/components/lightbox.js +2 -1
- package/dist/components/locale.d.ts +10 -0
- package/dist/components/locale.js +18 -2
- package/dist/components/log-entry.d.ts +40 -0
- package/dist/components/log-entry.js +146 -0
- package/dist/components/mention-input.js +4 -3
- package/dist/components/menu.d.ts +6 -2
- package/dist/components/menu.js +34 -37
- package/dist/components/meter.d.ts +9 -1
- package/dist/components/meter.js +64 -6
- package/dist/components/navigation.d.ts +21 -4
- package/dist/components/navigation.js +84 -19
- package/dist/components/notification-tray.js +5 -4
- package/dist/components/overlays.d.ts +3 -1
- package/dist/components/overlays.js +69 -40
- package/dist/components/page-frame.js +1 -1
- package/dist/components/panel.d.ts +1 -1
- package/dist/components/panel.js +18 -12
- package/dist/components/radio-card.js +10 -9
- package/dist/components/record.d.ts +25 -1
- package/dist/components/record.js +147 -18
- package/dist/components/segmented-bar.d.ts +3 -1
- package/dist/components/segmented-bar.js +16 -6
- package/dist/components/select.d.ts +5 -2
- package/dist/components/select.js +103 -127
- package/dist/components/shell-skeleton.js +1 -1
- package/dist/components/shell.d.ts +2 -2
- package/dist/components/shell.js +43 -45
- package/dist/components/sidebar.d.ts +7 -2
- package/dist/components/sidebar.js +55 -27
- package/dist/components/single-line-text.d.ts +2 -0
- package/dist/components/single-line-text.js +16 -0
- package/dist/components/site-banner.d.ts +5 -2
- package/dist/components/site-banner.js +17 -6
- package/dist/components/skeleton.d.ts +7 -6
- package/dist/components/skeleton.js +28 -16
- package/dist/components/skeletons.d.ts +12 -0
- package/dist/components/skeletons.js +71 -8
- package/dist/components/stages.d.ts +78 -6
- package/dist/components/stages.js +512 -156
- package/dist/components/stat-card.d.ts +10 -2
- package/dist/components/stat-card.js +92 -37
- package/dist/components/status-banner.js +1 -1
- package/dist/components/tabs.js +26 -13
- package/dist/components/theme-toggle.d.ts +4 -0
- package/dist/components/theme-toggle.js +67 -0
- package/dist/components/toast.d.ts +9 -2
- package/dist/components/toast.js +78 -29
- package/dist/preset.js +289 -33
- package/dist/styled-system/styles.css +3115 -1044
- package/dist/styled-system/tokens/index.mjs +598 -22
- package/dist/styled-system/tokens/tokens.d.ts +11 -11
- package/dist/styled-system/types/prop-type.d.ts +2 -2
- package/dist/styles.css +16 -4
- package/dist/theme/color.d.ts +3 -0
- package/dist/theme/color.js +5 -0
- package/dist/theme/derive.d.ts +9 -2
- package/dist/theme/derive.js +72 -12
- package/dist/theme/palettes.d.ts +49 -6
- package/dist/theme/palettes.js +84 -15
- package/dist/theme/tokens.d.ts +3 -3
- package/dist/theme/tokens.js +107 -32
- package/package.json +9 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ChatAttachment } from "../components/chat.js";
|
|
1
|
+
import type { ChatAttachment, ChatSource, ChatStep } from "../components/chat.js";
|
|
2
2
|
import type { ResolveFileUrl } from "../components/resolved-file-url.js";
|
|
3
3
|
import type { ReactNode } from "react";
|
|
4
4
|
/**
|
|
@@ -27,6 +27,20 @@ export type ChatConversationItem = {
|
|
|
27
27
|
message?: string;
|
|
28
28
|
retry: () => void;
|
|
29
29
|
};
|
|
30
|
+
/** Sources the reply cites beyond its Markdown's, such as the values it read, as cards. */
|
|
31
|
+
sources?: readonly ChatSource[];
|
|
32
|
+
/** Buttons under the reply for what it suggests doing on the page. */
|
|
33
|
+
suggestedActions?: ReactNode;
|
|
34
|
+
/** A closing line under the reply, such as what it read. */
|
|
35
|
+
footnote?: ReactNode;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* A step the agent took on its way to a reply. The thread folds a run of them into one line,
|
|
39
|
+
* and draws the newest one of the running turn as what the agent is doing.
|
|
40
|
+
*/
|
|
41
|
+
| {
|
|
42
|
+
kind: "step";
|
|
43
|
+
step: Omit<ChatStep, "id">;
|
|
30
44
|
}
|
|
31
45
|
/** A line about the conversation, such as how an earlier turn ended. */
|
|
32
46
|
| {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* What a tool call
|
|
3
|
-
*
|
|
2
|
+
* What a tool call does, read from a tool name that starts with its verb: `search_invoices` is
|
|
3
|
+
* "Search invoices", and a one-word noun like `webfetch` "Use webfetch".
|
|
4
4
|
*/
|
|
5
|
-
export declare function
|
|
5
|
+
export declare function describeToolCall(toolName: string): string;
|
|
@@ -12,10 +12,10 @@ const singleWordVerbs = new Set([
|
|
|
12
12
|
"write",
|
|
13
13
|
]);
|
|
14
14
|
/**
|
|
15
|
-
* What a tool call
|
|
16
|
-
*
|
|
15
|
+
* What a tool call does, read from a tool name that starts with its verb: `search_invoices` is
|
|
16
|
+
* "Search invoices", and a one-word noun like `webfetch` "Use webfetch".
|
|
17
17
|
*/
|
|
18
|
-
export function
|
|
18
|
+
export function describeToolCall(toolName) {
|
|
19
19
|
const [verb = toolName, ...rest] = splitIdentifierWords(toolName).map((word) => word.toLowerCase());
|
|
20
20
|
const object = rest.join(" ");
|
|
21
21
|
if (!object && !singleWordVerbs.has(verb))
|
|
@@ -6,10 +6,10 @@ import { parseMentions } from "./contract.js";
|
|
|
6
6
|
/* TanStack owns Markdown parsing/rendering; the adapter adds our mention chips and bare URLs.
|
|
7
7
|
* Raw HTML stays text. Source definitions are lifted out separately for the reply's source row.
|
|
8
8
|
*/
|
|
9
|
-
// Sized by the bubble it sits in.
|
|
9
|
+
// Sized by the bubble it sits in; its paragraphs, lists and tables 12px apart.
|
|
10
10
|
const text = css({
|
|
11
11
|
display: "grid",
|
|
12
|
-
gap: "
|
|
12
|
+
gap: "layout.contentGap",
|
|
13
13
|
minWidth: "0",
|
|
14
14
|
overflowWrap: "anywhere",
|
|
15
15
|
"& :is(p, ul, ol, pre, table, blockquote, h1, h2, h3, h4, h5, h6, hr)": { margin: "none" },
|
package/dist/ciridae.css
CHANGED
|
@@ -37,11 +37,18 @@
|
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
/* [data-edge] paints a hairline inside the element's own box
|
|
41
|
-
|
|
40
|
+
/* [data-edge] paints a hairline inside the element's own box. The design counts that stroke in
|
|
41
|
+
the layout, so an edged element keeps a 1px transparent border under the ring. A border the
|
|
42
|
+
element sets itself wins: a native button resetting the browser's sets `1px solid transparent`,
|
|
43
|
+
and a face the design lays its edge over, such as a text button, sets `border: 0`. */
|
|
42
44
|
[data-ciridae-ui][data-edge] {
|
|
43
45
|
position: relative;
|
|
44
46
|
}
|
|
47
|
+
@layer base {
|
|
48
|
+
:where([data-ciridae-ui][data-edge]) {
|
|
49
|
+
border: 1px solid transparent;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
45
52
|
[data-ciridae-ui][data-edge]::before {
|
|
46
53
|
content: "";
|
|
47
54
|
position: absolute;
|
|
@@ -64,8 +71,13 @@
|
|
|
64
71
|
[data-ciridae-ui][data-edge="control"]::before {
|
|
65
72
|
background: var(--colors-border\.control);
|
|
66
73
|
}
|
|
67
|
-
|
|
68
|
-
|
|
74
|
+
/* data-edge="gradient" draws the gradient its element sets in --edge, such as
|
|
75
|
+
token(gradients.border.infoGlass). In the base layer, so a component's own
|
|
76
|
+
::before background still wins. */
|
|
77
|
+
@layer base {
|
|
78
|
+
:where([data-ciridae-ui][data-edge="gradient"])::before {
|
|
79
|
+
background: var(--edge, none);
|
|
80
|
+
}
|
|
69
81
|
}
|
|
70
82
|
/* An open trigger draws its own border in the text color, so the ring steps aside. */
|
|
71
83
|
[data-ciridae-ui][data-edge][data-popup-open]::before {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type ReactNode, type RefObject } from "react";
|
|
2
2
|
import { type CiridaeLabels } from "./locale.js";
|
|
3
3
|
import { type UserChipProps } from "./user-chip.js";
|
|
4
|
+
export { LogActor, LogEntry, LogGroupHeader, type LogActorProps, type LogEntryProps, type LogGroupHeaderProps, } from "./log-entry.js";
|
|
4
5
|
export type ActivityFeedActor = Pick<UserChipProps, "name" | "kind" | "src" | "icon" | "profile">;
|
|
5
6
|
export type ActivityFeedUpdate = {
|
|
6
7
|
kind: "update";
|
|
@@ -14,6 +14,7 @@ import { Toolbar } from "./layout.js";
|
|
|
14
14
|
import { useCiridaeLocale } from "./locale.js";
|
|
15
15
|
import { MentionChip, openMentionPicker, readMentions, } from "./mention-input.js";
|
|
16
16
|
import { UserName } from "./user-chip.js";
|
|
17
|
+
export { LogActor, LogEntry, LogGroupHeader, } from "./log-entry.js";
|
|
17
18
|
const FILTERS = [
|
|
18
19
|
{ value: "all", label: "all", empty: "noActivity" },
|
|
19
20
|
{ value: "comments", label: "comments", empty: "noComments" },
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { type PaletteName } from "../theme/index.js";
|
|
2
2
|
import { type AppNavigationProps } from "./app-navigation.js";
|
|
3
|
+
import { type HotkeyChord } from "./hotkeys.js";
|
|
3
4
|
import { type MenuItem } from "./menu.js";
|
|
4
|
-
import type { ReactNode } from "react";
|
|
5
|
+
import type { ComponentProps, ReactNode } from "react";
|
|
5
6
|
/** The palette the reader shows, which the host keeps, such as in storage, for `ThemeProvider`. */
|
|
6
7
|
export type PaletteChoice = {
|
|
7
8
|
/** `undefined` is the stylesheet's own palette, `ciridae`. */
|
|
@@ -31,6 +32,17 @@ export type AccountMenuProps = {
|
|
|
31
32
|
};
|
|
32
33
|
/** The signed-in account's menu: the app's entries, the color theme and palette, then sign out. */
|
|
33
34
|
export declare function AccountMenu({ avatar, name, detail, items, palette, signOut, compact, }: AccountMenuProps): import("react").JSX.Element;
|
|
35
|
+
export type AssistantLauncherProps = Omit<ComponentProps<"button">, "children"> & {
|
|
36
|
+
/** Such as "Ask Ciridae"; the rail's button is named by it. */
|
|
37
|
+
label: string;
|
|
38
|
+
icon: ReactNode;
|
|
39
|
+
/** The hotkey that opens the assistant, shown at the end; the host binds it. */
|
|
40
|
+
hotkey?: HotkeyChord;
|
|
41
|
+
/** The icon alone, round, for the rail. */
|
|
42
|
+
compact?: boolean;
|
|
43
|
+
};
|
|
44
|
+
/** Opens the app's assistant from the sidebar's foot: a raised pill, or a round icon in the rail. */
|
|
45
|
+
export declare function AssistantLauncher({ label, icon: launcherIcon, hotkey, compact, className, ...props }: AssistantLauncherProps): import("react").JSX.Element;
|
|
34
46
|
/** A footer control drawn for every width: a sidebar row, a rail icon and a phone-header icon. */
|
|
35
47
|
export type AppFrameItem = {
|
|
36
48
|
id: string;
|
|
@@ -1,18 +1,20 @@
|
|
|
1
|
-
import { jsx as _jsx,
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
/*
|
|
3
3
|
* The app's frame, composed from the shell's pieces: the sidebar that folds to a rail and then
|
|
4
4
|
* into the phone menu, its footer rows and account menu drawn for each width, and, given a
|
|
5
5
|
* `title`, the main panel's header too. `AccountMenu` is the signed-in account's menu: the app's
|
|
6
6
|
* own entries, the color theme and palette, and sign out. Auth stays with the host, which passes
|
|
7
|
-
* sign-out as an action.
|
|
7
|
+
* sign-out as an action. `AssistantLauncher` opens the app's assistant from the sidebar's foot.
|
|
8
8
|
*/
|
|
9
9
|
import { ArrowClockwiseIcon, CaretUpIcon, CircleHalfIcon, PaletteIcon, PulseIcon, SignOutIcon, } from "@phosphor-icons/react";
|
|
10
|
+
import { css, cx } from "#primitives/css";
|
|
10
11
|
import { PALETTE_NAMES } from "../theme/index.js";
|
|
11
12
|
import { AppNavigation } from "./app-navigation.js";
|
|
12
13
|
import { Button, IconButton } from "./button.js";
|
|
13
14
|
import { useColorTheme } from "./color-theme.js";
|
|
14
15
|
import { icon } from "./icon.js";
|
|
15
16
|
import { KeyboardShortcuts } from "./keyboard-shortcuts.js";
|
|
17
|
+
import { ShortcutHint } from "./keycap.js";
|
|
16
18
|
import { Link } from "./link.js";
|
|
17
19
|
import { useCiridaeLocale } from "./locale.js";
|
|
18
20
|
import { Menu } from "./menu.js";
|
|
@@ -58,7 +60,13 @@ function paletteMenuItem({ value, onValueChange }, labels) {
|
|
|
58
60
|
export function AccountMenu({ avatar, name, detail, items = [], palette, signOut, compact = false, }) {
|
|
59
61
|
const { labels } = useCiridaeLocale();
|
|
60
62
|
const colorThemeItem = useColorThemeMenuItem();
|
|
61
|
-
return (_jsx(Menu
|
|
63
|
+
return (_jsx(Menu
|
|
64
|
+
// In the sidebar the menu is as wide as the account row it opens from, as the design draws
|
|
65
|
+
// it, so it covers whatever sits above that row; the avatar alone opens a menu of its own width.
|
|
66
|
+
, {
|
|
67
|
+
// In the sidebar the menu is as wide as the account row it opens from, as the design draws
|
|
68
|
+
// it, so it covers whatever sits above that row; the avatar alone opens a menu of its own width.
|
|
69
|
+
matchTriggerWidth: !compact, trigger: _jsx(AppAccountButton, { "aria-label": labels.accountMenu.menu, avatar: avatar, compact: compact, detail: detail, name: name, trailing: _jsx(CaretUpIcon, { className: icon({ size: "small" }) }) }), items: [
|
|
62
70
|
...items,
|
|
63
71
|
colorThemeItem,
|
|
64
72
|
...(palette ? [paletteMenuItem(palette, labels)] : []),
|
|
@@ -75,6 +83,40 @@ export function AccountMenu({ avatar, name, detail, items = [], palette, signOut
|
|
|
75
83
|
: []),
|
|
76
84
|
] }));
|
|
77
85
|
}
|
|
86
|
+
// A raised pill on the canvas; in the rail, a round button with the same face.
|
|
87
|
+
const assistantLauncher = css({
|
|
88
|
+
display: "flex",
|
|
89
|
+
alignItems: "center",
|
|
90
|
+
gap: "control.iconGap",
|
|
91
|
+
width: "100%",
|
|
92
|
+
height: "control.default",
|
|
93
|
+
paddingInlineStart: "control.compactInline",
|
|
94
|
+
paddingInlineEnd: "control.launcherInlineEnd",
|
|
95
|
+
border: "1px solid token(colors.border.canvas)",
|
|
96
|
+
borderRadius: "shell",
|
|
97
|
+
background: "token(gradients.raised)",
|
|
98
|
+
color: "fg.primary",
|
|
99
|
+
font: "inherit",
|
|
100
|
+
fontWeight: "medium",
|
|
101
|
+
letterSpacing: "compact",
|
|
102
|
+
lineHeight: "tight",
|
|
103
|
+
cursor: "pointer",
|
|
104
|
+
_hover: { background: "bg.raisedHover" },
|
|
105
|
+
"&[aria-pressed=true]": { background: "bg.field" },
|
|
106
|
+
_focusVisible: { focusRing: "outer" },
|
|
107
|
+
"& > [data-label]": { flex: "1", minWidth: "0", textAlign: "start" },
|
|
108
|
+
"&[data-compact]": {
|
|
109
|
+
justifyContent: "center",
|
|
110
|
+
width: "control.compact",
|
|
111
|
+
height: "control.compact",
|
|
112
|
+
padding: "none",
|
|
113
|
+
borderRadius: "round",
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
/** Opens the app's assistant from the sidebar's foot: a raised pill, or a round icon in the rail. */
|
|
117
|
+
export function AssistantLauncher({ label, icon: launcherIcon, hotkey, compact = false, className, ...props }) {
|
|
118
|
+
return (_jsxs("button", { type: "button", "aria-label": compact ? label : undefined, ...props, className: cx(assistantLauncher, className), "data-ciridae-ui": "", "data-compact": compact || undefined, children: [launcherIcon, !compact && (_jsxs(_Fragment, { children: [_jsx("span", { "data-label": "", children: label }), hotkey && _jsx(ShortcutHint, { boxed: true, hotkey: hotkey })] }))] }));
|
|
119
|
+
}
|
|
78
120
|
/** The app's frame: the sidebar for every width, its footer and account, and the page. */
|
|
79
121
|
export function AppFrame({ title, actions, sidebarTop, sidebarRail, navigation, sidebar, sidebarFooter, items = [], footnote, buildSha = null, statusUrl, railActions, compactActions, account, overlays, children, }) {
|
|
80
122
|
const { labels } = useCiridaeLocale();
|
|
@@ -47,6 +47,10 @@ export type AppNavigationProps = Omit<ComponentProps<"aside">, "children"> & {
|
|
|
47
47
|
footer?: ReactNode;
|
|
48
48
|
/** Unfold every section while the sidebar has room for all of its rows. Defaults to true. */
|
|
49
49
|
autoUnfold?: boolean;
|
|
50
|
+
/** Show the rail at any width, as the reader chose; the host keeps the choice. */
|
|
51
|
+
collapsed?: boolean;
|
|
52
|
+
/** With `collapsed`, a round button at the rail's foot that unfolds it. */
|
|
53
|
+
onExpand?: () => void;
|
|
50
54
|
labels?: Partial<{
|
|
51
55
|
more: string;
|
|
52
56
|
less: string;
|
|
@@ -63,4 +67,27 @@ export declare function useSidebarRoom(foldedRows: number): {
|
|
|
63
67
|
footerRef: import("react").RefObject<HTMLDivElement | null>;
|
|
64
68
|
roomy: boolean;
|
|
65
69
|
};
|
|
66
|
-
export declare function AppNavigation({ sections, activeId: ownActiveId, renderLink, header, railHeader, footer, autoUnfold, labels: ownLabels, ...props }: AppNavigationProps): import("react").JSX.Element;
|
|
70
|
+
export declare function AppNavigation({ sections, activeId: ownActiveId, renderLink, header, railHeader, footer, autoUnfold, collapsed, onExpand, labels: ownLabels, ...props }: AppNavigationProps): import("react").JSX.Element;
|
|
71
|
+
export type Workspace = {
|
|
72
|
+
id: string;
|
|
73
|
+
name: string;
|
|
74
|
+
};
|
|
75
|
+
export type WorkspaceMarkProps = {
|
|
76
|
+
name: string;
|
|
77
|
+
/** The workspace the reader is in; the others are drawn quieter. */
|
|
78
|
+
current?: boolean;
|
|
79
|
+
/** `small` for a row in the workspace menu. */
|
|
80
|
+
size?: "regular" | "small";
|
|
81
|
+
className?: string;
|
|
82
|
+
};
|
|
83
|
+
/** A workspace's mark: its name's first letter on a square tile. */
|
|
84
|
+
export declare function WorkspaceMark({ name, current, size, className, }: WorkspaceMarkProps): import("react").JSX.Element;
|
|
85
|
+
export type WorkspaceSwitcherProps = {
|
|
86
|
+
workspaces: readonly Workspace[];
|
|
87
|
+
currentId: string;
|
|
88
|
+
onSelect: (id: string) => void;
|
|
89
|
+
/** The mark alone, for the rail. */
|
|
90
|
+
compact?: boolean;
|
|
91
|
+
};
|
|
92
|
+
/** The current workspace, opening a menu of every workspace the reader belongs to, current checked. */
|
|
93
|
+
export declare function WorkspaceSwitcher({ workspaces, currentId, onSelect, compact, }: WorkspaceSwitcherProps): import("react").JSX.Element;
|
|
@@ -1,19 +1,31 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
/*
|
|
3
3
|
* The app's sidebar from data. `AppNavigation` draws its sections twice from the same list: as the
|
|
4
|
-
* expanded sidebar, and as the icon rail the shell shows under 1000px
|
|
5
|
-
* `maxVisible` behind a More row, and unfolds on its own while the item it
|
|
6
|
-
* `autoUnfold`, while the sidebar has room for every row. Pinned and recent
|
|
7
|
-
* like any other, which the caller builds from its own state.
|
|
4
|
+
* expanded sidebar, and as the icon rail the shell shows under 1000px or while `collapsed`. A
|
|
5
|
+
* section can fold past `maxVisible` behind a More row, and unfolds on its own while the item it
|
|
6
|
+
* holds is open or, with `autoUnfold`, while the sidebar has room for every row. Pinned and recent
|
|
7
|
+
* records are sections like any other, which the caller builds from its own state.
|
|
8
|
+
* `WorkspaceSwitcher` heads the sidebar: the current workspace's mark and name, opening a menu of
|
|
9
|
+
* the others.
|
|
8
10
|
*/
|
|
9
|
-
import { CaretDownIcon, CaretUpIcon } from "@phosphor-icons/react";
|
|
11
|
+
import { CaretDownIcon, CaretLineRightIcon, CaretUpDownIcon, CaretUpIcon, } from "@phosphor-icons/react";
|
|
10
12
|
import { Fragment, useLayoutEffect, useRef, useState, } from "react";
|
|
11
|
-
import { css } from "#primitives/css";
|
|
13
|
+
import { css, cx } from "#primitives/css";
|
|
14
|
+
import { IconButton } from "./button.js";
|
|
12
15
|
import { icon } from "./icon.js";
|
|
13
16
|
import { useCiridaeLocale } from "./locale.js";
|
|
17
|
+
import { Menu } from "./menu.js";
|
|
14
18
|
import { AppSidebar, AppSidebarFooter, AppSidebarNav, AppSidebarRail } from "./shell.js";
|
|
15
|
-
import { SidebarItem, SidebarPanel, SidebarSection } from "./sidebar.js";
|
|
16
|
-
|
|
19
|
+
import { SidebarItem, SidebarPanel, SidebarRailRule, SidebarSection } from "./sidebar.js";
|
|
20
|
+
import { singleLineTextClass } from "./single-line-text.js";
|
|
21
|
+
const navCount = css({ color: "fg.subtle", fontSize: "code", fontVariantNumeric: "tabular-nums" });
|
|
22
|
+
// The rail's items, a section at a time: the sections stand apart by the rail's gap.
|
|
23
|
+
const railSection = css({
|
|
24
|
+
display: "flex",
|
|
25
|
+
flexDirection: "column",
|
|
26
|
+
alignItems: "center",
|
|
27
|
+
gap: "layout.tightGap",
|
|
28
|
+
});
|
|
17
29
|
const sectionFooter = css({
|
|
18
30
|
display: "grid",
|
|
19
31
|
gap: "control.trackInset",
|
|
@@ -84,8 +96,9 @@ function sectionRuns(sections) {
|
|
|
84
96
|
}
|
|
85
97
|
return runs;
|
|
86
98
|
}
|
|
87
|
-
export function AppNavigation({ sections, activeId: ownActiveId = null, renderLink, header, railHeader, footer, autoUnfold = true, labels: ownLabels, ...props }) {
|
|
88
|
-
const
|
|
99
|
+
export function AppNavigation({ sections, activeId: ownActiveId = null, renderLink, header, railHeader, footer, autoUnfold = true, collapsed = false, onExpand, labels: ownLabels, ...props }) {
|
|
100
|
+
const locale = useCiridaeLocale();
|
|
101
|
+
const labels = { ...locale.labels.navigation, ...ownLabels };
|
|
89
102
|
const activeIds = ownActiveId === null ? [] : typeof ownActiveId === "string" ? [ownActiveId] : ownActiveId;
|
|
90
103
|
// Every row a fold hides, less the More row it trades them for.
|
|
91
104
|
const foldedRows = sections.reduce((rows, section) => rows +
|
|
@@ -95,8 +108,86 @@ export function AppNavigation({ sections, activeId: ownActiveId = null, renderLi
|
|
|
95
108
|
const room = useSidebarRoom(foldedRows);
|
|
96
109
|
const roomy = autoUnfold && room.roomy;
|
|
97
110
|
const sectionBlock = (section) => (_jsxs(SidebarSection, { label: section.label, children: [_jsx(SectionItems, { section: section, activeIds: activeIds, renderLink: renderLink, roomy: roomy, labels: labels }), section.footer && _jsx("div", { className: sectionFooter, children: section.footer })] }, section.id));
|
|
98
|
-
return (_jsxs(AppSidebar, { "aria-label": labels.navigation, ...props, children: [_jsxs(AppSidebarNav, { ref: room.navRef, children: [header, sectionRuns(sections).map((run) => run.panel ? (_jsx(SidebarPanel, { children: run.sections.map(sectionBlock) }, run.sections[0].id)) : (_jsx(Fragment, { children: run.sections.map(sectionBlock) }, run.sections[0].id)))] }), _jsxs(AppSidebarRail, { children: [railHeader, sections
|
|
99
|
-
.filter((section) => section.rail !== false)
|
|
100
|
-
.
|
|
101
|
-
|
|
111
|
+
return (_jsxs(AppSidebar, { "aria-label": labels.navigation, collapsed: collapsed, ...props, children: [_jsxs(AppSidebarNav, { ref: room.navRef, children: [header, sectionRuns(sections).map((run) => run.panel ? (_jsx(SidebarPanel, { children: run.sections.map(sectionBlock) }, run.sections[0].id)) : (_jsx(Fragment, { children: run.sections.map(sectionBlock) }, run.sections[0].id)))] }), _jsxs(AppSidebarRail, { children: [railHeader, sections
|
|
112
|
+
.filter((section) => section.rail !== false && section.items.length > 0)
|
|
113
|
+
.map((section) => (_jsxs("div", { className: railSection, children: [section.label && _jsx(SidebarRailRule, {}), section.items.map((item) => (_jsx(SidebarItem, { "aria-label": item.label, icon: item.icon, active: activeIds.includes(item.id), href: item.href, render: item.render ?? renderLink?.(item), onClick: item.onSelect }, item.id)))] }, section.id)))] }), _jsxs(AppSidebarFooter, { ref: room.footerRef, children: [collapsed && onExpand && (_jsx(AppSidebarRail, { children: _jsx(IconButton, { "aria-label": locale.labels.common.expand, size: "medium", onClick: onExpand, children: _jsx(CaretLineRightIcon, { className: cx(icon({ size: "small" }), css({ _rtl: { transform: "scaleX(-1)" } })) }) }) })), footer] })] }));
|
|
114
|
+
}
|
|
115
|
+
/*
|
|
116
|
+
* A workspace's square mark with its initial: the primary face for the current one, the raised
|
|
117
|
+
* face with a quieter initial for the others. Its edge is a gradient, so it is drawn under the face
|
|
118
|
+
* through a transparent border.
|
|
119
|
+
*/
|
|
120
|
+
const workspaceMark = css({
|
|
121
|
+
display: "inline-grid",
|
|
122
|
+
placeItems: "center",
|
|
123
|
+
flexShrink: "0",
|
|
124
|
+
width: "workspace.mark",
|
|
125
|
+
height: "workspace.mark",
|
|
126
|
+
border: "1px solid transparent",
|
|
127
|
+
borderRadius: "keycap",
|
|
128
|
+
background: "token(gradients.raised) padding-box, token(gradients.border.otherWorkspaceMark) border-box",
|
|
129
|
+
color: "fg.label",
|
|
130
|
+
fontSize: "code",
|
|
131
|
+
fontWeight: "medium",
|
|
132
|
+
letterSpacing: "compact",
|
|
133
|
+
lineHeight: "tight",
|
|
134
|
+
"&[data-current]": {
|
|
135
|
+
background: "token(gradients.primary) padding-box, token(gradients.border.primary) border-box",
|
|
136
|
+
color: "bg.canvas",
|
|
137
|
+
_light: { color: "fg.onAccent" },
|
|
138
|
+
},
|
|
139
|
+
"&[data-size=small]": { width: "control.mark", height: "control.mark", borderRadius: "control" },
|
|
140
|
+
});
|
|
141
|
+
/** A workspace's mark: its name's first letter on a square tile. */
|
|
142
|
+
export function WorkspaceMark({ name, current = false, size = "regular", className, }) {
|
|
143
|
+
return (_jsx("span", { "aria-hidden": "true", className: cx(workspaceMark, className), "data-ciridae-ui": "", "data-current": current || undefined, "data-size": size, children: name.trim().charAt(0).toLocaleUpperCase() }));
|
|
144
|
+
}
|
|
145
|
+
// The sidebar's first row: hovered it takes the nav row's wash, open a grey well in the canvas's edge.
|
|
146
|
+
const workspaceSwitcher = css({
|
|
147
|
+
display: "flex",
|
|
148
|
+
alignItems: "center",
|
|
149
|
+
gap: "layout.controlGap",
|
|
150
|
+
width: "100%",
|
|
151
|
+
height: "control.default",
|
|
152
|
+
padding: "control.compactBlock",
|
|
153
|
+
border: "0",
|
|
154
|
+
borderRadius: "keycap",
|
|
155
|
+
background: "transparent",
|
|
156
|
+
color: "fg.primary",
|
|
157
|
+
font: "inherit",
|
|
158
|
+
fontSize: "body",
|
|
159
|
+
fontWeight: "medium",
|
|
160
|
+
letterSpacing: "compact",
|
|
161
|
+
lineHeight: "tight",
|
|
162
|
+
textAlign: "start",
|
|
163
|
+
cursor: "pointer",
|
|
164
|
+
transitionProperty: "background",
|
|
165
|
+
transitionDuration: "fast",
|
|
166
|
+
_hover: { background: "token(gradients.selected)", _light: { background: "bg.rowHover" } },
|
|
167
|
+
// Open, it keeps its open face under the pointer too.
|
|
168
|
+
"&[data-popup-open], &[data-popup-open]:hover": {
|
|
169
|
+
background: "bg.openTrigger",
|
|
170
|
+
_light: { background: "bg.openTrigger" },
|
|
171
|
+
boxShadow: "inset 0 0 0 1px token(colors.border.canvas)",
|
|
172
|
+
},
|
|
173
|
+
_focusVisible: { focusRing: "outer" },
|
|
174
|
+
"& > [data-name]": { flex: "1" },
|
|
175
|
+
"& > svg": { flexShrink: "0", color: "fg.muted" },
|
|
176
|
+
"&[data-compact]": {
|
|
177
|
+
justifyContent: "center",
|
|
178
|
+
width: "control.compact",
|
|
179
|
+
height: "control.compact",
|
|
180
|
+
padding: "none",
|
|
181
|
+
},
|
|
182
|
+
});
|
|
183
|
+
/** The current workspace, opening a menu of every workspace the reader belongs to, current checked. */
|
|
184
|
+
export function WorkspaceSwitcher({ workspaces, currentId, onSelect, compact = false, }) {
|
|
185
|
+
const current = workspaces.find((workspace) => workspace.id === currentId);
|
|
186
|
+
const name = current?.name ?? "";
|
|
187
|
+
return (_jsx(Menu, { trigger: _jsxs("button", { type: "button", "aria-label": compact ? name : undefined, className: workspaceSwitcher, "data-ciridae-ui": "", "data-compact": compact || undefined, children: [_jsx(WorkspaceMark, { name: name, current: true }), !compact && (_jsxs(_Fragment, { children: [_jsx("span", { className: singleLineTextClass, "data-name": "", children: name }), _jsx(CaretUpDownIcon, { className: icon({ size: "small" }) })] }))] }), items: workspaces.map((workspace) => ({
|
|
188
|
+
label: workspace.name,
|
|
189
|
+
icon: (_jsx(WorkspaceMark, { name: workspace.name, current: workspace.id === currentId, size: "small" })),
|
|
190
|
+
checked: workspace.id === currentId,
|
|
191
|
+
onSelect: () => onSelect(workspace.id),
|
|
192
|
+
})), matchTriggerWidth: true }));
|
|
102
193
|
}
|
|
@@ -1,14 +1,39 @@
|
|
|
1
1
|
import { useRender } from "@base-ui/react/use-render";
|
|
2
|
-
import type { ReactNode } from "react";
|
|
2
|
+
import type { ComponentProps, ReactNode } from "react";
|
|
3
3
|
/** What a color says, wherever color carries meaning. */
|
|
4
4
|
export type Tone = "neutral" | "info" | "success" | "warning" | "danger";
|
|
5
|
+
/** A badge's tone: one of the five, or `ink` for an outcome, which no other system uses. */
|
|
6
|
+
export type BadgeTone = Tone | "ink";
|
|
5
7
|
export type BadgeProps = Omit<useRender.ComponentProps<"span">, "children"> & {
|
|
6
|
-
tone?:
|
|
8
|
+
tone?: BadgeTone;
|
|
7
9
|
/** `pill` for a judgment or state; `square` for a fact, such as a value's source. */
|
|
8
10
|
shape?: "pill" | "square";
|
|
9
|
-
/**
|
|
10
|
-
|
|
11
|
+
/**
|
|
12
|
+
* `filled` by default; `outline` is the card's face in the tone's edge; `bare` drops the box:
|
|
13
|
+
* the icon and the words, muted, the tone on the icon alone.
|
|
14
|
+
*/
|
|
15
|
+
appearance?: "filled" | "outline" | "bare";
|
|
11
16
|
icon?: ReactNode;
|
|
12
17
|
children: ReactNode;
|
|
13
18
|
};
|
|
14
19
|
export declare function Badge({ tone, shape, appearance, icon, children, render, className, ...props }: BadgeProps): import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
|
|
20
|
+
export type BadgeGlyphKind = "notReached" | "hereNow" | "terminal" | "contested" | "noChange" | "check" | "stop";
|
|
21
|
+
export type BadgeGlyphProps = Omit<ComponentProps<"svg">, "children"> & {
|
|
22
|
+
kind: BadgeGlyphKind;
|
|
23
|
+
};
|
|
24
|
+
/** A badge's drawn glyph: a lifecycle position, a contested verdict, an unchanged outcome. */
|
|
25
|
+
export declare function BadgeGlyph({ kind, className, ...props }: BadgeGlyphProps): import("react").JSX.Element;
|
|
26
|
+
/** How strongly a value has been checked: none, by a model, by a person, against the world. */
|
|
27
|
+
export type AssuranceLevel = 0 | 1 | 2 | 3;
|
|
28
|
+
export type AssuranceGlyphProps = Omit<ComponentProps<"svg">, "children"> & {
|
|
29
|
+
level: AssuranceLevel;
|
|
30
|
+
};
|
|
31
|
+
/** A three-bar meter, lit to the level, so a value's assurance reads without color. */
|
|
32
|
+
export declare function AssuranceGlyph({ level, className, ...props }: AssuranceGlyphProps): import("react").JSX.Element;
|
|
33
|
+
/** Where a record sits in its stages. The glyph is fixed per position; the words name the stage. */
|
|
34
|
+
export type LifecycleState = "notReached" | "hereNow" | "done" | "blocked" | "agentWaiting" | "expired" | "terminal" | "awaiting";
|
|
35
|
+
export type LifecycleBadgeProps = Omit<BadgeProps, "tone" | "shape" | "appearance" | "icon"> & {
|
|
36
|
+
state: LifecycleState;
|
|
37
|
+
};
|
|
38
|
+
/** A lifecycle pill: the state picks the glyph and the hue; the words are the stage's name. */
|
|
39
|
+
export declare function LifecycleBadge({ state, ...props }: LifecycleBadgeProps): import("react").JSX.Element;
|