altitude-design 0.4.0 → 0.4.2
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/README.md +1 -0
- package/dist/guidance/components/AILabel/README.md +35 -0
- package/dist/guidance/components/Accordion/README.md +25 -0
- package/dist/guidance/components/AgentFleet/README.md +48 -1
- package/dist/guidance/components/AgentLanes/README.md +32 -0
- package/dist/guidance/components/AgentRequests/README.md +47 -0
- package/dist/guidance/components/Alert/README.md +49 -0
- package/dist/guidance/components/AltitudeProvider/README.md +63 -0
- package/dist/guidance/components/AppShell/README.md +70 -0
- package/dist/guidance/components/AreaChart/README.md +58 -0
- package/dist/guidance/components/Artifact/README.md +69 -0
- package/dist/guidance/components/Avatar/README.md +41 -0
- package/dist/guidance/components/Await/README.md +48 -0
- package/dist/guidance/components/Badge/README.md +44 -0
- package/dist/guidance/components/BarChart/README.md +62 -0
- package/dist/guidance/components/BarList/README.md +58 -0
- package/dist/guidance/components/Board/README.md +67 -0
- package/dist/guidance/components/Breadcrumb/README.md +31 -0
- package/dist/guidance/components/Button/README.md +41 -0
- package/dist/guidance/components/Card/README.md +38 -0
- package/dist/guidance/components/ChartBrush/README.md +57 -0
- package/dist/guidance/components/ChartContainer/README.md +77 -0
- package/dist/guidance/components/ChartLegend/README.md +43 -0
- package/dist/guidance/components/ChartReference/README.md +74 -0
- package/dist/guidance/components/ChartTooltip/README.md +63 -0
- package/dist/guidance/components/Checkbox/README.md +35 -0
- package/dist/guidance/components/Checklist/README.md +66 -0
- package/dist/guidance/components/CodeBlock/README.md +44 -0
- package/dist/guidance/components/ColorPicker/README.md +39 -0
- package/dist/guidance/components/Combobox/README.md +35 -0
- package/dist/guidance/components/CommandPalette/README.md +177 -0
- package/dist/guidance/components/CommentThread/README.md +51 -0
- package/dist/guidance/components/Confirmation/README.md +60 -0
- package/dist/guidance/components/ContextMenu/README.md +51 -0
- package/dist/guidance/components/Conversation/README.md +124 -0
- package/dist/guidance/components/CopyButton/README.md +31 -0
- package/dist/guidance/components/DataGrid/README.md +75 -0
- package/dist/guidance/components/DatePicker/README.md +49 -0
- package/dist/guidance/components/Dialog/README.md +175 -0
- package/dist/guidance/components/DiffView/README.md +77 -0
- package/dist/guidance/components/DotPlot/README.md +44 -0
- package/dist/guidance/components/Drilldown/README.md +28 -0
- package/dist/guidance/components/DropZone/README.md +50 -0
- package/dist/guidance/components/DropdownMenu/README.md +44 -0
- package/dist/guidance/components/EditRegion/README.md +159 -0
- package/dist/guidance/components/EmptyState/README.md +49 -0
- package/dist/guidance/components/FeatureGate/README.md +39 -0
- package/dist/guidance/components/Field/README.md +36 -0
- package/dist/guidance/components/FilterBar/README.md +63 -0
- package/dist/guidance/components/FormattedNumber/README.md +44 -0
- package/dist/guidance/components/Heatmap/README.md +71 -0
- package/dist/guidance/components/HoldButton/README.md +32 -0
- package/dist/guidance/components/HoverCard/README.md +53 -0
- package/dist/guidance/components/InlineCitation/README.md +32 -0
- package/dist/guidance/components/InlineEdit/README.md +247 -1
- package/dist/guidance/components/Input/README.md +36 -0
- package/dist/guidance/components/InputOTP/README.md +46 -0
- package/dist/guidance/components/ItemGroup/README.md +100 -0
- package/dist/guidance/components/Kbd/README.md +27 -0
- package/dist/guidance/components/LineChart/README.md +62 -0
- package/dist/guidance/components/Link/README.md +41 -0
- package/dist/guidance/components/Markdown/README.md +81 -0
- package/dist/guidance/components/Meter/README.md +52 -0
- package/dist/guidance/components/NotificationCenter/README.md +39 -0
- package/dist/guidance/components/NumberInput/README.md +37 -0
- package/dist/guidance/components/PageHeader/README.md +45 -0
- package/dist/guidance/components/Pagination/README.md +33 -0
- package/dist/guidance/components/PasswordInput/README.md +36 -0
- package/dist/guidance/components/PermissionMatrix/README.md +40 -0
- package/dist/guidance/components/Pile/README.md +42 -0
- package/dist/guidance/components/PlanPicker/README.md +37 -0
- package/dist/guidance/components/Popover/README.md +36 -0
- package/dist/guidance/components/Progress/README.md +31 -0
- package/dist/guidance/components/PromptInput/README.md +98 -0
- package/dist/guidance/components/PropertyList/README.md +80 -0
- package/dist/guidance/components/Question/README.md +54 -0
- package/dist/guidance/components/RadioGroup/README.md +37 -0
- package/dist/guidance/components/RelativeTime/README.md +47 -0
- package/dist/guidance/components/RemovalReceipt/README.md +77 -0
- package/dist/guidance/components/Resizable/README.md +29 -0
- package/dist/guidance/components/SaveBar/README.md +38 -0
- package/dist/guidance/components/SavedViews/README.md +58 -0
- package/dist/guidance/components/ScatterChart/README.md +63 -0
- package/dist/guidance/components/Schedule/README.md +66 -0
- package/dist/guidance/components/ScrollArea/README.md +28 -0
- package/dist/guidance/components/SecretField/README.md +46 -0
- package/dist/guidance/components/Select/README.md +70 -0
- package/dist/guidance/components/Separator/README.md +44 -0
- package/dist/guidance/components/Sheet/README.md +94 -0
- package/dist/guidance/components/Skeleton/README.md +36 -0
- package/dist/guidance/components/Slider/README.md +35 -0
- package/dist/guidance/components/SortableList/README.md +34 -0
- package/dist/guidance/components/Sparkline/README.md +58 -0
- package/dist/guidance/components/SplitButton/README.md +39 -0
- package/dist/guidance/components/Stat/README.md +61 -0
- package/dist/guidance/components/Stepper/README.md +61 -0
- package/dist/guidance/components/Suggestions/README.md +58 -0
- package/dist/guidance/components/Surface/README.md +28 -0
- package/dist/guidance/components/Switch/README.md +29 -0
- package/dist/guidance/components/SyncIndicator/README.md +54 -0
- package/dist/guidance/components/Table/README.md +62 -0
- package/dist/guidance/components/Tabs/README.md +55 -0
- package/dist/guidance/components/TagInput/README.md +50 -0
- package/dist/guidance/components/Terminal/README.md +42 -0
- package/dist/guidance/components/ThreadList/README.md +73 -0
- package/dist/guidance/components/TimeInput/README.md +37 -0
- package/dist/guidance/components/Timeline/README.md +55 -0
- package/dist/guidance/components/Toaster/README.md +38 -0
- package/dist/guidance/components/Toggle/README.md +52 -0
- package/dist/guidance/components/Toolbar/README.md +69 -0
- package/dist/guidance/components/Tooltip/README.md +29 -0
- package/dist/guidance/components/Tour/README.md +44 -0
- package/dist/guidance/components/Trace/README.md +92 -0
- package/dist/guidance/components/Tree/README.md +85 -0
- package/dist/guidance/index.json +117 -117
- package/dist/guidance/llms.txt +1 -1
- package/dist/guidance/manifest.json +120 -120
- package/dist/guidance/visual/ActionsInPlace.md +5 -5
- package/dist/guidance/visual/Distinctiveness.md +6 -2
- package/dist/guidance/visual/Research.md +14 -2
- package/dist/guidance/visual/Theming.md +1 -1
- package/dist/types/agent-fleet.d.ts +28 -0
- package/dist/types/ai-label.d.ts +7 -0
- package/dist/types/app-shell.d.ts +23 -0
- package/dist/types/area-chart.d.ts +8 -0
- package/dist/types/artifact.d.ts +14 -0
- package/dist/types/bar-chart.d.ts +6 -0
- package/dist/types/bar-list.d.ts +6 -0
- package/dist/types/breadcrumb.d.ts +6 -0
- package/dist/types/chart-brush.d.ts +6 -0
- package/dist/types/chart.d.ts +29 -1
- package/dist/types/checklist.d.ts +6 -0
- package/dist/types/code-block.d.ts +6 -0
- package/dist/types/collections.d.ts +15 -0
- package/dist/types/color-picker.d.ts +6 -0
- package/dist/types/command.d.ts +13 -0
- package/dist/types/comment-thread.d.ts +11 -0
- package/dist/types/confirmation.d.ts +8 -0
- package/dist/types/context-menu.d.ts +11 -0
- package/dist/types/controls.d.ts +174 -10
- package/dist/types/conversation.d.ts +21 -0
- package/dist/types/copy-button.d.ts +6 -0
- package/dist/types/core.d.ts +31 -0
- package/dist/types/data-grid.d.ts +25 -0
- package/dist/types/date-picker.d.ts +6 -0
- package/dist/types/diff-view.d.ts +8 -0
- package/dist/types/dot-plot.d.ts +6 -0
- package/dist/types/edit-region.d.ts +23 -0
- package/dist/types/empty.d.ts +9 -0
- package/dist/types/feature-gate.d.ts +8 -0
- package/dist/types/filter-bar.d.ts +8 -0
- package/dist/types/formatted-number.d.ts +6 -0
- package/dist/types/heatmap.d.ts +6 -0
- package/dist/types/inline-citation.d.ts +6 -0
- package/dist/types/inline-edit.d.ts +7 -0
- package/dist/types/input-otp.d.ts +11 -0
- package/dist/types/item-group.d.ts +29 -0
- package/dist/types/line-chart.d.ts +6 -0
- package/dist/types/link.d.ts +8 -1
- package/dist/types/markdown.d.ts +6 -0
- package/dist/types/meter.d.ts +6 -0
- package/dist/types/nav.d.ts +6 -0
- package/dist/types/notifications.d.ts +6 -0
- package/dist/types/number-input.d.ts +6 -0
- package/dist/types/overlays.d.ts +61 -0
- package/dist/types/page-header.d.ts +14 -0
- package/dist/types/pagination.d.ts +6 -0
- package/dist/types/password-input.d.ts +6 -0
- package/dist/types/permission-matrix.d.ts +7 -0
- package/dist/types/pile.d.ts +6 -0
- package/dist/types/plan-picker.d.ts +6 -0
- package/dist/types/prompt-input.d.ts +11 -0
- package/dist/types/property-list.d.ts +17 -0
- package/dist/types/question.d.ts +11 -0
- package/dist/types/relative-time.d.ts +8 -1
- package/dist/types/removal-receipt.d.ts +18 -1
- package/dist/types/save-bar.d.ts +6 -0
- package/dist/types/saved-views.d.ts +12 -0
- package/dist/types/scatter-chart.d.ts +8 -0
- package/dist/types/schedule.d.ts +21 -0
- package/dist/types/secret-field.d.ts +6 -0
- package/dist/types/select.d.ts +12 -0
- package/dist/types/separator.d.ts +8 -1
- package/dist/types/sheet.d.ts +19 -0
- package/dist/types/sparkline.d.ts +6 -0
- package/dist/types/split-button.d.ts +7 -0
- package/dist/types/stat.d.ts +7 -0
- package/dist/types/stepper.d.ts +12 -0
- package/dist/types/suggestions.d.ts +10 -0
- package/dist/types/sync-indicator.d.ts +8 -0
- package/dist/types/tag-input.d.ts +8 -0
- package/dist/types/terminal.d.ts +6 -0
- package/dist/types/thread-list.d.ts +16 -0
- package/dist/types/time-input.d.ts +6 -0
- package/dist/types/timeline.d.ts +9 -0
- package/dist/types/toggle.d.ts +6 -0
- package/dist/types/toolbar.d.ts +20 -0
- package/dist/types/tour.d.ts +10 -0
- package/dist/types/trace.d.ts +16 -0
- package/dist/types/tree.d.ts +21 -0
- package/dist/types-cjs/agent-fleet.d.cts +28 -0
- package/dist/types-cjs/ai-label.d.cts +7 -0
- package/dist/types-cjs/app-shell.d.cts +23 -0
- package/dist/types-cjs/area-chart.d.cts +8 -0
- package/dist/types-cjs/artifact.d.cts +14 -0
- package/dist/types-cjs/bar-chart.d.cts +6 -0
- package/dist/types-cjs/bar-list.d.cts +6 -0
- package/dist/types-cjs/breadcrumb.d.cts +6 -0
- package/dist/types-cjs/chart-brush.d.cts +6 -0
- package/dist/types-cjs/chart.d.cts +29 -1
- package/dist/types-cjs/checklist.d.cts +6 -0
- package/dist/types-cjs/code-block.d.cts +6 -0
- package/dist/types-cjs/collections.d.cts +15 -0
- package/dist/types-cjs/color-picker.d.cts +6 -0
- package/dist/types-cjs/command.d.cts +13 -0
- package/dist/types-cjs/comment-thread.d.cts +11 -0
- package/dist/types-cjs/confirmation.d.cts +8 -0
- package/dist/types-cjs/context-menu.d.cts +11 -0
- package/dist/types-cjs/controls.d.cts +174 -10
- package/dist/types-cjs/conversation.d.cts +21 -0
- package/dist/types-cjs/copy-button.d.cts +6 -0
- package/dist/types-cjs/core.d.cts +31 -0
- package/dist/types-cjs/data-grid.d.cts +25 -0
- package/dist/types-cjs/date-picker.d.cts +6 -0
- package/dist/types-cjs/diff-view.d.cts +8 -0
- package/dist/types-cjs/dot-plot.d.cts +6 -0
- package/dist/types-cjs/edit-region.d.cts +23 -0
- package/dist/types-cjs/empty.d.cts +9 -0
- package/dist/types-cjs/feature-gate.d.cts +8 -0
- package/dist/types-cjs/filter-bar.d.cts +8 -0
- package/dist/types-cjs/formatted-number.d.cts +6 -0
- package/dist/types-cjs/heatmap.d.cts +6 -0
- package/dist/types-cjs/inline-citation.d.cts +6 -0
- package/dist/types-cjs/inline-edit.d.cts +7 -0
- package/dist/types-cjs/input-otp.d.cts +11 -0
- package/dist/types-cjs/item-group.d.cts +29 -0
- package/dist/types-cjs/line-chart.d.cts +6 -0
- package/dist/types-cjs/link.d.cts +8 -1
- package/dist/types-cjs/markdown.d.cts +6 -0
- package/dist/types-cjs/meter.d.cts +6 -0
- package/dist/types-cjs/nav.d.cts +6 -0
- package/dist/types-cjs/notifications.d.cts +6 -0
- package/dist/types-cjs/number-input.d.cts +6 -0
- package/dist/types-cjs/overlays.d.cts +61 -0
- package/dist/types-cjs/page-header.d.cts +14 -0
- package/dist/types-cjs/pagination.d.cts +6 -0
- package/dist/types-cjs/password-input.d.cts +6 -0
- package/dist/types-cjs/permission-matrix.d.cts +7 -0
- package/dist/types-cjs/pile.d.cts +6 -0
- package/dist/types-cjs/plan-picker.d.cts +6 -0
- package/dist/types-cjs/prompt-input.d.cts +11 -0
- package/dist/types-cjs/property-list.d.cts +17 -0
- package/dist/types-cjs/question.d.cts +11 -0
- package/dist/types-cjs/relative-time.d.cts +8 -1
- package/dist/types-cjs/removal-receipt.d.cts +18 -1
- package/dist/types-cjs/save-bar.d.cts +6 -0
- package/dist/types-cjs/saved-views.d.cts +12 -0
- package/dist/types-cjs/scatter-chart.d.cts +8 -0
- package/dist/types-cjs/schedule.d.cts +21 -0
- package/dist/types-cjs/secret-field.d.cts +6 -0
- package/dist/types-cjs/select.d.cts +12 -0
- package/dist/types-cjs/separator.d.cts +8 -1
- package/dist/types-cjs/sheet.d.cts +19 -0
- package/dist/types-cjs/sparkline.d.cts +6 -0
- package/dist/types-cjs/split-button.d.cts +7 -0
- package/dist/types-cjs/stat.d.cts +7 -0
- package/dist/types-cjs/stepper.d.cts +12 -0
- package/dist/types-cjs/suggestions.d.cts +10 -0
- package/dist/types-cjs/sync-indicator.d.cts +8 -0
- package/dist/types-cjs/tag-input.d.cts +8 -0
- package/dist/types-cjs/terminal.d.cts +6 -0
- package/dist/types-cjs/thread-list.d.cts +16 -0
- package/dist/types-cjs/time-input.d.cts +6 -0
- package/dist/types-cjs/timeline.d.cts +9 -0
- package/dist/types-cjs/toggle.d.cts +6 -0
- package/dist/types-cjs/toolbar.d.cts +20 -0
- package/dist/types-cjs/tour.d.cts +10 -0
- package/dist/types-cjs/trace.d.cts +16 -0
- package/dist/types-cjs/tree.d.cts +21 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
A design and HCI framework for agent-built React web applications, with a component foundation where **height is the interface**. Its one idea: the ground is the record. Settled content sits on the ground; anything live rises above it; anything waiting to receive sinks below it.
|
|
4
4
|
|
|
5
5
|
- **Site:** https://richlaconte.github.io/altitude/
|
|
6
|
+
- **For agents:** https://richlaconte.github.io/altitude/llms.txt, an index of the guides, task patterns and every component's guide as plain Markdown ([llmstxt.org](https://llmstxt.org)), and `llms-full.txt` beside it with every guide in one file
|
|
6
7
|
- **Storybook:** every component, with its guidelines and a live preview in both schemes: https://richlaconte.github.io/altitude/storybook/
|
|
7
8
|
- **The design guide:** [`project/README.md`](https://github.com/richlaconte/altitude/blob/main/project/README.md), then [Elevation](https://github.com/richlaconte/altitude/blob/main/project/Elevation.md), [Motion](https://github.com/richlaconte/altitude/blob/main/project/Motion.md), [Charts](https://github.com/richlaconte/altitude/blob/main/project/Charts.md), [Theming](https://github.com/richlaconte/altitude/blob/main/project/Theming.md), [Fundamentals](https://github.com/richlaconte/altitude/blob/main/project/Fundamentals.md), [Distinctiveness](https://github.com/richlaconte/altitude/blob/main/project/Distinctiveness.md) and [Research](https://github.com/richlaconte/altitude/blob/main/project/Research.md)
|
|
8
9
|
|
|
@@ -23,3 +23,38 @@ Says when content was generated or changed by AI, and explains how when asked: f
|
|
|
23
23
|
- Do say confidence in words.
|
|
24
24
|
- Don't decorate AI content with gradients, glows or shimmer.
|
|
25
25
|
- Don't use the label as the only place AI involvement is disclosed when your rules require more.
|
|
26
|
+
|
|
27
|
+
<!-- example: generated from build/previews.txt by build/previews.mjs. Edit the preview, not this section. -->
|
|
28
|
+
## Example
|
|
29
|
+
|
|
30
|
+
`AILabel` as first used in its preview:
|
|
31
|
+
|
|
32
|
+
```tsx
|
|
33
|
+
import { AILabel } from 'altitude-design';
|
|
34
|
+
|
|
35
|
+
<AILabel subject="Summary" model="Opus 5.5" generatedAt={new Date(Date.now() - 4 * 60000)} confidence="Medium: two sources disagreed about the renewal date"
|
|
36
|
+
sources={[{ title: 'Call with Jamie Lee, 28 Oct', url: '#/calls/28' }, { title: 'Contract v3.pdf', url: '#/files/contract' }]} />
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The full preview, as rendered in Storybook and checked in CI (`col`, `al`, `ttl` are the preview page's own layout classes, not Altitude's):
|
|
40
|
+
|
|
41
|
+
```tsx
|
|
42
|
+
import { AILabel } from 'altitude-design';
|
|
43
|
+
|
|
44
|
+
export default function App() {
|
|
45
|
+
return (
|
|
46
|
+
<div className="col al">
|
|
47
|
+
<div>
|
|
48
|
+
<h3 className="ttl">Summary <AILabel subject="Summary" model="Opus 5.5" generatedAt={new Date(Date.now() - 4 * 60000)} confidence="Medium: two sources disagreed about the renewal date"
|
|
49
|
+
sources={[{ title: 'Call with Jamie Lee, 28 Oct', url: '#/calls/28' }, { title: 'Contract v3.pdf', url: '#/files/contract' }]} /></h3>
|
|
50
|
+
<p>Jamie wants to move to annual billing in November and asked whether the 30-day refund applies to the remaining monthly period.</p>
|
|
51
|
+
</div>
|
|
52
|
+
<div>
|
|
53
|
+
<h3 className="ttl">Reply <AILabel subject="Reply" model="Opus 5.5" generatedAt={new Date(Date.now() - 3 * 3600000)} reviewedBy="Maya Chen" editedBy="Maya Chen" /></h3>
|
|
54
|
+
<p>Hi Jamie, thanks for getting in touch. Your full refund of €240 is on its way.</p>
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
<!-- /example -->
|
|
@@ -5,3 +5,28 @@ Reveals sections one at a time. Closed items are **lids** lying flush on the pag
|
|
|
5
5
|
Closed rows sit on the ground divided by `line` hairlines. Hovering one raises a ghost of its lid (a faint raised tint with a hairline), so you can see what will open. Opening raises the item to level 2 (`surface-raised`, `elev-2`), opens a gap around it and hides the neighbouring dividers. Inside, the content sits in a compartment: an opening sunk into the lid (`surface-well`) with its back wall showing just below the rim, the same camera as an empty `Input`. The panel grows on `ease-glide`, the content emerges 60ms later, and the chevron turns on `ease-settle`. Closed panels are inert to keyboard and assistive tech.
|
|
6
6
|
|
|
7
7
|
**You provide:** `items` (`value`, `title`, `content`), `type` (`single` closes the others; `multiple` doesn't), `defaultValue` as an array of open values.
|
|
8
|
+
|
|
9
|
+
<!-- example: generated from build/previews.txt by build/previews.mjs. Edit the preview, not this section. -->
|
|
10
|
+
## Example
|
|
11
|
+
|
|
12
|
+
`Accordion` as first used in its preview:
|
|
13
|
+
|
|
14
|
+
```tsx
|
|
15
|
+
import { Accordion } from 'altitude-design';
|
|
16
|
+
|
|
17
|
+
<Accordion items={items} defaultValue={['b']} />
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
The full preview, as rendered in Storybook and checked in CI (`wrap` is the preview page's own layout class, not Altitude's):
|
|
21
|
+
|
|
22
|
+
```tsx
|
|
23
|
+
import { Accordion } from 'altitude-design';
|
|
24
|
+
|
|
25
|
+
const items = [
|
|
26
|
+
{ value: 'a', title: 'What does elevation mean here?', content: 'Height says how much something matters right now and what you can do with it: raised things are pushed, sunk things are filled.' },
|
|
27
|
+
{ value: 'b', title: 'Can I bring my own colour?', content: 'Pass any hex as accent to AltitudeProvider. The accent text and soft tints are derived to stay legible on every surface.' },
|
|
28
|
+
{ value: 'c', title: 'Does it respect reduced motion?', content: 'Yes. Surfaces still change level, tint and shadow, but nothing travels or scales.' },
|
|
29
|
+
];
|
|
30
|
+
export default function App() { return <div className="wrap"><Accordion items={items} defaultValue={['b']} /></div>; }
|
|
31
|
+
```
|
|
32
|
+
<!-- /example -->
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# AgentFleet
|
|
2
2
|
|
|
3
|
-
Every agent working on one job, as a roster: grouped by what they need from you (needs you, working, done), each row saying the agent's state in words, what it's doing in one line, how long it has waited or run, where it is in its plan and what it has spent, with warnings for trouble that's coming (two agents changing one file, an agent making no progress). For supervising several agents at once; see [the research](https://github.com/richlaconte/altitude/blob/v0.4.
|
|
3
|
+
Every agent working on one job, as a roster: grouped by what they need from you (needs you, working, done), each row saying the agent's state in words, what it's doing in one line, how long it has waited or run, where it is in its plan and what it has spent, with warnings for trouble that's coming (two agents changing one file, an agent making no progress). For supervising several agents at once; see [the research](https://github.com/richlaconte/altitude/blob/v0.4.2/docs/research/agent-orchestration/README.md).
|
|
4
4
|
|
|
5
5
|
**Elevation: a record of agents, with live rows.**
|
|
6
6
|
- **Rows** are the record: flush at rest, lifting one level when pointed at because selecting one opens it. The open agent is selected, as a table's row is: `accent-soft`, raised one level, `aria-current`.
|
|
@@ -32,3 +32,50 @@ const row = (a) => <AgentFleetItem key={a.id} {...a} selected={a.id === open} on
|
|
|
32
32
|
- Do say every state in words; the lamp and mark only repeat it.
|
|
33
33
|
- Don't show a spinner, a ticking counter or make a working row hover.
|
|
34
34
|
- Don't lay agents out as a grid of equal cards: the one that needs you gets lost among the ones that don't.
|
|
35
|
+
|
|
36
|
+
<!-- example: generated from build/previews.txt by build/previews.mjs. Edit the preview, not this section. -->
|
|
37
|
+
## Example
|
|
38
|
+
|
|
39
|
+
`AgentFleet` as first used in its preview:
|
|
40
|
+
|
|
41
|
+
```tsx
|
|
42
|
+
import { AgentFleet, AgentFleetGroup, summarizeFleet } from 'altitude-design';
|
|
43
|
+
|
|
44
|
+
<AgentFleet now={now} summary={summarizeFleet(seed.map((a) => a.state))}>
|
|
45
|
+
<AgentFleetGroup label="Needs you" empty="Nothing needs you.">{g.needsYou.map(row)}</AgentFleetGroup>
|
|
46
|
+
<AgentFleetGroup label="Working">{g.working.map(row)}</AgentFleetGroup>
|
|
47
|
+
<AgentFleetGroup label="Done" collapsible>{g.finished.map(row)}</AgentFleetGroup>
|
|
48
|
+
</AgentFleet>
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
The full preview, as rendered in Storybook and checked in CI (`af` is the preview page's own layout class, not Altitude's):
|
|
52
|
+
|
|
53
|
+
```tsx
|
|
54
|
+
import * as React from 'react';
|
|
55
|
+
import { AgentFleet, AgentFleetGroup, AgentFleetItem, groupAgents, summarizeFleet } from 'altitude-design';
|
|
56
|
+
|
|
57
|
+
const now = Date.UTC(2026, 9, 31, 14, 20), min = 60000;
|
|
58
|
+
const seed = [
|
|
59
|
+
{ id: 'api', name: 'Billing API', state: 'needs-input', needs: 'approval', activity: 'Wants to run the migration on staging', since: now - 6 * min, startedAt: now - 17 * min, step: { current: 3, total: 4 }, cost: '$0.38' },
|
|
60
|
+
{ id: 'qa', name: 'QA', state: 'failed', activity: '2 of 14 checkout tests failed', since: now - 2 * min, startedAt: now - 5 * min, cost: '$0.11' },
|
|
61
|
+
{ id: 'docs', name: 'Docs', state: 'stalled', stalledReason: 'Retried the same step 3 times', activity: 'Link check timed out on /enterprise; retrying', since: now - 5 * min, startedAt: now - 17 * min, step: { current: 3, total: 3 }, cost: '$0.31' },
|
|
62
|
+
{ id: 'pricing', name: 'Pricing page', state: 'working', activity: 'Writing the annual toggle in Pricing.tsx', startedAt: now - 17 * min, step: { current: 2, total: 4 }, cost: '$0.41', conflict: { file: 'src/content/pricing.ts', with: 'Docs' } },
|
|
63
|
+
{ id: 'changelog', name: 'Changelog', state: 'blocked', blockedBy: 'Pricing page' },
|
|
64
|
+
{ id: 'emails', name: 'Renewal emails', state: 'done', activity: 'Scheduled for Tuesday 9:00 to 1,240 customers', startedAt: now - 17 * min, endedAt: now - 3 * min, cost: '$0.29' },
|
|
65
|
+
];
|
|
66
|
+
export default function App() {
|
|
67
|
+
const [open, setOpen] = React.useState('pricing');
|
|
68
|
+
const g = groupAgents(seed, (a) => a);
|
|
69
|
+
const row = (a) => <AgentFleetItem key={a.id} {...a} selected={a.id === open} onSelect={() => setOpen(a.id)} />;
|
|
70
|
+
return (
|
|
71
|
+
<div className="af">
|
|
72
|
+
<AgentFleet now={now} summary={summarizeFleet(seed.map((a) => a.state))}>
|
|
73
|
+
<AgentFleetGroup label="Needs you" empty="Nothing needs you.">{g.needsYou.map(row)}</AgentFleetGroup>
|
|
74
|
+
<AgentFleetGroup label="Working">{g.working.map(row)}</AgentFleetGroup>
|
|
75
|
+
<AgentFleetGroup label="Done" collapsible>{g.finished.map(row)}</AgentFleetGroup>
|
|
76
|
+
</AgentFleet>
|
|
77
|
+
</div>
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
<!-- /example -->
|
|
@@ -19,3 +19,35 @@ Where each agent's time went, one lane per agent on a shared clock: working, wai
|
|
|
19
19
|
|
|
20
20
|
- Do keep it below the roster: it explains the past, the roster and requests are for now.
|
|
21
21
|
- Don't draw dependencies as arrows between lanes; say them on the waiting agent ("Waiting on Billing API").
|
|
22
|
+
|
|
23
|
+
<!-- example: generated from build/previews.txt by build/previews.mjs. Edit the preview, not this section. -->
|
|
24
|
+
## Example
|
|
25
|
+
|
|
26
|
+
`AgentLanes` as first used in its preview:
|
|
27
|
+
|
|
28
|
+
```tsx
|
|
29
|
+
import { AgentLanes } from 'altitude-design';
|
|
30
|
+
|
|
31
|
+
<AgentLanes lanes={lanes} start={t0} now={m(31)} selected={sel} onSelect={setSel} />
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
The full preview, as rendered in Storybook and checked in CI (`al` is the preview page's own layout class, not Altitude's):
|
|
35
|
+
|
|
36
|
+
```tsx
|
|
37
|
+
import * as React from 'react';
|
|
38
|
+
import { AgentLanes } from 'altitude-design';
|
|
39
|
+
|
|
40
|
+
const t0 = Date.UTC(2026, 9, 31, 14, 0), m = (n) => t0 + n * 60000;
|
|
41
|
+
const lanes = [
|
|
42
|
+
{ id: 'pricing', name: 'Pricing page', segments: [{ from: m(3), to: m(20), state: 'working' }, { from: m(20), to: m(25), state: 'waiting' }] },
|
|
43
|
+
{ id: 'api', name: 'Billing API', segments: [{ from: m(3), to: m(7), state: 'working' }, { from: m(7), to: m(10), state: 'waiting' }, { from: m(10), to: m(19), state: 'working' }] },
|
|
44
|
+
{ id: 'emails', name: 'Renewal emails', segments: [{ from: m(3), to: m(8), state: 'working' }, { from: m(8), to: m(13), state: 'waiting' }, { from: m(13), to: m(25), state: 'working' }] },
|
|
45
|
+
{ id: 'qa', name: 'QA', segments: [{ from: m(3), to: m(19), state: 'blocked' }, { from: m(19), to: m(21), state: 'working' }, { from: m(21), to: m(23), state: 'failed' }, { from: m(23), to: m(29), state: 'working' }] },
|
|
46
|
+
{ id: 'changelog', name: 'Changelog', segments: [{ from: m(27), to: m(30), state: 'working' }] },
|
|
47
|
+
];
|
|
48
|
+
export default function App() {
|
|
49
|
+
const [sel, setSel] = React.useState('qa');
|
|
50
|
+
return <div className="al"><AgentLanes lanes={lanes} start={t0} now={m(31)} selected={sel} onSelect={setSel} /></div>;
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
<!-- /example -->
|
|
@@ -27,3 +27,50 @@ Every decision your agents are waiting on, in one queue: approvals, questions, r
|
|
|
27
27
|
- Do say the consequence of an approval in `detail`.
|
|
28
28
|
- Don't offer "approve all": answer each with its consequence in view.
|
|
29
29
|
- Don't make an answer primary; the view's one primary stays where it is.
|
|
30
|
+
|
|
31
|
+
<!-- example: generated from build/previews.txt by build/previews.mjs. Edit the preview, not this section. -->
|
|
32
|
+
## Example
|
|
33
|
+
|
|
34
|
+
`AgentRequests` as first used in its preview:
|
|
35
|
+
|
|
36
|
+
```tsx
|
|
37
|
+
import { AgentRequest, AgentRequests, Button } from 'altitude-design';
|
|
38
|
+
|
|
39
|
+
<AgentRequests now={now}>
|
|
40
|
+
{left.map((r) => (
|
|
41
|
+
<AgentRequest key={r.id} agent={r.agent} kind={r.kind} since={r.since} title={r.title} detail={r.detail} onOpen={() => {}}
|
|
42
|
+
actions={<>{r.answers.map((a) => <Button key={a} size="sm" variant="secondary" onClick={() => done(r.id)}>{a}</Button>)}
|
|
43
|
+
{r.decline && <Button size="sm" variant="ghost" onClick={() => done(r.id)}>{r.decline}</Button>}</>} />
|
|
44
|
+
))}
|
|
45
|
+
</AgentRequests>
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The full preview, as rendered in Storybook and checked in CI (`ar` is the preview page's own layout class, not Altitude's):
|
|
49
|
+
|
|
50
|
+
```tsx
|
|
51
|
+
import * as React from 'react';
|
|
52
|
+
import { AgentRequest, AgentRequests, Button } from 'altitude-design';
|
|
53
|
+
|
|
54
|
+
const now = Date.UTC(2026, 9, 31, 14, 20), min = 60000;
|
|
55
|
+
const seed = [
|
|
56
|
+
{ id: 'emails', agent: 'Renewal emails', kind: 'question', since: now - 4 * min, title: 'Send the offer to every monthly customer, or only the active ones?', answers: ['Active only', 'Everyone'] },
|
|
57
|
+
{ id: 'api', agent: 'Billing API', kind: 'approval', since: now - 9 * min, title: 'Run the plans-table migration on staging?', detail: 'Adds annual price columns to 4 plans on staging. Production isn\'t touched.', answers: ['Run migration'], decline: "Don't run" },
|
|
58
|
+
{ id: 'qa', agent: 'QA', kind: 'failed', since: now - 1 * min, title: 'Checkout tests failed', detail: '2 of 14 failed: the annual total shows $290/yr, expected $290/year.', answers: ['Retry'] },
|
|
59
|
+
];
|
|
60
|
+
export default function App() {
|
|
61
|
+
const [left, setLeft] = React.useState(seed);
|
|
62
|
+
const done = (id) => setLeft((l) => l.filter((r) => r.id !== id));
|
|
63
|
+
return (
|
|
64
|
+
<div className="ar">
|
|
65
|
+
<AgentRequests now={now}>
|
|
66
|
+
{left.map((r) => (
|
|
67
|
+
<AgentRequest key={r.id} agent={r.agent} kind={r.kind} since={r.since} title={r.title} detail={r.detail} onOpen={() => {}}
|
|
68
|
+
actions={<>{r.answers.map((a) => <Button key={a} size="sm" variant="secondary" onClick={() => done(r.id)}>{a}</Button>)}
|
|
69
|
+
{r.decline && <Button size="sm" variant="ghost" onClick={() => done(r.id)}>{r.decline}</Button>}</>} />
|
|
70
|
+
))}
|
|
71
|
+
</AgentRequests>
|
|
72
|
+
</div>
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
<!-- /example -->
|
|
@@ -20,3 +20,52 @@ Several alerts as a **stack of sheets**, the same language as `Pile`. The first
|
|
|
20
20
|
- Keep it to a title and one sentence.
|
|
21
21
|
- Generic action-completion messages belong in a Toast. When an action removes an item from a list (‘Brand guidelines.pdf archived’ with Undo), don't add an Alert below the list: show the result in the item's own slot with [RemovalReceipt](../RemovalReceipt/README.md), then hand Undo to a Toast. Keep a durable recovery destination such as Archived → Restore; a timed notification must not be the only recovery path. See [Actions in place](../../visual/ActionsInPlace.md).
|
|
22
22
|
- An Alert still suits a failure or a changed record that can't be shown beside its item (it's outside the current view). Key it by its operation ID, and let its delayed `onDismiss` clear state only when that ID is still current. Dismissing an old Alert must not hide a newer action's feedback.
|
|
23
|
+
|
|
24
|
+
<!-- example: generated from build/previews.txt by build/previews.mjs. Edit the preview, not this section. -->
|
|
25
|
+
## Example
|
|
26
|
+
|
|
27
|
+
`Alert` as first used in its preview:
|
|
28
|
+
|
|
29
|
+
```tsx
|
|
30
|
+
import { Alert, Button } from 'altitude-design';
|
|
31
|
+
|
|
32
|
+
<Alert placement="page" tone="caution" variant="raised" title="Your trial ends in 3 days" action={<Button variant="primary" size="sm">Choose a plan</Button>}
|
|
33
|
+
onDismiss={() => setTrial(false)} dismissLabel="Remind me tomorrow" />
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
The full preview, as rendered in Storybook and checked in CI (`col`, `frame`, `cap`, `wrap` are the preview page's own layout classes, not Altitude's):
|
|
37
|
+
|
|
38
|
+
```tsx
|
|
39
|
+
import * as React from 'react';
|
|
40
|
+
import { Alert, AlertStack, Button } from 'altitude-design';
|
|
41
|
+
|
|
42
|
+
const queue = [
|
|
43
|
+
{ id: 'b', tone: 'critical', title: 'Build failed', body: 'Missing environment variable DATABASE_URL.' },
|
|
44
|
+
{ id: 'c', tone: 'caution', title: 'Certificate renews in 3 days', body: 'Renewal is automatic; nothing to do.' },
|
|
45
|
+
{ id: 'd', tone: 'accent', title: 'New region available', body: 'Frankfurt (eu-central-2) is now open.' },
|
|
46
|
+
];
|
|
47
|
+
export default function App() {
|
|
48
|
+
const [items, setItems] = React.useState(queue);
|
|
49
|
+
React.useEffect(() => { if (!items.length) { const t = setTimeout(() => setItems(queue), 900); return () => clearTimeout(t); } }, [items]);
|
|
50
|
+
const [trial, setTrial] = React.useState(true);
|
|
51
|
+
return (
|
|
52
|
+
<div className="col">
|
|
53
|
+
<div className="frame">
|
|
54
|
+
{trial && <Alert placement="page" tone="caution" variant="raised" title="Your trial ends in 3 days" action={<Button variant="primary" size="sm">Choose a plan</Button>}
|
|
55
|
+
onDismiss={() => setTrial(false)} dismissLabel="Remind me tomorrow" />}
|
|
56
|
+
<Alert placement="page" title="We're seeing delays in email delivery">Messages may take up to 15 minutes to arrive.</Alert>
|
|
57
|
+
<main tabIndex={-1}><p className="cap">Page banners lie edge to edge across the top. One that asks for action is raised; one that informs is a flat sheet.</p></main>
|
|
58
|
+
</div>
|
|
59
|
+
<div className="col wrap">
|
|
60
|
+
<Alert title="Scheduled maintenance">The API will be read-only on Sunday from 02:00 to 02:30 UTC.</Alert>
|
|
61
|
+
<Alert tone="positive" title="Domain verified">acme.dev now points at this project.</Alert>
|
|
62
|
+
<Alert tone="caution" variant="raised" title="You're at 90% of your plan" action={<Button size="sm" variant="primary">Upgrade</Button>} />
|
|
63
|
+
<AlertStack>
|
|
64
|
+
{items.map((a) => <Alert key={a.id} tone={a.tone} title={a.title} onDismiss={() => setItems((xs) => xs.filter((x) => x.id !== a.id))}>{a.body}</Alert>)}
|
|
65
|
+
</AlertStack>
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
<!-- /example -->
|
|
@@ -46,3 +46,66 @@ Nothing moves: a mode is there from load. Entering it is announced once, politel
|
|
|
46
46
|
|
|
47
47
|
Law 1 says the primary action rests at level 2, one per view. With `devChecks` (on by default when `process.env.NODE_ENV` isn't `'production'`), a `Button variant="primary"` registers with the provider while it's mounted. When more than one *visible* primary sits in the same view, one grouped warning names them and law 1. The page is one view, and each `Dialog`, `Sheet` and `Popover` is its own. Wrap independent parts (cards on a dashboard, each with its own task) in `<PrimaryScope>` to make them views of their own. The check is a warning only, it's gone from production builds, and the rule is `ALT-VISUAL-002` (`altitude explain ALT-VISUAL-002`). `PageHeader`'s `primary` slot is where a page's one primary belongs.
|
|
48
48
|
|
|
49
|
+
<!-- example: generated from build/previews.txt by build/previews.mjs. Edit the preview, not this section. -->
|
|
50
|
+
## Example
|
|
51
|
+
|
|
52
|
+
`AltitudeProvider` as first used in its preview:
|
|
53
|
+
|
|
54
|
+
```tsx
|
|
55
|
+
import { AltitudeProvider, Badge, Button, Input, Switch } from 'altitude-design';
|
|
56
|
+
|
|
57
|
+
<AltitudeProvider key={i} scheme={s} accent={accent} className="shell"><div className="scheme">
|
|
58
|
+
<h4>{typeof s === 'string' ? s[0].toUpperCase() + s.slice(1) : 'Custom'}</h4>
|
|
59
|
+
<small>{accent ? 'accent="' + accent + '"' : typeof s === 'string' ? 'scheme="' + s + '"' : 'scheme={paper}'}</small>
|
|
60
|
+
<Button variant="primary" size="sm">Publish</Button>
|
|
61
|
+
<Input size="sm" placeholder="Search" aria-label="Search" />
|
|
62
|
+
<div style={{ display: 'flex', gap: 10, alignItems: 'center' }}><Switch defaultChecked aria-label="Enabled" /><Badge tone="accent">Beta</Badge></div>
|
|
63
|
+
<div className="series">{[1, 2, 3, 4, 5, 6].map((n) => <span key={n} style={{ background: `var(--data-${n})` }} />)}<small>{accent ? 'data-1…6, led by the accent' : 'data-1…6'}</small></div>
|
|
64
|
+
</div></AltitudeProvider>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
The full preview, as rendered in Storybook and checked in CI (`schemes`, `shell`, `scheme`, `series`, `density` are the preview page's own layout classes, not Altitude's):
|
|
68
|
+
|
|
69
|
+
```tsx
|
|
70
|
+
import { AltitudeProvider, Badge, Button, Input, Switch, Tabs, TabsList, TabsTrigger } from 'altitude-design';
|
|
71
|
+
|
|
72
|
+
// A custom scheme: warm paper grounds and green ink laid over daylight. checkScheme() passes it.
|
|
73
|
+
const paper = { name: 'paper', base: 'daylight', tokens: {
|
|
74
|
+
'surface-trench': '#e5e1d2', 'surface-well': '#edeadf', 'surface-ground': '#f5f3ea', 'surface-raised': '#fffdf7',
|
|
75
|
+
'surface-lifted': '#fffdf8', 'surface-floating': '#fffefa', 'surface-overlay': '#fffefb', 'surface-modal': '#fffefc',
|
|
76
|
+
ink: '#1f3329', 'ink-muted': '#4b5d53', 'ink-faint': '#949b8e', line: '#e2ded0', 'line-control': '#76806f',
|
|
77
|
+
accent: '#284d39', 'on-accent': '#ffffff', 'accent-ink': '#284d39', 'accent-soft': '#e1e9dc', positive: '#2c6e3f',
|
|
78
|
+
} };
|
|
79
|
+
const list = [['daylight'], ['nightfall'], ['daylight', '#0f8a6b'], [paper]];
|
|
80
|
+
export default function App() {
|
|
81
|
+
return (
|
|
82
|
+
<>
|
|
83
|
+
<div className="schemes">
|
|
84
|
+
{list.map(([s, accent], i) => (
|
|
85
|
+
<AltitudeProvider key={i} scheme={s} accent={accent} className="shell"><div className="scheme">
|
|
86
|
+
<h4>{typeof s === 'string' ? s[0].toUpperCase() + s.slice(1) : 'Custom'}</h4>
|
|
87
|
+
<small>{accent ? 'accent="' + accent + '"' : typeof s === 'string' ? 'scheme="' + s + '"' : 'scheme={paper}'}</small>
|
|
88
|
+
<Button variant="primary" size="sm">Publish</Button>
|
|
89
|
+
<Input size="sm" placeholder="Search" aria-label="Search" />
|
|
90
|
+
<div style={{ display: 'flex', gap: 10, alignItems: 'center' }}><Switch defaultChecked aria-label="Enabled" /><Badge tone="accent">Beta</Badge></div>
|
|
91
|
+
<div className="series">{[1, 2, 3, 4, 5, 6].map((n) => <span key={n} style={{ background: `var(--data-${n})` }} />)}<small>{accent ? 'data-1…6, led by the accent' : 'data-1…6'}</small></div>
|
|
92
|
+
</div></AltitudeProvider>
|
|
93
|
+
))}
|
|
94
|
+
</div>
|
|
95
|
+
{/* Density (#245): 'auto' follows the pointer; 'compact' pins the mouse sizes, 'comfortable' the touch sizes. */}
|
|
96
|
+
<div className="density">
|
|
97
|
+
{['compact', 'comfortable'].map((d) => (
|
|
98
|
+
<AltitudeProvider key={d} density={d} className="shell"><div className="scheme">
|
|
99
|
+
<h4>{d === 'compact' ? 'Compact' : 'Comfortable'}</h4>
|
|
100
|
+
<small>density="{d}"</small>
|
|
101
|
+
<div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}><Button variant="primary">Save</Button><Button>Cancel</Button></div>
|
|
102
|
+
<Input placeholder="Search" aria-label="Search" />
|
|
103
|
+
<Tabs defaultValue="a"><TabsList aria-label={'Sections, ' + d}><TabsTrigger value="a">General</TabsTrigger><TabsTrigger value="b">Members</TabsTrigger></TabsList></Tabs>
|
|
104
|
+
</div></AltitudeProvider>
|
|
105
|
+
))}
|
|
106
|
+
</div>
|
|
107
|
+
</>
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
<!-- /example -->
|
|
@@ -49,3 +49,73 @@ The frame of an app: a sidebar of destinations (grouped, collapsible), a top bar
|
|
|
49
49
|
- Do say what a count counts (`label="4 unread"`).
|
|
50
50
|
- Don't raise the sidebar into a card or give it a shadow.
|
|
51
51
|
- Don't put actions in the sidebar's destinations; they navigate.
|
|
52
|
+
|
|
53
|
+
<!-- example: generated from build/previews.txt by build/previews.mjs. Edit the preview, not this section. -->
|
|
54
|
+
## Example
|
|
55
|
+
|
|
56
|
+
`AppShell` as first used in its preview:
|
|
57
|
+
|
|
58
|
+
```tsx
|
|
59
|
+
import { AppShell, Avatar, Button, DropdownMenu, Kbd, PageHeader, PageHeaderDescription, PageHeaderTitle, Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarHeader, SidebarMenu, SidebarMenuButton, SidebarMenuItem } from 'altitude-design';
|
|
60
|
+
|
|
61
|
+
<AppShell
|
|
62
|
+
sidebar={
|
|
63
|
+
<Sidebar>
|
|
64
|
+
<SidebarHeader>
|
|
65
|
+
<DropdownMenu items={[{ label: 'Acme Inc.' }, { label: 'Northwind' }, { type: 'separator' }, { label: 'Create workspace' }]}
|
|
66
|
+
trigger={<button className="as-ws"><Avatar name="Acme Inc." size={24} /><span className="alt-sidebar-text">Acme Inc.</span></button>} />
|
|
67
|
+
</SidebarHeader>
|
|
68
|
+
<SidebarContent>
|
|
69
|
+
<SidebarGroup label="Workspace"><SidebarMenu>{item('inbox', 'Inbox', 4)}{item('projects', 'Projects')}{item('reports', 'Reports')}</SidebarMenu></SidebarGroup>
|
|
70
|
+
<SidebarGroup label="Admin" collapsible><SidebarMenu>{item('members', 'Members')}{item('billing', 'Billing')}{item('settings', 'Settings')}</SidebarMenu></SidebarGroup>
|
|
71
|
+
</SidebarContent>
|
|
72
|
+
<SidebarFooter><SidebarMenu><SidebarMenuItem><SidebarMenuButton href="#/me" icon={<Avatar name="Maya Chen" size={20} />}>Maya Chen</SidebarMenuButton></SidebarMenuItem></SidebarMenu></SidebarFooter>
|
|
73
|
+
</Sidebar>
|
|
74
|
+
}
|
|
75
|
+
topBar={<Button variant="secondary" size="sm" className="search">Search… <Kbd>⌘K</Kbd></Button>}>
|
|
76
|
+
<PageHeader primary={<Button variant="primary">New message</Button>}><PageHeaderTitle>{at[0].toUpperCase() + at.slice(1)}</PageHeaderTitle><PageHeaderDescription>The sidebar's current destination sits on a raised pill that glides as you move. ⌘B collapses it to a rail.</PageHeaderDescription></PageHeader>
|
|
77
|
+
<div className="rows">{Array.from({ length: 8 }, (_, i) => <div key={i} />)}</div>
|
|
78
|
+
</AppShell>
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
The full preview, as rendered in Storybook and checked in CI (`as`, `as-ws`, `search`, `rows` are the preview page's own layout classes, not Altitude's):
|
|
82
|
+
|
|
83
|
+
```tsx
|
|
84
|
+
import * as React from 'react';
|
|
85
|
+
import { AppShell, Avatar, Button, DropdownMenu, Kbd, PageHeader, PageHeaderDescription, PageHeaderTitle, Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarHeader, SidebarMenu, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem } from 'altitude-design';
|
|
86
|
+
|
|
87
|
+
const I = ({ d }) => <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round"><path d={d} /></svg>;
|
|
88
|
+
const icons = { inbox: 'M3 13h5l2 3h4l2-3h5M5 5h14l2 8v6H3v-6z', projects: 'M3 7h7l2 2h9v10H3z', reports: 'M5 20V10M12 20V4M19 20v-7', members: 'M16 19v-1a4 4 0 0 0-8 0v1M12 11a3 3 0 1 0 0-6 3 3 0 0 0 0 6', billing: 'M3 7h18v10H3zM3 11h18', settings: 'M12 15a3 3 0 1 0 0-6 3 3 0 0 0 0 6M19 12l2-1-2-4-2 1-2-1-1-2h-4l-1 2-2 1-2-1-2 4 2 1v0l-2 1 2 4 2-1 2 1 1 2h4l1-2 2-1 2 1 2-4z' };
|
|
89
|
+
export default function App() {
|
|
90
|
+
const [at, setAt] = React.useState('inbox');
|
|
91
|
+
const item = (key, label, badge) => (
|
|
92
|
+
<SidebarMenuItem key={key}>
|
|
93
|
+
<SidebarMenuButton href={'#/' + key} current={at === key} icon={<I d={icons[key]} />} onClick={(e) => { e.preventDefault(); setAt(key); }}>{label}</SidebarMenuButton>
|
|
94
|
+
{badge && <SidebarMenuBadge label={`${badge} unread`}>{badge}</SidebarMenuBadge>}
|
|
95
|
+
</SidebarMenuItem>
|
|
96
|
+
);
|
|
97
|
+
return (
|
|
98
|
+
<div className="as">
|
|
99
|
+
<AppShell
|
|
100
|
+
sidebar={
|
|
101
|
+
<Sidebar>
|
|
102
|
+
<SidebarHeader>
|
|
103
|
+
<DropdownMenu items={[{ label: 'Acme Inc.' }, { label: 'Northwind' }, { type: 'separator' }, { label: 'Create workspace' }]}
|
|
104
|
+
trigger={<button className="as-ws"><Avatar name="Acme Inc." size={24} /><span className="alt-sidebar-text">Acme Inc.</span></button>} />
|
|
105
|
+
</SidebarHeader>
|
|
106
|
+
<SidebarContent>
|
|
107
|
+
<SidebarGroup label="Workspace"><SidebarMenu>{item('inbox', 'Inbox', 4)}{item('projects', 'Projects')}{item('reports', 'Reports')}</SidebarMenu></SidebarGroup>
|
|
108
|
+
<SidebarGroup label="Admin" collapsible><SidebarMenu>{item('members', 'Members')}{item('billing', 'Billing')}{item('settings', 'Settings')}</SidebarMenu></SidebarGroup>
|
|
109
|
+
</SidebarContent>
|
|
110
|
+
<SidebarFooter><SidebarMenu><SidebarMenuItem><SidebarMenuButton href="#/me" icon={<Avatar name="Maya Chen" size={20} />}>Maya Chen</SidebarMenuButton></SidebarMenuItem></SidebarMenu></SidebarFooter>
|
|
111
|
+
</Sidebar>
|
|
112
|
+
}
|
|
113
|
+
topBar={<Button variant="secondary" size="sm" className="search">Search… <Kbd>⌘K</Kbd></Button>}>
|
|
114
|
+
<PageHeader primary={<Button variant="primary">New message</Button>}><PageHeaderTitle>{at[0].toUpperCase() + at.slice(1)}</PageHeaderTitle><PageHeaderDescription>The sidebar's current destination sits on a raised pill that glides as you move. ⌘B collapses it to a rail.</PageHeaderDescription></PageHeader>
|
|
115
|
+
<div className="rows">{Array.from({ length: 8 }, (_, i) => <div key={i} />)}</div>
|
|
116
|
+
</AppShell>
|
|
117
|
+
</div>
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
<!-- /example -->
|
|
@@ -41,3 +41,61 @@ A total over time and the parts that make it up (requests by region, storage by
|
|
|
41
41
|
- Do keep to six bands or fewer; fold the rest into one "Other" series.
|
|
42
42
|
- Don't overlay several unstacked areas; compare trends with a `LineChart`.
|
|
43
43
|
- Don't add a gradient, a glow or a shadow to make it look richer.
|
|
44
|
+
|
|
45
|
+
<!-- example: generated from build/previews.txt by build/previews.mjs. Edit the preview, not this section. -->
|
|
46
|
+
## Example
|
|
47
|
+
|
|
48
|
+
`AreaChart` as first used in its preview:
|
|
49
|
+
|
|
50
|
+
```tsx
|
|
51
|
+
import { AreaChart, ChartLegend, ChartLegendContent } from 'altitude-design';
|
|
52
|
+
|
|
53
|
+
<AreaChart data={week ? reqB : reqA} x="day" stack curve="monotone">
|
|
54
|
+
<ChartLegend content={<ChartLegendContent toggle />} />
|
|
55
|
+
</AreaChart>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
The full preview, as rendered in Storybook and checked in CI (`ac`, `wide` are the preview page's own layout classes, not Altitude's):
|
|
59
|
+
|
|
60
|
+
```tsx
|
|
61
|
+
import * as React from 'react';
|
|
62
|
+
import { AreaChart, Button, Card, CardContent, CardDescription, CardHeader, CardTitle, ChartContainer, ChartLegend, ChartLegendContent } from 'altitude-design';
|
|
63
|
+
|
|
64
|
+
const acDays = Array.from({ length: 14 }, (_, i) => `2026-10-${String(i + 1).padStart(2, '0')}`);
|
|
65
|
+
const reqA = acDays.map((day, i) => ({ day, us: 1600 + i * 40 + (i % 3) * 90, eu: 1100 + Math.round(180 * Math.sin(i / 2)), apac: 520 + i * 35 }));
|
|
66
|
+
const reqB = acDays.map((day, i) => ({ day, us: 1500 + i * 55 + (i % 4) * 70, eu: 1250 + Math.round(140 * Math.cos(i / 2)), apac: 700 + i * 20 }));
|
|
67
|
+
const plans = ['2026-01-01', '2026-02-01', '2026-03-01', '2026-04-01', '2026-05-01', '2026-06-01', '2026-07-01', '2026-08-01']
|
|
68
|
+
.map((month, i) => ({ month, free: 620 - i * 30, team: 210 + i * 22, business: 90 + i * 15, enterprise: 20 + i * 6 }));
|
|
69
|
+
const storage = acDays.map((day, i) => ({ day, used: [41, 42, 42.5, 44, 45, 45.2, 47, 49, 50, 50.4, 53, 55, 56, 58][i] }));
|
|
70
|
+
const k = new Intl.NumberFormat('en', { notation: 'compact', maximumFractionDigits: 1 });
|
|
71
|
+
function Chart({ id, title, sub, children, className, ...props }) {
|
|
72
|
+
return (
|
|
73
|
+
<Card className={className}>
|
|
74
|
+
<CardHeader><CardTitle id={id}>{title}</CardTitle><CardDescription>{sub}</CardDescription></CardHeader>
|
|
75
|
+
<CardContent><ChartContainer aria-labelledby={id} {...props}>{children}</ChartContainer></CardContent>
|
|
76
|
+
</Card>
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
export default function App() {
|
|
80
|
+
const [week, setWeek] = React.useState(0);
|
|
81
|
+
return (
|
|
82
|
+
<div className="ac">
|
|
83
|
+
<Chart className="wide" id="a1" title="Requests by region" sub={<>Stacked: the total and where it came from · <Button size="sm" variant="ghost" onClick={() => setWeek((w) => 1 - w)}>{week ? 'First week' : 'Second week'}</Button></>}
|
|
84
|
+
config={{ us: { label: 'US', format: k.format }, eu: { label: 'EU', format: k.format }, apac: { label: 'APAC', format: k.format } }} aspect={3.2} views>
|
|
85
|
+
<AreaChart data={week ? reqB : reqA} x="day" stack curve="monotone">
|
|
86
|
+
<ChartLegend content={<ChartLegendContent toggle />} />
|
|
87
|
+
</AreaChart>
|
|
88
|
+
</Chart>
|
|
89
|
+
<Chart id="a2" title="Accounts by plan" sub='stack="expand", focus on Team'
|
|
90
|
+
config={{ free: { label: 'Free' }, team: { label: 'Team' }, business: { label: 'Business' }, enterprise: { label: 'Enterprise' } }} aspect={1.7}>
|
|
91
|
+
<AreaChart data={plans} x="month" stack="expand" focus={['team']} />
|
|
92
|
+
</Chart>
|
|
93
|
+
<Chart id="a3" title="Storage used" sub="One area: a flat fill under its line" config={{ used: { label: 'Used', format: (v) => `${v} GB` } }} aspect={1.7}
|
|
94
|
+
summary="Storage use rose from 41 GB to 58 GB over two weeks, fastest in the second.">
|
|
95
|
+
<AreaChart data={storage} x="day" />
|
|
96
|
+
</Chart>
|
|
97
|
+
</div>
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
<!-- /example -->
|
|
@@ -28,3 +28,72 @@ What an agent is making (a document, a component, a page, a chart, a config) in
|
|
|
28
28
|
- Do sandbox preview iframes; the frame's permissions are yours.
|
|
29
29
|
- Do keep the last whole version visible when a new one fails.
|
|
30
30
|
- Don't lift the pane or tint it for an earlier version; say it in the strip.
|
|
31
|
+
|
|
32
|
+
<!-- example: generated from build/previews.txt by build/previews.mjs. Edit the preview, not this section. -->
|
|
33
|
+
## Example
|
|
34
|
+
|
|
35
|
+
`Artifact` as first used in its preview:
|
|
36
|
+
|
|
37
|
+
```tsx
|
|
38
|
+
import { Artifact, ArtifactContent, ArtifactHeader, ArtifactVersions, CodeBlock, DiffView } from 'altitude-design';
|
|
39
|
+
|
|
40
|
+
<Artifact id="pricing-artifact" title="Pricing page" status={status} version={3}>
|
|
41
|
+
<ArtifactHeader onCopy={() => versions[v - 1].code} onDownload={() => {}} onClose={() => setOpen(false)} />
|
|
42
|
+
<ArtifactVersions versions={3} current={v} onChange={setV} onRestore={() => setV(3)} />
|
|
43
|
+
<ArtifactContent views={{
|
|
44
|
+
preview: <iframe sandbox="" srcDoc={html(v)} title={`Pricing page preview, version ${v}`} />,
|
|
45
|
+
source: <CodeBlock language="tsx" code={versions[v - 1].code} streaming={status === 'writing' && v === 3} />,
|
|
46
|
+
changes: v > 1 ? <DiffView caption={`Version ${v - 1} → ${v}`} before={versions[v - 2].code} after={versions[v - 1].code} /> : undefined,
|
|
47
|
+
}} />
|
|
48
|
+
</Artifact>
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
The full preview, as rendered in Storybook and checked in CI (`af`, `chat` are the preview page's own layout classes, not Altitude's):
|
|
52
|
+
|
|
53
|
+
```tsx
|
|
54
|
+
import * as React from 'react';
|
|
55
|
+
import { Artifact, ArtifactContent, ArtifactHeader, ArtifactReference, ArtifactVersions, CodeBlock, DiffView, Message, MessageContent, ResizableHandle, ResizablePanel, ResizablePanelGroup } from 'altitude-design';
|
|
56
|
+
|
|
57
|
+
const versions = [
|
|
58
|
+
{ code: `export function Pricing() {\n return <h1>Pricing</h1>;\n}` },
|
|
59
|
+
{ code: `export function Pricing() {\n return (\n <section>\n <h1>Pricing</h1>\n <p>Start free, upgrade when you grow.</p>\n </section>\n );\n}` },
|
|
60
|
+
{ code: `export function Pricing() {\n return (\n <section>\n <h1>Pricing</h1>\n <p>Start free, upgrade when you grow.</p>\n <Plans tiers={['Starter', 'Pro', 'Business']} />\n </section>\n );\n}` },
|
|
61
|
+
];
|
|
62
|
+
const html = (v) => `<body style="font:15px system-ui;margin:24px;color:#1b2230"><h1 style="font-size:22px">Pricing</h1>${v >= 2 ? '<p>Start free, upgrade when you grow.</p>' : ''}${v >= 3 ? '<div style="display:flex;gap:8px">' + ['Starter', 'Pro', 'Business'].map((t) => `<div style="flex:1;padding:12px;border:1px solid #d5dae3;border-radius:8px">${t}</div>`).join('') + '</div>' : ''}</body>`;
|
|
63
|
+
export default function App() {
|
|
64
|
+
const [open, setOpen] = React.useState(true);
|
|
65
|
+
const [status, setStatus] = React.useState('writing');
|
|
66
|
+
const [v, setV] = React.useState(3);
|
|
67
|
+
React.useEffect(() => { const t = setTimeout(() => setStatus('complete'), 2500); return () => clearTimeout(t); }, []);
|
|
68
|
+
return (
|
|
69
|
+
<div className="af">
|
|
70
|
+
<ResizablePanelGroup direction="horizontal" narrow="stack" narrowPanel={open ? 1 : 0}>
|
|
71
|
+
<ResizablePanel defaultSize={42} aria-label="Conversation">
|
|
72
|
+
<div className="chat">
|
|
73
|
+
<Message from="user"><MessageContent>Make a pricing page with three plans.</MessageContent></Message>
|
|
74
|
+
<Message from="assistant">
|
|
75
|
+
<MessageContent>Here's the page, with the three plans in a row.</MessageContent>
|
|
76
|
+
<ArtifactReference title="Pricing page" version={3} status={status} controls="pricing-artifact" onOpen={() => setOpen(true)} />
|
|
77
|
+
</Message>
|
|
78
|
+
</div>
|
|
79
|
+
</ResizablePanel>
|
|
80
|
+
<ResizableHandle />
|
|
81
|
+
<ResizablePanel defaultSize={58} aria-label="Pricing page">
|
|
82
|
+
{open && (
|
|
83
|
+
<Artifact id="pricing-artifact" title="Pricing page" status={status} version={3}>
|
|
84
|
+
<ArtifactHeader onCopy={() => versions[v - 1].code} onDownload={() => {}} onClose={() => setOpen(false)} />
|
|
85
|
+
<ArtifactVersions versions={3} current={v} onChange={setV} onRestore={() => setV(3)} />
|
|
86
|
+
<ArtifactContent views={{
|
|
87
|
+
preview: <iframe sandbox="" srcDoc={html(v)} title={`Pricing page preview, version ${v}`} />,
|
|
88
|
+
source: <CodeBlock language="tsx" code={versions[v - 1].code} streaming={status === 'writing' && v === 3} />,
|
|
89
|
+
changes: v > 1 ? <DiffView caption={`Version ${v - 1} → ${v}`} before={versions[v - 2].code} after={versions[v - 1].code} /> : undefined,
|
|
90
|
+
}} />
|
|
91
|
+
</Artifact>
|
|
92
|
+
)}
|
|
93
|
+
</ResizablePanel>
|
|
94
|
+
</ResizablePanelGroup>
|
|
95
|
+
</div>
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
<!-- /example -->
|
|
@@ -32,3 +32,44 @@ In an `AvatarGroup`, every face stands on one ground line, each cut out of the o
|
|
|
32
32
|
**You provide:** `name` (always: it's the accessible label, with the status appended), optional `src`, `size` in pixels (default 36), `shape`, `status`, `live`, `presence`, `lamp`, `onClick`. On `AvatarGroup`: `max`, `size`.
|
|
33
33
|
|
|
34
34
|
- Leave about a fifth of the avatar's size free above a live or pressable avatar for it to rise into, and a quarter above a group.
|
|
35
|
+
|
|
36
|
+
<!-- example: generated from build/previews.txt by build/previews.mjs. Edit the preview, not this section. -->
|
|
37
|
+
## Example
|
|
38
|
+
|
|
39
|
+
`Avatar` as first used in its preview:
|
|
40
|
+
|
|
41
|
+
```tsx
|
|
42
|
+
import { Avatar } from 'altitude-design';
|
|
43
|
+
|
|
44
|
+
<Avatar name="Mara Quist" size={44} status="active" live presence={1} />
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The full preview, as rendered in Storybook and checked in CI (`col`, `people`, `who`, `cap` are the preview page's own layout classes, not Altitude's):
|
|
48
|
+
|
|
49
|
+
```tsx
|
|
50
|
+
import { Avatar, AvatarGroup } from 'altitude-design';
|
|
51
|
+
|
|
52
|
+
export default function App() {
|
|
53
|
+
return (
|
|
54
|
+
<div className="col" style={{ gap: 22 }}>
|
|
55
|
+
<div className="people">
|
|
56
|
+
<div className="who"><Avatar name="Mara Quist" size={44} status="active" live presence={1} /><span className="cap">live</span></div>
|
|
57
|
+
<div className="who"><Avatar name="Jon Reyes" size={44} status="active" /><span className="cap">active</span></div>
|
|
58
|
+
<div className="who"><Avatar name="Lena Park" size={44} status="idle" /><span className="cap">idle</span></div>
|
|
59
|
+
<div className="who"><Avatar name="Sam Okafor" size={44} status="offline" /><span className="cap">offline</span></div>
|
|
60
|
+
<div className="who"><Avatar name="Noa Levi" size={44} status="active" onClick={() => {}} /><span className="cap">pressable</span></div>
|
|
61
|
+
<div className="who"><Avatar name="Northwind Studio" size={44} shape="square" /><span className="cap">team</span></div>
|
|
62
|
+
<div className="who"><Avatar name="Deploy bot" size={44} shape="square" status="active" /><span className="cap">bot</span></div>
|
|
63
|
+
</div>
|
|
64
|
+
<div className="people" style={{ alignItems: 'center' }}>
|
|
65
|
+
<AvatarGroup max={5} size={32}>
|
|
66
|
+
{[['Ada Obi', 'active', true], ['Ivy Chen', 'idle'], ['Theo Marsh', 'active'], ['Rui Costa', 'idle'], ['Noa Levi', 'offline'], ['Eli Ward', 'offline'], ['Kai Ito', 'offline']].map(([n, st, live]) => <Avatar key={n} name={n} status={st} live={live} />)}
|
|
67
|
+
</AvatarGroup>
|
|
68
|
+
{[24, 36, 64].map((s) => <Avatar key={s} name="Ines Park" size={s} status="active" />)}
|
|
69
|
+
</div>
|
|
70
|
+
<p className="cap">People who are here rest on the page: a filled lamp when they're active, an empty one when they're away. Offline people lie flush and grey. Mara is speaking, so she lifts off her shadow, ringed in her presence colour. Teams and bots are square. Hover Noa: an avatar you can press rises like everything else that acts.</p>
|
|
71
|
+
</div>
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
<!-- /example -->
|