@twoux/ui 0.0.0-stage → 0.1.0
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/LICENSE.md +61 -0
- package/LICENSES/Apache-2.0.txt +202 -0
- package/NOTICE.md +64 -0
- package/SHADCN-DIFF.md +228 -0
- package/agent/AGENTS.md +358 -0
- package/agent/docs-md/accessibility.md +49 -0
- package/agent/docs-md/action-catalogue.md +81 -0
- package/agent/docs-md/actions.md +74 -0
- package/agent/docs-md/agent-activity-log.md +70 -0
- package/agent/docs-md/agent-permissions.md +82 -0
- package/agent/docs-md/agent-run.md +90 -0
- package/agent/docs-md/agents-on-behalf.md +74 -0
- package/agent/docs-md/ai-column.md +85 -0
- package/agent/docs-md/ai-origin-label.md +70 -0
- package/agent/docs-md/announcing-features.md +73 -0
- package/agent/docs-md/answer-actions.md +81 -0
- package/agent/docs-md/app-shell.md +114 -0
- package/agent/docs-md/artifact-card.md +89 -0
- package/agent/docs-md/ask-on-screen.md +72 -0
- package/agent/docs-md/assistant-page.md +100 -0
- package/agent/docs-md/assistant-panel.md +142 -0
- package/agent/docs-md/avatar.md +82 -0
- package/agent/docs-md/banner.md +106 -0
- package/agent/docs-md/board.md +163 -0
- package/agent/docs-md/breadcrumb.md +89 -0
- package/agent/docs-md/breakdown-table.md +97 -0
- package/agent/docs-md/bulk-action-bar.md +112 -0
- package/agent/docs-md/button.md +293 -0
- package/agent/docs-md/card.md +115 -0
- package/agent/docs-md/change-list.md +99 -0
- package/agent/docs-md/change-request.md +166 -0
- package/agent/docs-md/chart-area.md +87 -0
- package/agent/docs-md/chart-bar.md +100 -0
- package/agent/docs-md/chart-box-plot.md +88 -0
- package/agent/docs-md/chart-bullet.md +89 -0
- package/agent/docs-md/chart-candlestick.md +112 -0
- package/agent/docs-md/chart-card-line.md +120 -0
- package/agent/docs-md/chart-donut.md +90 -0
- package/agent/docs-md/chart-drill-down.md +72 -0
- package/agent/docs-md/chart-funnel.md +71 -0
- package/agent/docs-md/chart-heatmap.md +76 -0
- package/agent/docs-md/chart-histogram.md +81 -0
- package/agent/docs-md/chart-map.md +165 -0
- package/agent/docs-md/chart-mixed.md +88 -0
- package/agent/docs-md/chart-price.md +92 -0
- package/agent/docs-md/chart-radial-bars.md +109 -0
- package/agent/docs-md/chart-radial-lines.md +111 -0
- package/agent/docs-md/chart-sankey.md +104 -0
- package/agent/docs-md/chart-scatter.md +87 -0
- package/agent/docs-md/chart-share-bar.md +89 -0
- package/agent/docs-md/chart-treemap.md +98 -0
- package/agent/docs-md/chart-waterfall.md +93 -0
- package/agent/docs-md/checkbox.md +131 -0
- package/agent/docs-md/choosing-selection-control.md +71 -0
- package/agent/docs-md/citations-sources.md +79 -0
- package/agent/docs-md/citing-sources.md +63 -0
- package/agent/docs-md/clarify-before-acting.md +69 -0
- package/agent/docs-md/clarifying-question.md +92 -0
- package/agent/docs-md/code-block.md +174 -0
- package/agent/docs-md/colours.md +87 -0
- package/agent/docs-md/columns-menu.md +140 -0
- package/agent/docs-md/combobox.md +161 -0
- package/agent/docs-md/command-palette.md +126 -0
- package/agent/docs-md/confidence-note.md +87 -0
- package/agent/docs-md/configurable-dashboard.md +76 -0
- package/agent/docs-md/confirmation-dialog.md +93 -0
- package/agent/docs-md/connected-tools.md +80 -0
- package/agent/docs-md/connector-picker.md +127 -0
- package/agent/docs-md/consistency.md +53 -0
- package/agent/docs-md/content-density.md +61 -0
- package/agent/docs-md/conversation-history.md +78 -0
- package/agent/docs-md/create-record.md +80 -0
- package/agent/docs-md/csv-import.md +88 -0
- package/agent/docs-md/dashboard-items.md +78 -0
- package/agent/docs-md/data-export.md +61 -0
- package/agent/docs-md/data-grid.md +137 -0
- package/agent/docs-md/data-vis-colours.md +76 -0
- package/agent/docs-md/data-visualisation.md +76 -0
- package/agent/docs-md/date-picker.md +178 -0
- package/agent/docs-md/delete-record.md +75 -0
- package/agent/docs-md/dense-table.md +495 -0
- package/agent/docs-md/design-tokens.md +53 -0
- package/agent/docs-md/disabled-read-only.md +75 -0
- package/agent/docs-md/drag-and-drop.md +74 -0
- package/agent/docs-md/dropdown-menu.md +137 -0
- package/agent/docs-md/edit-record.md +73 -0
- package/agent/docs-md/empty-loading-error.md +78 -0
- package/agent/docs-md/empty-state.md +128 -0
- package/agent/docs-md/error-messages.md +75 -0
- package/agent/docs-md/error-state.md +123 -0
- package/agent/docs-md/export-menu.md +86 -0
- package/agent/docs-md/feedback-prompt.md +88 -0
- package/agent/docs-md/field-mapping.md +78 -0
- package/agent/docs-md/file-drop.md +110 -0
- package/agent/docs-md/filter-bar.md +138 -0
- package/agent/docs-md/filter-panel.md +103 -0
- package/agent/docs-md/filtering-search.md +79 -0
- package/agent/docs-md/follow-ups-starters.md +79 -0
- package/agent/docs-md/form-validation.md +95 -0
- package/agent/docs-md/generated-columns.md +65 -0
- package/agent/docs-md/generated-drafts-files.md +71 -0
- package/agent/docs-md/grouped-list.md +89 -0
- package/agent/docs-md/guide-cue.md +170 -0
- package/agent/docs-md/help-panel.md +103 -0
- package/agent/docs-md/help-system.md +74 -0
- package/agent/docs-md/iconography.md +49 -0
- package/agent/docs-md/inbox.md +108 -0
- package/agent/docs-md/inline-editing-validation.md +80 -0
- package/agent/docs-md/inline-input-in-answer.md +70 -0
- package/agent/docs-md/items-palette.md +116 -0
- package/agent/docs-md/keyboard-model.md +47 -0
- package/agent/docs-md/keyboard-navigation.md +68 -0
- package/agent/docs-md/keyboard-shortcut-hint.md +87 -0
- package/agent/docs-md/keyboard-shortcut-sheet.md +90 -0
- package/agent/docs-md/labelling-ai-output.md +66 -0
- package/agent/docs-md/layout.md +47 -0
- package/agent/docs-md/list-table.md +254 -0
- package/agent/docs-md/loading-skeleton.md +91 -0
- package/agent/docs-md/managing-agents.md +71 -0
- package/agent/docs-md/message-composer.md +105 -0
- package/agent/docs-md/migrating.md +49 -0
- package/agent/docs-md/motion.md +93 -0
- package/agent/docs-md/navigation.md +76 -0
- package/agent/docs-md/notification-channels.md +65 -0
- package/agent/docs-md/notifications-decision.md +100 -0
- package/agent/docs-md/onboarding-step.md +114 -0
- package/agent/docs-md/order-book.md +108 -0
- package/agent/docs-md/page-header.md +96 -0
- package/agent/docs-md/pagination.md +88 -0
- package/agent/docs-md/pane-layout.md +105 -0
- package/agent/docs-md/period-comparison.md +66 -0
- package/agent/docs-md/plan-card.md +120 -0
- package/agent/docs-md/popover-rules.md +88 -0
- package/agent/docs-md/prompt-editor.md +90 -0
- package/agent/docs-md/propose-apply.md +65 -0
- package/agent/docs-md/provider-logo.md +97 -0
- package/agent/docs-md/radio.md +83 -0
- package/agent/docs-md/ranked-list.md +102 -0
- package/agent/docs-md/reasoning-progress.md +72 -0
- package/agent/docs-md/record-detail.md +71 -0
- package/agent/docs-md/regenerating-answers.md +70 -0
- package/agent/docs-md/run-status-bar.md +94 -0
- package/agent/docs-md/saved-tasks-schedules.md +63 -0
- package/agent/docs-md/saved-views.md +149 -0
- package/agent/docs-md/saving-views.md +68 -0
- package/agent/docs-md/scheduler.md +258 -0
- package/agent/docs-md/secondary-panels.md +72 -0
- package/agent/docs-md/segmented-control.md +88 -0
- package/agent/docs-md/selection-bulk-actions.md +70 -0
- package/agent/docs-md/separator.md +75 -0
- package/agent/docs-md/setup-checklist.md +92 -0
- package/agent/docs-md/shape-elevation.md +55 -0
- package/agent/docs-md/sheet-grid.md +211 -0
- package/agent/docs-md/side-panel.md +134 -0
- package/agent/docs-md/sign-in.md +114 -0
- package/agent/docs-md/spacing.md +55 -0
- package/agent/docs-md/split-button.md +100 -0
- package/agent/docs-md/stat-compare.md +82 -0
- package/agent/docs-md/static-dashboard.md +75 -0
- package/agent/docs-md/status-badge.md +140 -0
- package/agent/docs-md/step-card.md +75 -0
- package/agent/docs-md/stepper.md +92 -0
- package/agent/docs-md/suggested-changes.md +91 -0
- package/agent/docs-md/switch.md +78 -0
- package/agent/docs-md/table-settings.md +81 -0
- package/agent/docs-md/tabs.md +85 -0
- package/agent/docs-md/tag-input.md +108 -0
- package/agent/docs-md/tag.md +79 -0
- package/agent/docs-md/task-builder.md +87 -0
- package/agent/docs-md/team-instructions.md +72 -0
- package/agent/docs-md/templates-gallery.md +85 -0
- package/agent/docs-md/text-input.md +160 -0
- package/agent/docs-md/thinking-steps.md +89 -0
- package/agent/docs-md/timeline.md +189 -0
- package/agent/docs-md/timestamp.md +84 -0
- package/agent/docs-md/timestamps.md +70 -0
- package/agent/docs-md/toast.md +117 -0
- package/agent/docs-md/tooltip.md +90 -0
- package/agent/docs-md/typography.md +62 -0
- package/agent/docs-md/undo-destructive.md +66 -0
- package/agent/docs-md/unsaved-changes.md +71 -0
- package/agent/docs-md/usage-meter.md +80 -0
- package/agent/docs-md/user-feedback.md +71 -0
- package/agent/docs-md/voice-and-tone.md +60 -0
- package/agent/docs-md/why-choice.md +93 -0
- package/agent/docs-md/workspace-onboarding.md +74 -0
- package/agent/docs-md/writing.md +48 -0
- package/agent/llms-full.txt +18369 -0
- package/agent/llms.txt +198 -0
- package/agent/skills/twoux-audit/SKILL.md +49 -0
- package/agent/skills/twoux-build/SKILL.md +39 -0
- package/agent/skills/twoux-design/SKILL.md +54 -0
- package/agent/skills/twoux-write/SKILL.md +32 -0
- package/bin/check.mjs +123 -0
- package/bin/docs.mjs +24 -0
- package/bin/guard.mjs +27 -0
- package/bin/init.mjs +113 -0
- package/bin/knowledge.mjs +87 -0
- package/bin/mcp.mjs +116 -0
- package/bin/theme.mjs +168 -0
- package/bin/twoux.mjs +25 -0
- package/package.json +80 -3
- package/registry/jobs.json +1028 -0
- package/registry/palette.json +770 -0
- package/registry/rules.json +913 -0
- package/src/components/agent-review.tsx +364 -0
- package/src/components/ai-basics.tsx +212 -0
- package/src/components/ai-work.tsx +304 -0
- package/src/components/ai.tsx +227 -0
- package/src/components/alert-dialog.tsx +203 -0
- package/src/components/alert.tsx +86 -0
- package/src/components/avatar.tsx +114 -0
- package/src/components/badge.tsx +59 -0
- package/src/components/board.tsx +397 -0
- package/src/components/breadcrumb.tsx +114 -0
- package/src/components/button-group.tsx +90 -0
- package/src/components/button.tsx +109 -0
- package/src/components/calendar.tsx +247 -0
- package/src/components/card.tsx +102 -0
- package/src/components/chart-map.tsx +336 -0
- package/src/components/charts-more.tsx +1331 -0
- package/src/components/charts-radial.tsx +328 -0
- package/src/components/charts.tsx +390 -0
- package/src/components/checkbox.tsx +40 -0
- package/src/components/code.tsx +139 -0
- package/src/components/collapsible.tsx +37 -0
- package/src/components/combobox.tsx +339 -0
- package/src/components/command-extras.tsx +11 -0
- package/src/components/command.tsx +196 -0
- package/src/components/data-grid.tsx +213 -0
- package/src/components/data-table.tsx +334 -0
- package/src/components/data-views.tsx +740 -0
- package/src/components/date-picker.tsx +686 -0
- package/src/components/dense-table.tsx +207 -0
- package/src/components/dialog.tsx +162 -0
- package/src/components/dropdown-menu.tsx +264 -0
- package/src/components/empty.tsx +114 -0
- package/src/components/feedback-prompt.tsx +80 -0
- package/src/components/field.tsx +254 -0
- package/src/components/fields.tsx +112 -0
- package/src/components/flow.tsx +840 -0
- package/src/components/guide-cue.tsx +273 -0
- package/src/components/inbox.tsx +113 -0
- package/src/components/input-group.tsx +176 -0
- package/src/components/input.tsx +26 -0
- package/src/components/kbd.tsx +34 -0
- package/src/components/label.tsx +27 -0
- package/src/components/labels.tsx +176 -0
- package/src/components/motion.tsx +136 -0
- package/src/components/onboarding.tsx +454 -0
- package/src/components/order-book.tsx +185 -0
- package/src/components/pane-layout.tsx +417 -0
- package/src/components/popover.tsx +96 -0
- package/src/components/provider-logos.data.ts +49 -0
- package/src/components/radio-group.tsx +52 -0
- package/src/components/scheduler.tsx +997 -0
- package/src/components/segmented-control.tsx +28 -0
- package/src/components/select.tsx +195 -0
- package/src/components/separator.tsx +31 -0
- package/src/components/sheet.tsx +148 -0
- package/src/components/shell.tsx +426 -0
- package/src/components/shortcut-sheet.tsx +83 -0
- package/src/components/skeleton.tsx +18 -0
- package/src/components/sonner.tsx +107 -0
- package/src/components/spinner.tsx +13 -0
- package/src/components/split-button.tsx +58 -0
- package/src/components/stepper.tsx +78 -0
- package/src/components/switch.tsx +43 -0
- package/src/components/table.tsx +126 -0
- package/src/components/tabs.tsx +118 -0
- package/src/components/tag-input.tsx +253 -0
- package/src/components/textarea.tsx +21 -0
- package/src/components/theme.tsx +80 -0
- package/src/components/timeline.tsx +408 -0
- package/src/components/toggle-group.tsx +87 -0
- package/src/components/toggle.tsx +54 -0
- package/src/components/tooltip.tsx +65 -0
- package/src/components/workspace-actions.tsx +294 -0
- package/src/components/workspace-import.tsx +465 -0
- package/src/components/workspace.tsx +1884 -0
- package/src/index.ts +80 -0
- package/src/lib/active-indicator.tsx +53 -0
- package/src/lib/chart-kit.tsx +72 -0
- package/src/lib/cn.ts +6 -0
- package/src/lib/shortcuts.tsx +61 -0
- package/src/lib/workspace.tsx +10 -0
- package/src/shadcn/alert-dialog.tsx +196 -0
- package/src/shadcn/alert.tsx +65 -0
- package/src/shadcn/avatar.tsx +108 -0
- package/src/shadcn/badge.tsx +47 -0
- package/src/shadcn/breadcrumb.tsx +108 -0
- package/src/shadcn/button-group.tsx +83 -0
- package/src/shadcn/button.tsx +63 -0
- package/src/shadcn/calendar.tsx +220 -0
- package/src/shadcn/card.tsx +91 -0
- package/src/shadcn/checkbox.tsx +31 -0
- package/src/shadcn/collapsible.tsx +33 -0
- package/src/shadcn/command.tsx +184 -0
- package/src/shadcn/dialog.tsx +158 -0
- package/src/shadcn/dropdown-menu.tsx +256 -0
- package/src/shadcn/empty.tsx +103 -0
- package/src/shadcn/field.tsx +248 -0
- package/src/shadcn/input-group.tsx +170 -0
- package/src/shadcn/input.tsx +20 -0
- package/src/shadcn/kbd.tsx +28 -0
- package/src/shadcn/label.tsx +23 -0
- package/src/shadcn/popover.tsx +88 -0
- package/src/shadcn/radio-group.tsx +44 -0
- package/src/shadcn/select.tsx +189 -0
- package/src/shadcn/separator.tsx +27 -0
- package/src/shadcn/sheet.tsx +142 -0
- package/src/shadcn/skeleton.tsx +13 -0
- package/src/shadcn/sonner.tsx +40 -0
- package/src/shadcn/switch.tsx +34 -0
- package/src/shadcn/table.tsx +115 -0
- package/src/shadcn/tabs.tsx +90 -0
- package/src/shadcn/textarea.tsx +17 -0
- package/src/shadcn/toggle-group.tsx +83 -0
- package/src/shadcn/toggle.tsx +46 -0
- package/src/shadcn/tooltip.tsx +56 -0
- package/src/styles/palette.css +127 -0
- package/src/styles/theme.css +35 -0
- package/src/styles/twoux.css +236 -0
- package/theme.json +14 -0
- package/README.md +0 -3
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Answer actions
|
|
2
|
+
|
|
3
|
+
The row under every answer: origin label on the left, then Copy, Helpful, Not helpful, Regenerate and a More menu. Always visible, never only on hover.
|
|
4
|
+
|
|
5
|
+
Kind: component · Group: AI and agents · Status: built, import from @twoux/ui
|
|
6
|
+
|
|
7
|
+
## When to use
|
|
8
|
+
|
|
9
|
+
- Under every answer the assistant gives in the side panel or on the assistant page.
|
|
10
|
+
- Under a stacked group of answer parts once, after the last part, not after each one.
|
|
11
|
+
- Under an answer that was stopped, so Regenerate and feedback are still reachable.
|
|
12
|
+
|
|
13
|
+
## When not to use
|
|
14
|
+
|
|
15
|
+
- For actions on a generated file, such as Download. Those belong in the file's own card.
|
|
16
|
+
- For feedback about the page or product. Use the product feedback route, not answer feedback.
|
|
17
|
+
- Under approval results, notices or the clarifying question card.
|
|
18
|
+
|
|
19
|
+
## Structure
|
|
20
|
+
|
|
21
|
+
1. Origin label: "Assistant · from your workspace data" (see AI origin label)
|
|
22
|
+
2. Copy: copies the answer as plain text with record IDs
|
|
23
|
+
3. Helpful and Not helpful: feedback toggles with submitting and sent states
|
|
24
|
+
4. Regenerate: latest answer only
|
|
25
|
+
5. More (⋯): Save as task, Open as page, Copy link
|
|
26
|
+
6. Not helpful form (optional): a short dismissible form under the answer
|
|
27
|
+
|
|
28
|
+
## Guidelines
|
|
29
|
+
|
|
30
|
+
- Show at most four icon buttons; anything else goes in More.
|
|
31
|
+
- Keep the row visible at all times. Hover-only actions are invisible to keyboard and touch users.
|
|
32
|
+
- Feedback has three states: default, sending (spinner in the pressed button) and sent. Once sent, the chosen button stays pressed and the other is disabled with a reason.
|
|
33
|
+
- Not helpful opens an optional form under the answer: reasons as checkboxes (Wrong figures, Missed records, Too long, Other) and a note. It can be closed without sending.
|
|
34
|
+
- Same order everywhere: label, Copy, Helpful, Not helpful, Regenerate, More.
|
|
35
|
+
|
|
36
|
+
## Content
|
|
37
|
+
|
|
38
|
+
- Tooltips: "Copy", "Helpful", "Not helpful", "Regenerate answer", "More actions".
|
|
39
|
+
- After feedback: "Feedback sent". Disabled reason on the other button: "You marked this as helpful".
|
|
40
|
+
- Copy confirmation is a short tooltip, "Copied", not a toast.
|
|
41
|
+
|
|
42
|
+
## Accessibility
|
|
43
|
+
|
|
44
|
+
- Feedback buttons are toggle buttons with aria-pressed, named "Helpful" and "Not helpful".
|
|
45
|
+
- "Feedback sent" and "Copied" are announced as status messages without moving focus.
|
|
46
|
+
- Disabled buttons stay focusable so their reason can be read.
|
|
47
|
+
|
|
48
|
+
## Keyboard
|
|
49
|
+
|
|
50
|
+
| Keys | Action |
|
|
51
|
+
|---|---|
|
|
52
|
+
| Tab | Move through the actions after reading the answer |
|
|
53
|
+
| Enter / Space | Press the focused action |
|
|
54
|
+
| Esc | Close the More menu or the Not helpful form |
|
|
55
|
+
|
|
56
|
+
## Examples
|
|
57
|
+
|
|
58
|
+
### actions
|
|
59
|
+
|
|
60
|
+
```tsx
|
|
61
|
+
import { AnswerActions, AssistantMessage, DropdownMenuItem } from "@twoux/ui";
|
|
62
|
+
|
|
63
|
+
export const meta = { title: "Under every answer", description: "Copy, Helpful, Not helpful and Regenerate (latest answer only), with the rest under More. Always visible, not on hover. Feedback shows sending, then stays pressed.", order: 1 };
|
|
64
|
+
|
|
65
|
+
export default function Example() {
|
|
66
|
+
return (
|
|
67
|
+
<div className="w-full max-w-lg">
|
|
68
|
+
<AssistantMessage from="24 listings" actions={<AnswerActions text="2 listings close this week with fewer than 10 applicants." onRegenerate={() => {}} onFeedback={() => new Promise((r) => setTimeout(r, 500))} more={<><DropdownMenuItem>Save as task</DropdownMenuItem><DropdownMenuItem>Open as page</DropdownMenuItem><DropdownMenuItem>Copy link</DropdownMenuItem></>} />}>
|
|
69
|
+
<p><strong>2 listings</strong> close this week with fewer than 10 applicants.</p>
|
|
70
|
+
</AssistantMessage>
|
|
71
|
+
</div>
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Related
|
|
77
|
+
|
|
78
|
+
- Regenerating an answer
|
|
79
|
+
- Labelling AI output
|
|
80
|
+
- AI origin label
|
|
81
|
+
- Assistant side panel
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# App shell and sidebar navigation
|
|
2
|
+
|
|
3
|
+
The frame of every screen: a tinted left sidebar for navigation and an inset white canvas with a top bar for breadcrumb, Ask and search.
|
|
4
|
+
|
|
5
|
+
Kind: component · Group: Navigation · Status: built, import from @twoux/ui
|
|
6
|
+
|
|
7
|
+
## When to use
|
|
8
|
+
|
|
9
|
+
- Every signed-in screen uses the same shell, with navigation in the same place and order.
|
|
10
|
+
- Products with more than a handful of sections, where a vertical list scales better than a top bar.
|
|
11
|
+
|
|
12
|
+
## When not to use
|
|
13
|
+
|
|
14
|
+
- Switching views of the same data, such as Live or Drafts. Use tabs in the page header.
|
|
15
|
+
- Moving between records in a list. Use J/K with the Side panel.
|
|
16
|
+
- Showing location deeper than two levels. Use the Breadcrumb.
|
|
17
|
+
|
|
18
|
+
## Structure
|
|
19
|
+
|
|
20
|
+
1. Sidebar: workspace switcher
|
|
21
|
+
2. Sidebar: personal items (Inbox with count, My candidates, Assistant)
|
|
22
|
+
3. Sidebar: group headings with items (Hiring: Listings, Pipeline, Reports; Workspace: Settings, Members, Billing)
|
|
23
|
+
4. Sidebar footer: setup checklist ("Get set up 3 / 5")
|
|
24
|
+
5. Canvas top bar: breadcrumb, Ask ⌘J, Search or run a command ⌘K
|
|
25
|
+
6. Canvas: page header and content; side panel docks on the right
|
|
26
|
+
7. Optional rail: 48px of icons with names in tooltips and a Collapse sidebar button at the foot
|
|
27
|
+
|
|
28
|
+
## Guidelines
|
|
29
|
+
|
|
30
|
+
- Keep the sidebar visible on desktop with text labels on every item. Collapse it to a rail of icons only on workspaces where the content needs the width (trading, editors), with each name in a tooltip, the Collapse sidebar button and ⌘B, and the choice saved per person.
|
|
31
|
+
- Two levels at most: group headings and items. Deeper structure becomes tabs on the page.
|
|
32
|
+
- Mark the current item with aria-current="page" and a filled background, not colour alone.
|
|
33
|
+
- Most used items first, settings last.
|
|
34
|
+
- Below 860px the sidebar hides; the top bar gains an inbox button with the same count, so nothing that needs the person disappears.
|
|
35
|
+
- The top bar holds global tools only (Ask, search); page actions live in the page header.
|
|
36
|
+
- Nav items are at least 24px tall with full-width hit areas, to meet the target size minimum.
|
|
37
|
+
- Use frame="flush" for workspaces whose regions sit edge to edge, divided by hairlines instead of gaps and cards; keep the inset canvas everywhere else.
|
|
38
|
+
|
|
39
|
+
## Content
|
|
40
|
+
|
|
41
|
+
- Item labels are short nouns that match the page title they open ("Listings" opens "Listings").
|
|
42
|
+
- Group headings are short and never include the word "navigation".
|
|
43
|
+
- Counts are numbers only and show only when above zero.
|
|
44
|
+
|
|
45
|
+
## Accessibility
|
|
46
|
+
|
|
47
|
+
- The sidebar is a nav landmark with a unique label ("Main"); the canvas is main; the side panel is complementary (2.4.1).
|
|
48
|
+
- Navigation order and labels are the same on every page (3.2.3).
|
|
49
|
+
- Search and the command palette provide a second way to reach pages (2.4.5).
|
|
50
|
+
- Each page has a title that names the page and the product (2.4.2).
|
|
51
|
+
- Focus is always visible on nav items, including inside the tinted sidebar (2.4.7).
|
|
52
|
+
|
|
53
|
+
## Keyboard
|
|
54
|
+
|
|
55
|
+
| Keys | Action |
|
|
56
|
+
|---|---|
|
|
57
|
+
| G L / G P / G I | Go to Listings, Pipeline or Inbox |
|
|
58
|
+
| ⌘K | Search or run a command |
|
|
59
|
+
| ⌘J | Open the Assistant |
|
|
60
|
+
| Tab | Move through sidebar items in order |
|
|
61
|
+
| ⌘B / Ctrl+B | Collapse or expand the sidebar, when the app offers the rail |
|
|
62
|
+
|
|
63
|
+
## Examples
|
|
64
|
+
|
|
65
|
+
### workspace
|
|
66
|
+
|
|
67
|
+
```tsx
|
|
68
|
+
import type React from "react";
|
|
69
|
+
import { AppShell, Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Button, PageHeader, SidebarItem, SidebarSection } from "@twoux/ui";
|
|
70
|
+
import { BarChart3, Briefcase, Inbox, Plus, Settings, MessageSquare, Users } from "lucide-react";
|
|
71
|
+
import { useState } from "react";
|
|
72
|
+
|
|
73
|
+
export const meta = { title: "Hiring workspace", description: "Tinted sidebar with a text label on every item, two levels at most, and the current page marked by fill and aria-current. Below 860px the sidebar moves behind the menu button.", order: 1 };
|
|
74
|
+
|
|
75
|
+
export default function Example() {
|
|
76
|
+
const [page, setPage] = useState("Listings");
|
|
77
|
+
const item = (label: string, icon: React.ReactNode, count?: number) => <SidebarItem key={label} label={label} icon={icon} count={count} current={page === label} onClick={() => setPage(label)} />;
|
|
78
|
+
return (
|
|
79
|
+
<div className="h-120 w-full overflow-hidden rounded-sm border border-line">
|
|
80
|
+
<AppShell
|
|
81
|
+
sidebar={
|
|
82
|
+
<>
|
|
83
|
+
<div className="px-2 pt-1 text-base font-semibold text-ink">TwoUX Hiring</div>
|
|
84
|
+
<SidebarSection>{[item("Inbox", <Inbox />, 4), item("Assistant", <MessageSquare />)]}</SidebarSection>
|
|
85
|
+
<SidebarSection title="Hiring">{[item("Listings", <Briefcase />), item("Candidates", <Users />), item("Reports", <BarChart3 />)]}</SidebarSection>
|
|
86
|
+
<SidebarSection title="Workspace">{[item("Settings", <Settings />)]}</SidebarSection>
|
|
87
|
+
</>
|
|
88
|
+
}
|
|
89
|
+
topBar={
|
|
90
|
+
<Breadcrumb>
|
|
91
|
+
<BreadcrumbList>
|
|
92
|
+
<BreadcrumbItem><BreadcrumbLink href="#hiring">Hiring</BreadcrumbLink></BreadcrumbItem>
|
|
93
|
+
<BreadcrumbSeparator />
|
|
94
|
+
<BreadcrumbItem><BreadcrumbPage>{page}</BreadcrumbPage></BreadcrumbItem>
|
|
95
|
+
</BreadcrumbList>
|
|
96
|
+
</Breadcrumb>
|
|
97
|
+
}
|
|
98
|
+
>
|
|
99
|
+
<PageHeader title={page} description="24 listings across 6 teams" actions={<><Button variant="outline">Export</Button><Button shortcut="C"><Plus />New listing</Button></>} />
|
|
100
|
+
</AppShell>
|
|
101
|
+
</div>
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Related
|
|
107
|
+
|
|
108
|
+
- Breadcrumb
|
|
109
|
+
- Page header
|
|
110
|
+
- Inbox (triage)
|
|
111
|
+
- Command palette (⌘K)
|
|
112
|
+
- Side panel (drawer)
|
|
113
|
+
- Setup checklist
|
|
114
|
+
- Usage meter
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Generated file card
|
|
2
|
+
|
|
3
|
+
A compact card under an answer for one thing the assistant made: name, type and size, origin label, and up to two actions with the rest in a More menu.
|
|
4
|
+
|
|
5
|
+
Kind: component · Group: AI and agents · Status: built, import from @twoux/ui
|
|
6
|
+
|
|
7
|
+
## When to use
|
|
8
|
+
|
|
9
|
+
- For each draft or file the assistant produced in an answer.
|
|
10
|
+
- In agent run results when a step produced a file.
|
|
11
|
+
- For a short command or snippet a person will copy into another tool.
|
|
12
|
+
|
|
13
|
+
## When not to use
|
|
14
|
+
|
|
15
|
+
- For records that already exist. Cite them by ID instead.
|
|
16
|
+
- For files people attached. Those are context chips in the composer.
|
|
17
|
+
- For a figure or short table that answers the question. Keep it in the answer text.
|
|
18
|
+
|
|
19
|
+
## Structure
|
|
20
|
+
|
|
21
|
+
1. Icon for the type (document, table, chart, code)
|
|
22
|
+
2. Title: file name or what it is
|
|
23
|
+
3. Metadata: type, size in KB or MB, rows or words
|
|
24
|
+
4. Origin label with sources on hover
|
|
25
|
+
5. Actions: up to two buttons (Open, Download, Copy, Suggest for record) and More (⋯)
|
|
26
|
+
6. Inline preview (optional): first lines of a snippet, or the chart image
|
|
27
|
+
|
|
28
|
+
## Guidelines
|
|
29
|
+
|
|
30
|
+
- One card per output, stacked under the answer in the order they were made.
|
|
31
|
+
- Code and commands preview in the Code block style with Copy; long previews cut at about eight lines with Open for the rest.
|
|
32
|
+
- While a file is being made, the card shows a skeleton line and "Writing job description"; text around it may stream, the card does not.
|
|
33
|
+
- If making the file failed, the card says why and offers Try again; other cards in the answer stay.
|
|
34
|
+
|
|
35
|
+
## Content
|
|
36
|
+
|
|
37
|
+
- Sizes in KB, MB or GB with one decimal at most: "1.2 MB".
|
|
38
|
+
- Loading text names the thing: "Writing job description", "Loading 38 listings".
|
|
39
|
+
- Title is the file name or a plain description, never "Artifact" or "Output 1".
|
|
40
|
+
|
|
41
|
+
## Accessibility
|
|
42
|
+
|
|
43
|
+
- The card is a group with an accessible name from its title.
|
|
44
|
+
- Action names include the file: "Open job description".
|
|
45
|
+
- The More menu follows the menu button pattern.
|
|
46
|
+
|
|
47
|
+
## Keyboard
|
|
48
|
+
|
|
49
|
+
| Keys | Action |
|
|
50
|
+
|---|---|
|
|
51
|
+
| Tab | Reach the card's actions in order |
|
|
52
|
+
| Enter | Run the focused action |
|
|
53
|
+
| ↓ / ↑ | Move through the More menu |
|
|
54
|
+
| Esc | Close the More menu |
|
|
55
|
+
|
|
56
|
+
## Examples
|
|
57
|
+
|
|
58
|
+
### draft
|
|
59
|
+
|
|
60
|
+
```tsx
|
|
61
|
+
import { ArtifactCard, Button, toast } from "@twoux/ui";
|
|
62
|
+
import { useEffect, useState } from "react";
|
|
63
|
+
|
|
64
|
+
export const meta = { title: "A draft the assistant made", description: "One card per output, with type and size, the origin label and two actions. While it is being written, the card says so; the draft reaches the record only through Suggest for record.", order: 1 };
|
|
65
|
+
|
|
66
|
+
export default function Example() {
|
|
67
|
+
const [writing, setWriting] = useState(true);
|
|
68
|
+
useEffect(() => { const t = setTimeout(() => setWriting(false), 1400); return () => clearTimeout(t); }, []);
|
|
69
|
+
return (
|
|
70
|
+
<div className="w-full max-w-lg">
|
|
71
|
+
<ArtifactCard
|
|
72
|
+
title="Job description"
|
|
73
|
+
meta="Document · 312 words"
|
|
74
|
+
from="LST-1044 and the hiring handbook"
|
|
75
|
+
writing={writing}
|
|
76
|
+
actions={<><Button variant="outline" size="xs" onClick={() => toast("Proposed for LST-1044")}>Suggest for record</Button><Button variant="ghost" size="xs">Open</Button></>}
|
|
77
|
+
preview={<p>Customer Success Lead, Bristol (hybrid). £45,000 to £52,000. You'll look after 40 hiring teams, help them get value from TwoUX in their first 90 days…</p>}
|
|
78
|
+
/>
|
|
79
|
+
</div>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Related
|
|
85
|
+
|
|
86
|
+
- Generated drafts and files
|
|
87
|
+
- Code block
|
|
88
|
+
- AI origin label
|
|
89
|
+
- Answer actions
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Ask about what's on screen
|
|
2
|
+
|
|
3
|
+
⌘J or Ask opens the assistant beside the data with the current selection and filters as removable context chips, so the scope of every answer is visible.
|
|
4
|
+
|
|
5
|
+
Kind: pattern · Group: Patterns: AI
|
|
6
|
+
|
|
7
|
+
**Problem:** People ask "why are these doing badly?" about the rows in front of them. A floating chat with no context makes them retype what they can see, and answers silently cover the wrong set of records.
|
|
8
|
+
|
|
9
|
+
**Solution:** The assistant opens in the same side panel as record detail. Selection and active filters become context chips automatically; removing a chip narrows the scope. Starters are based on what is on screen. Answers lead with the figure, then a table, then the reason.
|
|
10
|
+
|
|
11
|
+
**Built from:** Assistant side panel, Message composer, Follow-ups and starters, Team instructions and files, Side panel (drawer), Bulk action bar
|
|
12
|
+
|
|
13
|
+
## How it works
|
|
14
|
+
|
|
15
|
+
1. Open with ⌘J anywhere, or Ask in the bulk bar. No floating chat bubble on every page.
|
|
16
|
+
2. The composer shows context chips for what the assistant can see: "2 listings", "Status is Live, Expiring". Remove a chip to narrow it; @ adds a record.
|
|
17
|
+
3. The empty state offers two or three tasks based on the context, as chips, then the composer.
|
|
18
|
+
4. The answer leads with the figure, then a table, then the reason, with record IDs as citations.
|
|
19
|
+
5. Follow-ups appear under the answer as muted rows; any table in the answer can open as a filtered view.
|
|
20
|
+
|
|
21
|
+
## When to use
|
|
22
|
+
|
|
23
|
+
- For questions about records the person can see or has selected: compare, explain, summarise.
|
|
24
|
+
- When the answer will be checked against the list beside it.
|
|
25
|
+
|
|
26
|
+
## When not to use
|
|
27
|
+
|
|
28
|
+
- To change records. The assistant proposes; see Propose then apply.
|
|
29
|
+
- For filtering a list by known fields. Filter pills are faster than typing.
|
|
30
|
+
|
|
31
|
+
## Guidelines
|
|
32
|
+
|
|
33
|
+
- Scope is always visible and editable; the assistant never reads more than the chips say unless the person widens it.
|
|
34
|
+
- If the answer needs records outside the chips, ask before widening (see Clarifying before acting).
|
|
35
|
+
- A caveat sits by the composer and is specific: "Uses your workspace data only. Check figures before you act on them."
|
|
36
|
+
- Every message aligns to the same side in the side panel and on the page; who wrote it is shown by name ("You", "Assistant"), not by alignment.
|
|
37
|
+
- The composer is pinned to the bottom and only the thread scrolls. While someone reads or copies older messages, new output doesn't scroll them away; show "New answer ↓" instead.
|
|
38
|
+
- The Ask entry point is a secondary button. It never becomes a second primary button on the page and carries no AI styling.
|
|
39
|
+
- After context is added, the assistant says what it will use in one line: "Using 2 listings and team file Salary bands 2026." People can correct it before anything runs.
|
|
40
|
+
- Connecting an outside tool happens in the thread as a short step card, one step at a time, collapsing to "Connected as Leeds hiring team". People don't leave for settings.
|
|
41
|
+
- Errors keep the question: say what failed (no access, connection lost, service error), that nothing changed, and offer Try again with the question still in the composer.
|
|
42
|
+
- A refusal says plainly what the assistant won't do and what it can do instead: "I can't rank candidates for rejection. I can summarise each application against the role's criteria."
|
|
43
|
+
|
|
44
|
+
## Content
|
|
45
|
+
|
|
46
|
+
- Call it Assistant. Say what it did ("Read 2 listings and 3,000 views"), not that it is AI. No "magic" or "smart".
|
|
47
|
+
- Starters are broad, runnable tasks, not niche examples.
|
|
48
|
+
|
|
49
|
+
## Accessibility
|
|
50
|
+
|
|
51
|
+
- Streamed answers are announced once complete, not word by word.
|
|
52
|
+
- ⌘J returns focus to where it was when the panel closes.
|
|
53
|
+
- The thread is a region named "Assistant conversation"; each finished answer is announced once through a hidden live region.
|
|
54
|
+
|
|
55
|
+
## Do
|
|
56
|
+
|
|
57
|
+
- Do turn selection and filters into chips automatically.
|
|
58
|
+
- Do lead answers with the number.
|
|
59
|
+
|
|
60
|
+
## Don't
|
|
61
|
+
|
|
62
|
+
- Don't add a floating chat bubble to every page.
|
|
63
|
+
- Don't open with a blank "How can I help?".
|
|
64
|
+
|
|
65
|
+
## Related
|
|
66
|
+
|
|
67
|
+
- Propose, then apply
|
|
68
|
+
- Clarifying before acting
|
|
69
|
+
- Citing sources
|
|
70
|
+
- Keyboard-first navigation
|
|
71
|
+
- Assistant side panel
|
|
72
|
+
- Message composer
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# Assistant page
|
|
2
|
+
|
|
3
|
+
The same conversation opened as a full page, with Answer and Records tabs and a Sources panel listing every record the answer used.
|
|
4
|
+
|
|
5
|
+
Kind: component · Group: AI and agents · Status: built, import from @twoux/ui
|
|
6
|
+
|
|
7
|
+
## When to use
|
|
8
|
+
|
|
9
|
+
- Answers that rely on many records and need checking. The Sources panel lists every record so people can verify without hunting.
|
|
10
|
+
- Research-style questions across the whole workspace, such as Thorough answers that also read candidates and past listings.
|
|
11
|
+
- When the person wants to keep the answer as the main task, not as help beside a table.
|
|
12
|
+
|
|
13
|
+
## When not to use
|
|
14
|
+
|
|
15
|
+
- A quick question about the rows on screen. Use the Assistant side panel (assistant-panel) so the table stays visible.
|
|
16
|
+
- Watching an agent work through steps over time. Use the Agent run timeline (agent-run).
|
|
17
|
+
- Browsing past conversations. Use Previous conversations (conversation-history).
|
|
18
|
+
|
|
19
|
+
## Structure
|
|
20
|
+
|
|
21
|
+
1. Top bar (Answer and Records tabs, Share, Open in side panel)
|
|
22
|
+
2. Centred reading column (thread)
|
|
23
|
+
3. Records tab (the rows the answer used, as a table)
|
|
24
|
+
4. Sources panel (every cited record)
|
|
25
|
+
5. Message box
|
|
26
|
+
|
|
27
|
+
## Guidelines
|
|
28
|
+
|
|
29
|
+
- Expand opens the same conversation; "Open in side panel" goes back without losing the thread, context or scroll position.
|
|
30
|
+
- Use a centred reading column and align messages user to the end, assistant to the start, on full, dedicated surfaces.
|
|
31
|
+
- The Sources panel lists every record the answer used. Selecting a citation highlights its source in the panel.
|
|
32
|
+
- The Records tab shows the rows behind the answer as a table that can open as a filtered view, so the claim and the data sit one click apart.
|
|
33
|
+
- Say when there are no sources rather than showing filler: empty states keep trust.
|
|
34
|
+
- Long task conversations get an outline: a list of key moments (question asked, values given, changes applied) that jumps to each message.
|
|
35
|
+
- Long drafts open here beside the conversation rather than in a second panel.
|
|
36
|
+
|
|
37
|
+
## Content
|
|
38
|
+
|
|
39
|
+
- Tabs are nouns: Answer and Records (with a count). Avoid branded names for modes or tabs.
|
|
40
|
+
- Source rows show the record ID, title and what was used (for example "Views and applies, last 30 days").
|
|
41
|
+
- Keep the same caveat and origin label as in the panel so the two surfaces read as one product.
|
|
42
|
+
|
|
43
|
+
## Accessibility
|
|
44
|
+
|
|
45
|
+
- Answer and Records use the tabs pattern: role="tablist", role="tab" with aria-selected and aria-controls, arrow keys move between tabs.
|
|
46
|
+
- The Sources panel is an aside labelled "Sources" so it is reachable as a landmark.
|
|
47
|
+
- Highlighting a source after a citation is selected must not rely on colour alone; add an outline or marker (SC 1.4.1).
|
|
48
|
+
- New answers are announced through the thread's log region without moving focus (SC 4.1.3).
|
|
49
|
+
|
|
50
|
+
## Keyboard
|
|
51
|
+
|
|
52
|
+
| Keys | Action |
|
|
53
|
+
|---|---|
|
|
54
|
+
| Left / Right arrow | Move between Answer and Records tabs |
|
|
55
|
+
| Tab | Move from the tabs into the panel content |
|
|
56
|
+
| ⌘J / Ctrl+J | Focus the message box |
|
|
57
|
+
| Enter | Send |
|
|
58
|
+
| Esc | Close an open menu |
|
|
59
|
+
|
|
60
|
+
## Examples
|
|
61
|
+
|
|
62
|
+
### page
|
|
63
|
+
|
|
64
|
+
```tsx
|
|
65
|
+
import { AssistantMessage, AssistantPage, Citation, MessageComposer, SourcesList, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, UserMessage } from "@twoux/ui";
|
|
66
|
+
import { useState } from "react";
|
|
67
|
+
|
|
68
|
+
export const meta = { title: "The conversation as a full page", description: "A centred reading column with Answer and Records tabs, and a Sources panel listing every record the answer used. Selecting a citation highlights its source.", order: 1 };
|
|
69
|
+
|
|
70
|
+
const SOURCES = [{ id: "LST-1041", title: "Frontend Engineer", detail: "Manchester · 6 applicants" }, { id: "LST-1044", title: "Customer Success Lead", detail: "Bristol · 3 applicants" }];
|
|
71
|
+
|
|
72
|
+
export default function Example() {
|
|
73
|
+
const [active, setActive] = useState<string>();
|
|
74
|
+
return (
|
|
75
|
+
<div className="h-120 w-full overflow-auto rounded-sm border border-line bg-surface">
|
|
76
|
+
<AssistantPage
|
|
77
|
+
thread={
|
|
78
|
+
<>
|
|
79
|
+
<UserMessage>Which listings close this week with fewer than 10 applicants?</UserMessage>
|
|
80
|
+
<AssistantMessage from="24 listings">
|
|
81
|
+
<p><strong>2 listings</strong>: Frontend Engineer <Citation id="LST-1041" active={active === "LST-1041"} onOpen={setActive} /> and Customer Success Lead <Citation id="LST-1044" active={active === "LST-1044"} onOpen={setActive} />.</p>
|
|
82
|
+
</AssistantMessage>
|
|
83
|
+
</>
|
|
84
|
+
}
|
|
85
|
+
records={<Table aria-label="Records used"><TableHeader><TableRow><TableHead>Listing</TableHead><TableHead className="text-right">Applicants</TableHead></TableRow></TableHeader><TableBody>{SOURCES.map((s) => <TableRow key={s.id}><TableCell>{s.title}</TableCell><TableCell className="tabular text-right">{s.detail?.split("· ")[1]}</TableCell></TableRow>)}</TableBody></Table>}
|
|
86
|
+
sources={<SourcesList sources={SOURCES} active={active} onSelect={setActive} />}
|
|
87
|
+
composer={<MessageComposer noun={{ one: "listing", many: "listings" }} onSend={() => {}} placeholder="Ask a follow-up" />}
|
|
88
|
+
/>
|
|
89
|
+
</div>
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Related
|
|
95
|
+
|
|
96
|
+
- Assistant side panel
|
|
97
|
+
- Citations and sources
|
|
98
|
+
- Message composer
|
|
99
|
+
- Follow-ups and starters
|
|
100
|
+
- Tabs (underline)
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# Assistant side panel
|
|
2
|
+
|
|
3
|
+
A side panel, opened with ⌘J, where people ask about the records on screen and get answers grounded in the current selection and filters.
|
|
4
|
+
|
|
5
|
+
Kind: component · Group: AI and agents · Status: built, import from @twoux/ui
|
|
6
|
+
|
|
7
|
+
## When to use
|
|
8
|
+
|
|
9
|
+
- Quick questions about the listings or candidates on screen, where the answer needs the data beside it. ⌘J opens the panel with the current selection and filters already attached.
|
|
10
|
+
- When people need to invoke help without leaving the table they are working through. HAX G7 asks for efficient invocation and G8 for efficient dismissal, so the panel opens and closes with one shortcut.
|
|
11
|
+
- When an answer may lead to a change. The panel can hold a proposal next to the rows it affects, so the person can compare before applying.
|
|
12
|
+
|
|
13
|
+
## When not to use
|
|
14
|
+
|
|
15
|
+
- Long research answers that need room for sources and a records list. Use the Assistant page (assistant-page) instead, which keeps the same conversation.
|
|
16
|
+
- Repeated tasks with the same shape every week. Use the Task builder (task-builder) so the inputs are slots, not free text.
|
|
17
|
+
- Filling one value per row across a table. Use the AI column (ai-column), which writes into a generated column and cites each row.
|
|
18
|
+
- Do not add a floating chat bubble to every page. The panel opens from ⌘J or Ask in the bulk bar, where the context is.
|
|
19
|
+
|
|
20
|
+
## Structure
|
|
21
|
+
|
|
22
|
+
1. Panel bar (Assistant, conversation title, Open as a full page, Previous conversations, Share, New, Close)
|
|
23
|
+
2. Thread (questions, thinking row, answers)
|
|
24
|
+
3. Clarifying question card (docked above the message box when needed)
|
|
25
|
+
4. Message box with context chips
|
|
26
|
+
5. Caveat line
|
|
27
|
+
|
|
28
|
+
## Guidelines
|
|
29
|
+
|
|
30
|
+
- Open in the same side panel as row details, in push mode by default, so the table stays readable. Opening the assistant closes the row detail panel and the other way round.
|
|
31
|
+
- Turn the selection and active filters into context chips automatically. Removing a chip narrows what the assistant reads.
|
|
32
|
+
- Lead each answer with the figure, then a small table, then the reason. Every claim about a record carries its record ID as a citation.
|
|
33
|
+
- Changes never apply from the thread. They arrive as Suggested changes with checkboxes and one Apply button, and nothing changes until the person applies.
|
|
34
|
+
- While a response is running, the send button becomes Stop in the same place. Stopping keeps what was written and says it is incomplete.
|
|
35
|
+
- Keep the message box pinned and let only the thread scroll. Do not jump the thread to the bottom while someone is reading an older message.
|
|
36
|
+
- Show the caveat under the message box at all times: what data it uses and what to check.
|
|
37
|
+
|
|
38
|
+
## Content
|
|
39
|
+
|
|
40
|
+
- Call it Assistant. Do not use "AI-powered", "smart" or "magic". Say what it read and what it did.
|
|
41
|
+
- Empty state: say what it will use ("Ask about 2 listings. Uses what you have selected and filtered. Nothing changes until you apply it.").
|
|
42
|
+
- Caveat copy is specific: "Uses your workspace data only. Check figures before you act on them."
|
|
43
|
+
- Answers open with the answer. No greeting, no "Great question", no disclaimer about being an AI before the content.
|
|
44
|
+
- Name the conversation from the first question and show it in the panel bar, so it can be found later.
|
|
45
|
+
|
|
46
|
+
## Accessibility
|
|
47
|
+
|
|
48
|
+
- Render the panel as an aside with aria-label="Assistant" (complementary landmark). Label it because the row details panel is a second complementary landmark.
|
|
49
|
+
- The thread is a log (role="log", implicit aria-live polite) with an accessible name, so new answers are announced without moving focus (SC 4.1.3).
|
|
50
|
+
- Do not announce every streamed token. Announce the thinking step titles and the finished answer; Stop satisfies SC 2.2.2 for content that auto-updates in parallel with the table.
|
|
51
|
+
- When the panel opens, focus moves to the message box; Esc closes the innermost open layer first (menu, history, then panel) and focus returns to the trigger, as the dialog pattern does (SC 2.4.3).
|
|
52
|
+
- The send button changes its accessible name from "Send (Enter)" to "Stop" while running, which the button pattern allows when the label itself changes.
|
|
53
|
+
- The panel must not cover the focused row in push mode (SC 2.4.11).
|
|
54
|
+
|
|
55
|
+
## Keyboard
|
|
56
|
+
|
|
57
|
+
| Keys | Action |
|
|
58
|
+
|---|---|
|
|
59
|
+
| ⌘J / Ctrl+J | Open or close the assistant with the current selection as context |
|
|
60
|
+
| Enter | Send the message |
|
|
61
|
+
| Shift+Enter | New line |
|
|
62
|
+
| Esc | Close the open menu, then history, then the panel |
|
|
63
|
+
| ⌘↵ | Apply the suggested changes in focus |
|
|
64
|
+
|
|
65
|
+
## Examples
|
|
66
|
+
|
|
67
|
+
### flow
|
|
68
|
+
|
|
69
|
+
```tsx
|
|
70
|
+
import { AnswerActions, AssistantMessage, AssistantPanel, Button, Citation, FollowUps, MessageComposer, StepCard, SuggestedChanges, Table, TableBody, TableCell, TableRow, ThinkingSteps, UserMessage, toast } from "@twoux/ui";
|
|
71
|
+
import { useEffect, useState } from "react";
|
|
72
|
+
|
|
73
|
+
export const meta = { title: "Ask, propose, apply", description: "The assistant reads the selection, thinks in visible steps, answers with the figure first and record IDs, and proposes changes. Nothing changes until you apply; the step card shows progress and Undo follows.", order: 1 };
|
|
74
|
+
|
|
75
|
+
type Stage = "idle" | "thinking" | "answered" | "applying" | "applied";
|
|
76
|
+
|
|
77
|
+
export default function Example() {
|
|
78
|
+
const [open, setOpen] = useState(true);
|
|
79
|
+
const [stage, setStage] = useState<Stage>("idle");
|
|
80
|
+
const [t, setT] = useState(0);
|
|
81
|
+
const [steps, setSteps] = useState<("pending" | "done")[]>(["pending", "pending"]);
|
|
82
|
+
useEffect(() => {
|
|
83
|
+
if (stage !== "thinking") return;
|
|
84
|
+
const i = setInterval(() => setT((x) => x + 0.1), 100);
|
|
85
|
+
const d = setTimeout(() => setStage("answered"), 1800);
|
|
86
|
+
return () => { clearInterval(i); clearTimeout(d); };
|
|
87
|
+
}, [stage]);
|
|
88
|
+
useEffect(() => {
|
|
89
|
+
if (stage !== "applying") return;
|
|
90
|
+
const a = setTimeout(() => setSteps(["done", "pending"]), 600);
|
|
91
|
+
const b = setTimeout(() => { setSteps(["done", "done"]); setStage("applied"); toast.success("Extended 2 listings by 30 days", { action: { label: "Undo", onClick: () => setStage("answered") } }); }, 1200);
|
|
92
|
+
return () => { clearTimeout(a); clearTimeout(b); };
|
|
93
|
+
}, [stage]);
|
|
94
|
+
return (
|
|
95
|
+
<div className="flex h-140 w-full overflow-hidden rounded-sm border border-line bg-surface">
|
|
96
|
+
<div className="grid min-w-0 flex-1 content-start gap-2 p-4">
|
|
97
|
+
<span className="text-base text-ink-2">2 listings selected</span>
|
|
98
|
+
{!open && <Button variant="outline" size="sm" shortcut="⌘J" className="justify-self-start" onClick={() => setOpen(true)}>Ask the assistant</Button>}
|
|
99
|
+
</div>
|
|
100
|
+
<AssistantPanel caveat="Reads listings and applications, not emails. Check closing dates before you extend a listing."
|
|
101
|
+
open={open}
|
|
102
|
+
onOpenChange={setOpen}
|
|
103
|
+
title="Why are these closing with few applicants?"
|
|
104
|
+
onOpenPage={() => {}}
|
|
105
|
+
composer={<MessageComposer noun={{ one: "listing", many: "listings" }} chips={[{ id: "sel", label: "2 selected listings" }, { id: "f", label: "Status is Closing soon" }]} busy={stage === "thinking"} onStop={() => setStage("idle")} onSend={() => { setT(0); setStage("thinking"); }} />}
|
|
106
|
+
>
|
|
107
|
+
{stage === "idle" ? (
|
|
108
|
+
<p className="text-base text-ink-2">Ask about the 2 selected listings, for example “Why are these closing with few applicants?”</p>
|
|
109
|
+
) : (
|
|
110
|
+
<>
|
|
111
|
+
<UserMessage>Why are these closing with few applicants?</UserMessage>
|
|
112
|
+
<ThinkingSteps done={stage !== "thinking"} seconds={stage === "thinking" ? t : 1.8} steps={[{ title: "Reading 2 listings", detail: "LST-1041, LST-1044" }, { title: "Comparing with 22 similar listings", detail: "Same teams, last 90 days" }, { title: "Checking salary and posting dates" }]} />
|
|
113
|
+
{stage !== "thinking" && (
|
|
114
|
+
<AssistantMessage
|
|
115
|
+
from="2 listings and 22 similar ones"
|
|
116
|
+
actions={<AnswerActions text="Both close in under 5 days with 9 applicants between them." onRegenerate={() => { setT(0); setStage("thinking"); }} onFeedback={() => new Promise((r) => setTimeout(r, 400))} />}
|
|
117
|
+
followUps={<FollowUps items={["Which job boards are they on?", "Draft a shorter job description for LST-1044"]} onPick={() => {}} />}
|
|
118
|
+
>
|
|
119
|
+
<p><strong>9 applicants</strong> between them, against a median of 38 for similar listings.</p>
|
|
120
|
+
<Table aria-label="Applicants"><TableBody><TableRow><TableCell>Frontend Engineer <Citation id="LST-1041" /></TableCell><TableCell className="tabular text-right">6</TableCell></TableRow><TableRow><TableCell>Customer Success Lead <Citation id="LST-1044" /></TableCell><TableCell className="tabular text-right">3</TableCell></TableRow></TableBody></Table>
|
|
121
|
+
<p>Both were posted 9 days ago and close within 5 days. Similar listings stay open 30 days.</p>
|
|
122
|
+
</AssistantMessage>
|
|
123
|
+
)}
|
|
124
|
+
{stage === "answered" && <SuggestedChanges changes={[{ id: "a", label: "Extend Frontend Engineer by 30 days", detail: "LST-1041", before: "Closes 21 Nov", after: "Closes 21 Dec" }, { id: "b", label: "Extend Customer Success Lead by 30 days", detail: "LST-1044", before: "Closes 23 Nov", after: "Closes 23 Dec" }]} onApply={() => { setSteps(["pending", "pending"]); setStage("applying"); }} onDismiss={() => setStage("idle")} />}
|
|
125
|
+
{(stage === "applying" || stage === "applied") && <StepCard title="Extending 2 listings" steps={[{ label: "Frontend Engineer closes 21 Dec", state: steps[0] }, { label: "Customer Success Lead closes 23 Dec", state: steps[1] }]} />}
|
|
126
|
+
</>
|
|
127
|
+
)}
|
|
128
|
+
</AssistantPanel>
|
|
129
|
+
</div>
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Related
|
|
135
|
+
|
|
136
|
+
- Assistant page
|
|
137
|
+
- Message composer
|
|
138
|
+
- Thinking and steps
|
|
139
|
+
- Suggested changes
|
|
140
|
+
- Citations and sources
|
|
141
|
+
- Previous conversations
|
|
142
|
+
- Side panel (drawer)
|