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
11 changes: 11 additions & 0 deletions .changeset/explorer-on-event.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
'@walkeros/explorer': patch
---

Adds the `on-event` design token: text on an event-colour fill, such as a tag
card's Done button. Dark ink on the dark event colours, white on the light ones,
both above 4.5:1.

Adds `EventLog` to the design components: the walkerOS events of a session,
newest first, beside the selected event as fields or as JSON, with copy and
clear.
11 changes: 11 additions & 0 deletions .changeset/frame-links.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
'@walkeros/mcp': minor
'@walkeros/cli': patch
---

`frame_manage` returns the frames a frame shows as `links` and its variant group
as `variantSetId`. `parentId`, `extends` and `placements` are gone from its
output, from the exported frame types and from what the CLI's `listFrames`,
`listPageFrames` and `getFrame` return. A page read returns that page's frames
only. `FRAME_HINT_EXTENDS_BASE` is now `FRAME_HINT_FOLLOW_LINKS`. Needs the
walkerOS app release that serves links.
11 changes: 11 additions & 0 deletions .changeset/tag-mode-loader.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
'@walkeros/core': patch
'@walkeros/cli': patch
'@walkeros/walker.js': patch
---

walker.js and every web bundle the CLI builds now carry the Tag Mode loader,
`moin()` from `@walkeros/core`. It stays idle unless the walkerOS app opens your
page for Tag Mode (`?elbMoin`), then loads the Tag Mode script from
`cdn.walkeros.io`. Without that host in your CSP's `script-src`, it loads
nothing (a `'strict-dynamic'` policy admits it through walker.js's nonce).
11 changes: 8 additions & 3 deletions apps/explorer/AGENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,11 +160,16 @@ design tokens; walkeros.io's home page is built from it.
- Folders follow the atomic order: `atoms/` (Button, InlineCode, EventLegend,
Icon, InstallCommand, Card, Eyebrow, Stat, Text, TextLink, PhotoPlaceholder),
`molecules/` (CheckList, SectionHeading, ProblemCard, FeatureItem, FaqItem,
PlanCard, CaseCard, HighlightCard, BrowserFrame, QuickstartSteps), `layout/`
(Section, CardGrid, Split, Cluster (`separated` sets a call to action apart
from the content above), Hero) and `viz/` (HeroTaggingViz,
PlanCard, CaseCard, HighlightCard, BrowserFrame, EventLog, QuickstartSteps),
`layout/` (Section, CardGrid, Split, Cluster (`separated` sets a call to
action apart from the content above), Hero) and `viz/` (HeroTaggingViz,
ArticleTeaserTracking, DestinationMappingViz). Nothing imports upward; a demo
may use atoms.
- Reuse before a new style: a molecule's buttons are the `Button` atom, its code
chips `InlineCode`, its glyphs `Icon`. `atoms/` also keeps two pieces outside
the entry that more than one folder uses: `CodeTokens` (code runs in the
demos' colours, for a dark ground; the token type is `codeTokens.ts`) and
`useCopy` (copy to the clipboard, then say how it went).
- Each folder keeps its own `index.ts`; the entry `index.ts` re-exports the
four, so names stay flat. Everything the entry reaches imports only `react`
(`__tests__/entry.test.ts` fails otherwise), and the demo data in `viz/data/`
Expand Down
8 changes: 5 additions & 3 deletions apps/explorer/STYLE.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@ never a component.
| Text | `--fg` (primary), `--fg-2` (secondary, labels, inactive buttons), `--fg-3` (meta, placeholder, disabled) |
| Grounds | `--bg` (page, inputs), `--bg-2` (headers, footers, button groups), `--surface` (boxes, menus), `--surface-2` (hover, inline code) |
| Edges | `--border` (hairlines), `--border-strong` (inputs, controls, floating layers), `--focus` (focus ring) |
| Actions | `--primary` fill with `--on-primary` text, `--link` for text in the brand colour, `--danger` with `--on-danger` |
| Actions | `--primary` with `--on-primary`, `--link` for text in the brand colour, `--danger` with `--on-danger`, a tag card's Done with `--on-event` |
| Status | `--success`, `--warning`, `--danger`, `--info`, tints `--success-bg` and the like; always with an icon and a word |
| Events | `--event-entity`, `--event-action`, `--event-property`, `--event-context`, `--event-globals`; on dark grounds only |
| Events | `--event-entity`, `--event-action`, `--event-property`, `--event-context`, `--event-globals`; dark grounds, and a tag card's Done |
| Flow steps | `--step-source`, `--step-transformer`, `--step-collector`, `--step-destination`, `--step-store`, `--platform-web`, `--platform-server` |
| Code | `--code-bg`, `--code-bar`, `--code-border`, `--code-fg`; editor colours are the `syntax-*` constants |
| Product visuals | `--viz-*` |
Expand Down Expand Up @@ -640,7 +640,9 @@ its own root.
grounds; control edges, the focus ring and step colours 3:1.
- `--primary` is a fill under `--on-primary`; text in the brand colour is
`--link`.
- Event colours sit only on dark grounds (a dark island).
- Event colours sit only on dark grounds (a dark island). One exception: a tag
card's Done is filled with its kind colour and takes `--on-event` for its
text; the colour names the kind, the word names the action.
- Every status shows with an icon and a word, never by colour alone. Any other
state a colour tells also gets a glyph, an edge style or a shape, and its word
(visible, or in the tooltip and the accessible name where it does not fit).
Expand Down
2 changes: 1 addition & 1 deletion apps/explorer/design/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ The app, the explorer and Tag Mode use these tokens with a few more rules.
- **Status.** `danger`, `success`, `warning` and `info` always come with an icon and a word. On a tinted `*-bg` fill the text is `fg`; the status colour marks the icon and the border. `success` sits on the blue side of green so it stays apart from `danger` with red-green colour blindness. Any other state a hue tells (seen, not seen yet, added, removed) also gets a cue without colour, an edge style, a glyph or a shape, and its word: visible where it fits, otherwise in the tooltip and the accessible name.
- **Flow steps.** `step-source`, `step-transformer`, `step-collector`, `step-destination` and `step-store` colour step badges, node accents and edges; `platform-web` and `platform-server` mark platforms. Always beside the word.
- **Charts and notes.** Series take `chart-1` to `chart-6` in order, each labelled. `annotation` marks human notes and comment pins.
- **Tag kinds.** The five event parts have a dark value for dark grounds and a light value that reads on the light theme's page grounds, so a key on light chrome (a tool button's word) can take its event colour directly. Mark layers, legends, code and visualisations stay dark islands and show the dark values in both themes. `event-user` and `event-consent` have the dark value only: dark grounds only. Every event colour always comes with its word.
- **Tag kinds.** The five event parts have a dark value for dark grounds and a light value that reads on the light theme's page grounds, so a key on light chrome (a tool button's word) can take its event colour directly. Mark layers, legends, code and visualisations stay dark islands and show the dark values in both themes. `event-user` and `event-consent` have the dark value only: dark grounds only. Every event colour always comes with its word. One exception to never using these hues for UI states: a tag card's one action, Done, is filled with its kind colour and takes `on-event` for its text. The colour names the kind; the word names the action.
- **Editors.** Monaco, Shiki and Prism use `syntax-*` on `code-bg`, with `code-line-number`, `code-selection`, `code-inserted-bg` and `code-deleted-bg`. Visualisations keep `viz-*`. Code surfaces stay dark in both themes.
- **Tailwind.** `tokens.css` declares `--radius-*`, `--font-sans` and `--font-mono` under Tailwind's own names, so importing it alone already re-scales Tailwind's `rounded-*` (`rounded-lg` becomes 12px) and re-fonts `font-sans` and `font-mono`. Move radius classes to the design scale in the same change (`rounded-lg` to `rounded-md`, `rounded-xl` to `rounded-lg`). `tailwind.css` then removes the palette, the default radii, sizes and shadows, and maps `max-w-*` to the `container-*` widths.

