Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .changeset/docs-pente-fino-readmes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
'@treeui/tokens': patch
'@treeui/utils': patch
'@treeui/vue': patch
'@treeui/mcp': patch
---

New `README.md` files for `@treeui/tokens` and `@treeui/utils`, previously blank on npm. `@treeui/vue`'s README now uses the named `TreeUIPlugin` import/install and replaces its hand-kept component list with a category overview pointing at the generated Storybook index and `docs/ai/CONTRACTS.yaml`. `@treeui/mcp`'s README now enumerates its resources and tools by name instead of by category.
8 changes: 4 additions & 4 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@

## Validation

- [ ] `pnpm lint`
- [ ] `pnpm typecheck`
- [ ] `pnpm test`
- [ ] `pnpm build`
- [ ] Ran the quality gates in [CONTRIBUTING.md](../CONTRIBUTING.md#before-opening-a-pull-request)
- [ ] Added a changeset (`pnpm changeset`) if this is a user-facing package change
- [ ] Ran the Kotlin/Rust port gates if this touches `packages/compose` or `packages/egui`
- [ ] Updated the `docs/ai` contracts if a public API changed

## Notes

Expand Down
14 changes: 8 additions & 6 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ changes, update `docs/ai/practices.json`.
`packages/icons/README.md` and `docs/ai/STANDARDS.yaml` → `icons.branchline`. The rules
are enforced by rendering, not by review: `branchline.test.ts` fails a build when two
icons differ by less than 5% of their ink at 16px, or when any node changes less than 1%
of a glyph's ink. Never derive geometry from an icon's name.
of a glyph's ink at 32px. Never derive geometry from an icon's name.
- **Sizes**: `sm | md | lg` — shared across most components.
- **Action variants**: `solid | outline | ghost | soft | danger`.
- **Card variants**: `outline | soft | inset` — a surface scale, not action variants. `solid` is
Expand Down Expand Up @@ -177,11 +177,13 @@ Run before any PR — [CONTRIBUTING.md](./CONTRIBUTING.md#before-opening-a-pull-
owns this list and its Docker equivalents:

```bash
pnpm lint # ESLint, zero warnings
pnpm typecheck # TypeScript strict mode across every workspace package
pnpm test # Vitest unit tests with coverage
pnpm build:site # what CI builds: packages + landing + both Storybooks + examples
pnpm test:e2e # Playwright; optional locally, required in CI
pnpm lint # ESLint, zero warnings
pnpm typecheck # TypeScript strict mode across every workspace package
pnpm test # Vitest unit tests with coverage
pnpm build:site # what CI builds: packages + landing + both Storybooks + examples
pnpm typecheck:strict-templates # typechecks consumer templates against the built package (strictTemplates)
pnpm check:theme # validates the example's accent presets against the colour contract
pnpm test:e2e # Playwright; optional locally, required in CI
```

When touching the token model, regenerate the ports' source and check it in —
Expand Down
18 changes: 13 additions & 5 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,25 @@ Use the setup in [README.md](./README.md#getting-started) to install dependencie
Run the quality gates — the same commands CI runs:

```bash
pnpm lint # ESLint, zero warnings
pnpm typecheck # TypeScript strict mode across every workspace package
pnpm codegen:native:check # the ports' generated token source matches the model
pnpm test # Vitest unit tests with coverage
pnpm build:site # what CI builds: packages + landing + both Storybooks + examples
pnpm lint # ESLint, zero warnings
pnpm typecheck # TypeScript strict mode across every workspace package
pnpm codegen:native:check # the ports' generated token source matches the model
pnpm test # Vitest unit tests with coverage
pnpm build:site # what CI builds: packages + landing + both Storybooks + examples
pnpm typecheck:strict-templates # typechecks consumer templates against the built package (strictTemplates)
pnpm check:theme # validates the example's accent presets against the colour contract
```

If `codegen:native:check` fails, run `pnpm codegen:native` and commit what it
writes. It means the token model changed and the Kotlin and Rust ports are
carrying the previous release's values.

`typecheck:strict-templates` and `check:theme` both read the **built**
package, not the source, so they run after `build:site` here — in CI they sit
between the dedicated `build:packages` step and the `build:site` step, but
this list has no standalone `build:packages` line, since `build:site` already
runs it first.

If your change touches interaction or accessibility, also run:

```bash
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ packages/compose Kotlin component package for Android (Jetpack Compo
packages/egui Rust component crates for the desktop (egui)
packages/mcp TreeUI AI catalog and MCP server for coding agents
docs/ai Machine-oriented contracts for tools and agents
tooling Docker, ESLint, and TypeScript shared config
assets Self-hosted fonts used by the Vue and React Storybooks
tooling Docker, ESLint, TypeScript config, and the strictTemplates gate
scripts Site assembly, static serving, and asset scripts
tests Playwright end-to-end specs
```
Expand Down
17 changes: 11 additions & 6 deletions RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,15 @@ Required check for branch protection. Steps:
Fix with `pnpm codegen:native` and commit the result.
5. `pnpm test` (Vitest)
6. `pnpm build:packages`
7. `pnpm build:site` (landing + Vue/React Storybooks + example dashboards)
8. `pnpm exec playwright install --with-deps chromium`
9. `pnpm test:e2e` (with `PW_SKIP_BUILD=1`, reusing the build from step 7)
10. Uploads the `site` artifact for the pages job.
7. `pnpm typecheck:strict-templates` — typechecks consumer-app templates with
`strictTemplates` against the package just built in step 6, not the source.
8. `pnpm check:theme` — validates the example dashboard's accent presets
against the colour contract; the validator is also imported from the
built package.
9. `pnpm build:site` (landing + Vue/React Storybooks + example dashboards)
10. `pnpm exec playwright install --with-deps chromium`
11. `pnpm test:e2e` (with `PW_SKIP_BUILD=1`, reusing the build from step 9)
12. Uploads the `site` artifact for the pages job.

### `kotlin` and `rust` jobs (run on every PR and on push to `main`)

Expand Down Expand Up @@ -280,8 +285,8 @@ release-specific commands are:
```bash
pnpm build:packages # build the six published packages only
pnpm changeset # create a changeset entry
pnpm changeset status # list pending changesets
pnpm ai:catalog # regenerate docs/ai/treeui.catalog.json
pnpm changeset:status # list pending changesets since origin/main
pnpm ai:catalog # regenerate docs/ai/treeui.catalog.json and packages/mcp/src/generated/treeui.catalog.json
pnpm codegen:native # regenerate the ports' token source
pnpm codegen:native:check # fail if that source is stale (runs in CI)
pnpm mcp:start # run the local TreeUI MCP server
Expand Down
3 changes: 3 additions & 0 deletions apps/docs/src/Roadmap.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ Milestones 1–3 are complete and part of the public surface:
- **Milestone 3 — Routing, cards & inline links**: `TNavMenu` RouterLink support, `TCard` `title` prop and `#actions` slot, `TCard variant="inset"`, `TLink`.
- **Data visualization**: `TChart`, `TSparkline`, `TDonutChart`, sharing a framework-agnostic geometry engine in `@treeui/utils` and the `--tree-color-chart-*` palette.
- **React package**: `@treeui/react` ships the core primitives on the same tokens and `t-*` classes.
- **Android / Compose**: `packages/compose` (`treeui-tokens`, `treeui-compose`) — token layer complete, component set starting (`TButton`, `TBadge`, `TCard`).
- **Desktop / Rust**: `packages/egui` (`treeui-tokens`, `treeui-egui`) — token layer complete, component set starting (`TButton`, `TBadge`, `TCard`).

## Planned

Expand All @@ -29,6 +31,7 @@ Milestones 1–3 are complete and part of the public surface:
| Motion token expansion | More duration/easing steps and a reduced-motion contract. Not started. |
| Theme builder | Visual token editor on top of `@treeui/tokens`. Exploratory. |
| Additional framework packages | `@treeui/svelte` and Web Components reusing the same tokens, naming, and accessibility guidance. Exploratory. |
| Storybook coverage for shipped components | `TPane`, `TDropVeil`, `TCalendar` / `TCalendarMonthGrid` / `TCalendarTimeGrid`, `TSpanLanes`, `TAudioPlayer`, `TVoiceRecorder` are implemented and exported but have no story file, so they render with no link in the generated Foundation/Components index. Not started. |

## Table direction

Expand Down
2 changes: 2 additions & 0 deletions apps/docs/src/stories/AppShell.stories.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { Meta, StoryObj } from '@storybook/vue3-vite';
import { ref } from 'vue';
import { TAppShell, TAvatar, TBadge, TButton, TNavMenu } from '@treeui/vue';
import { practiceNote } from './practice-refs';

const items = [
{ label: 'Overview', value: 'overview', description: 'Summary and recent work', shortLabel: 'O' },
Expand All @@ -17,6 +18,7 @@ const meta = {
parameters: {
// The shell fills the viewport, so give it the whole canvas.
layout: 'fullscreen',
docs: { description: { component: practiceNote('TAppShell') } },
},
args: {
side: 'left',
Expand Down
4 changes: 4 additions & 0 deletions apps/docs/src/stories/ColorSwatch.stories.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { Meta, StoryObj } from '@storybook/vue3-vite';
import { ref } from 'vue';
import { TColorSwatch } from '@treeui/vue';
import { practiceNote } from './practice-refs';

const presets = [
{ label: 'Ocean blue', value: '#0969da' },
Expand All @@ -14,6 +15,9 @@ const meta = {
title: 'Components/Data Entry/ColorSwatch',
component: TColorSwatch,
tags: ['autodocs'],
parameters: {
docs: { description: { component: practiceNote('TColorSwatch') } },
},
args: {
options: presets,
allowCustom: true,
Expand Down
4 changes: 4 additions & 0 deletions apps/docs/src/stories/DatePicker.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,15 @@ import type { Meta, StoryObj } from '@storybook/vue3-vite';
import { ref } from 'vue';
import { TButton, TDatePicker } from '@treeui/vue';
import { InfoIcon, iconProps } from './icon-helpers';
import { practiceNote } from './practice-refs';

const meta = {
title: 'Components/Data Entry/DatePicker',
component: TDatePicker,
tags: ['autodocs'],
parameters: {
docs: { description: { component: practiceNote('TDatePicker') } },
},
args: {
size: 'md',
disabled: false,
Expand Down
4 changes: 4 additions & 0 deletions apps/docs/src/stories/FileUpload.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,15 @@ import type {
TFileUploadRetryPayload,
TFileUploadState,
} from '@treeui/vue';
import { practiceNote } from './practice-refs';

const meta = {
title: 'Components/Data Entry/FileUpload',
component: TFileUpload,
tags: ['autodocs'],
parameters: {
docs: { description: { component: practiceNote('TFileUpload') } },
},
args: {
size: 'md',
multiple: true,
Expand Down
4 changes: 4 additions & 0 deletions apps/docs/src/stories/NavMenu.stories.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { Meta, StoryObj } from '@storybook/vue3-vite';
import { ref } from 'vue';
import { TNavMenu } from '@treeui/vue';
import { practiceNote } from './practice-refs';

const items = [
{ label: 'Overview', value: 'overview', description: 'Summary and activity', badge: '12', shortLabel: 'O' },
Expand All @@ -19,6 +20,9 @@ const meta = {
title: 'Components/Layout/NavMenu',
component: TNavMenu,
tags: ['autodocs'],
parameters: {
docs: { description: { component: practiceNote('TNavMenu') } },
},
args: {
size: 'md',
collapsed: false,
Expand Down
4 changes: 4 additions & 0 deletions apps/docs/src/stories/Radio.stories.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import type { Meta, StoryObj } from '@storybook/vue3-vite';
import { ref } from 'vue';
import { TRadio, TRadioGroup } from '@treeui/vue';
import { practiceNote } from './practice-refs';

const meta = {
title: 'Components/Data Entry/Radio',
component: TRadioGroup,
tags: ['autodocs'],
parameters: {
docs: { description: { component: practiceNote('TRadio') } },
},
args: {
size: 'md',
disabled: false,
Expand Down
4 changes: 4 additions & 0 deletions apps/docs/src/stories/Sparkline.stories.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import type { Meta, StoryObj } from '@storybook/vue3-vite';
import { TSparkline, TStat } from '@treeui/vue';
import { practiceNote } from './practice-refs';

const sample = [12, 18, 9, 22, 30, 24, 28, 26, 33, 38];

const meta = {
title: 'Components/Data Display/Sparkline',
component: TSparkline,
tags: ['autodocs'],
parameters: {
docs: { description: { component: practiceNote('TSparkline') } },
},
args: {
data: sample,
type: 'line',
Expand Down
4 changes: 4 additions & 0 deletions apps/docs/src/stories/Stat.stories.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import type { Meta, StoryObj } from '@storybook/vue3-vite';
import { TCard, TStat, TStatGroup, TText } from '@treeui/vue';
import { CheckIcon, InfoIcon, iconProps } from './icon-helpers';
import { practiceNote } from './practice-refs';

const meta = {
title: 'Components/Data Display/Stat',
component: TStat,
tags: ['autodocs'],
parameters: {
docs: { description: { component: practiceNote('TStat', 'TStatGroup') } },
},
args: {
label: 'Monthly recurring revenue',
value: '$48.2k',
Expand Down
4 changes: 4 additions & 0 deletions apps/docs/src/stories/Text.stories.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import type { Meta, StoryObj } from '@storybook/vue3-vite';
import { TText } from '@treeui/vue';
import { practiceNote } from './practice-refs';

const meta = {
title: 'Components/Data Display/Text',
component: TText,
tags: ['autodocs'],
parameters: {
docs: { description: { component: practiceNote('TText') } },
},
args: {
as: 'span',
size: 'md',
Expand Down
4 changes: 4 additions & 0 deletions apps/docs/src/stories/Textarea.stories.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import type { Meta, StoryObj } from '@storybook/vue3-vite';
import { ref } from 'vue';
import { TTextarea } from '@treeui/vue';
import { practiceNote } from './practice-refs';

const meta = {
title: 'Components/Data Entry/Textarea',
component: TTextarea,
tags: ['autodocs'],
parameters: {
docs: { description: { component: practiceNote('TTextarea') } },
},
args: {
size: 'md',
disabled: false,
Expand Down
4 changes: 4 additions & 0 deletions apps/docs/src/stories/Timeline.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { Meta, StoryObj } from '@storybook/vue3-vite';
import { TTimeline } from '@treeui/vue';
import type { TTimelineItem } from '@treeui/vue';
import { CheckIcon, InfoIcon, iconProps } from './icon-helpers';
import { practiceNote } from './practice-refs';

const releaseEvents: TTimelineItem[] = [
{
Expand Down Expand Up @@ -56,6 +57,9 @@ const meta = {
title: 'Components/Data Display/Timeline',
component: TTimeline,
tags: ['autodocs'],
parameters: {
docs: { description: { component: practiceNote('TTimeline') } },
},
args: {
size: 'md',
items: releaseEvents,
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/src/stories/Toast.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const meta = {
title: 'Components/Feedback/Toast',
component: TToastProvider,
parameters: {
docs: { description: { component: practiceNote('TToastProvider') } },
docs: { description: { component: practiceNote('TToast', 'TToastProvider') } },
},
tags: ['autodocs'],
args: {
Expand Down
4 changes: 4 additions & 0 deletions apps/docs/src/stories/ToggleGroup.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { Meta, StoryObj } from '@storybook/vue3-vite';
import type { ComponentProps } from 'vue-component-type-helpers';
import { ref } from 'vue';
import { TStack, TText, TToggleGroup } from '@treeui/vue';
import { practiceNote } from './practice-refs';

// `TToggleGroup` is generic over its option type AND its selection mode, so
// `typeof TToggleGroup` is a generic FUNCTION, which Storybook's
Expand All @@ -21,6 +22,9 @@ const meta: Meta<TToggleGroupArgs> = {
title: 'Components/Data Entry/ToggleGroup',
component: TToggleGroup as never,
tags: ['autodocs'],
parameters: {
docs: { description: { component: practiceNote('TToggleGroup') } },
},
args: {
size: 'md',
variant: 'outline',
Expand Down
34 changes: 28 additions & 6 deletions apps/docs/src/stories/practice-refs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,7 @@ const { practices, storybookDocPage } = practicesData;
const practiceLink = (id: string, title: string) =>
`<a target="_top" href="./?path=/docs/${storybookDocPage}--docs#${id}">${title}</a>`;

/**
* Docs-page note for a story meta (`parameters.docs.description.component`)
* listing the named TreeUI practices — from `docs/ai/practices.json` — that the
* component follows, each linking to its section on Foundation/Practices.
*/
export const practiceNote = (componentName: string): string => {
const practiceNoteForComponent = (componentName: string): string => {
const followed = practices.filter((practice) => practice.components.includes(componentName));

if (followed.length === 0) {
Expand All @@ -26,3 +21,30 @@ export const practiceNote = (componentName: string): string => {

return `Follows the TreeUI practices: ${links}.`;
};

/**
* Docs-page note for a story meta (`parameters.docs.description.component`)
* listing the named TreeUI practices — from `docs/ai/practices.json` — that the
* component follows, each linking to its section on Foundation/Practices.
*
* Pass a single component name for a story whose meta documents one
* component — the output is a single, unprefixed sentence, unchanged from the
* original single-argument form. Pass more than one name (e.g. a provider
* story that also demonstrates a paired component) and each component gets
* its own labelled block, listing only the practices IT follows — the sets
* are never merged, since that would misstate which practices the other
* component actually follows.
*/
export const practiceNote = (...names: string[]): string => {
if (names.length <= 1) {
return names.length === 0 ? '' : practiceNoteForComponent(names[0]);
}

return names
.map((name) => {
const note = practiceNoteForComponent(name);
return note ? `**${name}** — ${note}` : '';
})
.filter((block) => block !== '')
.join('\n\n');
};
Loading
Loading