Expand Down
8 changes: 8 additions & 0 deletions apps/explorer/design/tokens.json
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,14 @@
},
"usage": "Text and icons on a `danger` fill such as a destructive button (dark 8.6:1, light 6.5:1)."
},
{
"name": "on-event",
"value": {
"dark": "#111827",
"light": "#ffffff"
},
"usage": "Text and icons on an event-colour fill: a tag card's Done. Dark ink on the dark event values (5.6:1 or more), white on the light ones (about 5:1)."
},
{
"name": "success",
"value": {
Expand Down
3 changes: 3 additions & 0 deletions apps/explorer/src/design/__tests__/contrast.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@ const PAIRS: readonly Pair[] = [
...['fg', 'fg-2', 'fg-3', 'link', ...STATUS].map((fg) => text(fg, GROUNDS)),
text('on-primary', ['primary']),
text('on-danger', ['danger']),
// A tag card's Done is filled with its kind colour: the event part's dark
// value in the dark theme, its light value in the light theme.
...THEMED_EVENTS.map((ground) => text('on-event', [ground])),
...STATUS.map(
(status): Pair => ({ ...text('fg', [`${status}-bg`]), over: GROUNDS }),
),
Expand Down
32 changes: 32 additions & 0 deletions apps/explorer/src/design/components/atoms/CodeTokens.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React from 'react';
import type { CodeToken } from '../codeTokens';
import { cx } from '../cx';

/**
* Code runs in their colours, for a dark ground: the demos and the code panes
* of product UI. A marked run tints only while `active`.
*/
export function CodeTokens({
tokens,
active = false,
}: {
tokens: readonly CodeToken[];
active?: boolean;
}) {
return (
<>
{tokens.map((token, index) => (
<span
key={index}
className={cx(
'elb-viz-tok',
`elb-viz-tok--${token.kind}`,
active && token.mark && `elb-viz-tok--mark-${token.mark}`,
)}
>
{token.t}
</span>
))}
</>
);
}
51 changes: 4 additions & 47 deletions apps/explorer/src/design/components/atoms/InstallCommand.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
import React, {
useEffect,
useId,
useRef,
useState,
type ButtonHTMLAttributes,
} from 'react';
import React, { useId, type ButtonHTMLAttributes } from 'react';
import { cx } from '../cx';
import { Icon } from './Icon';
import { useCopy } from './useCopy';

export interface InstallCommandProps extends Omit<
ButtonHTMLAttributes<HTMLButtonElement>,
Expand All @@ -16,24 +11,6 @@ export interface InstallCommandProps extends Omit<
command?: string;
}

type CopyState = 'idle' | 'done' | 'failed';

/** How long the check stays after a successful copy. */
const CONFIRM_MS = 1400;

/**
* True only when the clipboard took the text. A missing clipboard API (http,
* older browsers) throws on access, which counts as a failure like a rejection.
*/
async function copyText(text: string): Promise<boolean> {
try {
await navigator.clipboard.writeText(text);
return true;
} catch {
return false;
}
}

/**
* A copy-to-clipboard chip for a CLI command. It confirms with a check only when
* the copy succeeded, and says "Copy failed" otherwise. The button's accessible
Expand All @@ -48,25 +25,7 @@ export function InstallCommand({
...rest
}: InstallCommandProps) {
const hintId = useId();
const [state, setState] = useState<CopyState>('idle');
const mounted = useRef(true);
const timer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);

useEffect(() => {
mounted.current = true;
return () => {
mounted.current = false;
clearTimeout(timer.current);
};
}, []);

const copy = async (): Promise<void> => {
const copied = await copyText(command);
if (!mounted.current) return;
clearTimeout(timer.current);
setState(copied ? 'done' : 'failed');
if (copied) timer.current = setTimeout(() => setState('idle'), CONFIRM_MS);
};
const [state, copy] = useCopy();

return (
<>
Expand All @@ -75,9 +34,7 @@ export function InstallCommand({
aria-describedby={describedBy ? `${hintId} ${describedBy}` : hintId}
type="button"
className={cx('elb-cmd', className)}
onClick={() => {
void copy();
}}
onClick={() => copy(command)}
>
<span>
<span className="elb-cmd__prompt" aria-hidden="true">
Expand Down
52 changes: 52 additions & 0 deletions apps/explorer/src/design/components/atoms/useCopy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { useEffect, useRef, useState } from 'react';

export type CopyState = 'idle' | 'done' | 'failed';

/** How long `done` stays after a successful copy. */
const CONFIRM_MS = 1400;

/**
* True only when the clipboard took the text. A missing clipboard API (http,
* older browsers) throws on access, which counts as a failure like a rejection.
*/
async function copyText(text: string): Promise<boolean> {
try {
await navigator.clipboard.writeText(text);
return true;
} catch {
return false;
}
}

/**
* Copies a text to the clipboard and says how it went: `done` for 1.4 s after
* the clipboard took it, `failed` until the next try.
*/
export function useCopy(): readonly [CopyState, (text: string) => void] {
const [state, setState] = useState<CopyState>('idle');
const mounted = useRef(true);
const timer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);

useEffect(() => {
mounted.current = true;
return () => {
mounted.current = false;
clearTimeout(timer.current);
};
}, []);

const copy = async (text: string): Promise<void> => {
const copied = await copyText(text);
if (!mounted.current) return;
clearTimeout(timer.current);
setState(copied ? 'done' : 'failed');
if (copied) timer.current = setTimeout(() => setState('idle'), CONFIRM_MS);
};

return [
state,
(text) => {
void copy(text);
},
];
}
31 changes: 31 additions & 0 deletions apps/explorer/src/design/components/codeTokens.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/** The colour role of one run of code. */
export type TokenKind =
| 'tag'
| 'attr'
| 'punct'
| 'str'
| 'text'
| 'kw'
| 'num'
| 'fn'
| 'key'
| 'value';

/** The five parts of a walkerOS event; each has its own colour. */
export type EventPart =
| 'entity'
| 'action'
| 'property'
| 'context'
| 'globals';

/** One run of code text; `mark` tints it in an event part's colour. */
export interface CodeToken {
readonly t: string;
readonly kind: TokenKind;
readonly mark?: EventPart;
}

export function tok(t: string, kind: TokenKind, mark?: EventPart): CodeToken {
return mark ? { t, kind, mark } : { t, kind };
}
Loading
Loading