diff --git a/apps/web/src/components/hero/airship-mock/agent-timeline.tsx b/apps/web/src/components/hero/airship-mock/agent-timeline.tsx new file mode 100644 index 0000000..637fe82 --- /dev/null +++ b/apps/web/src/components/hero/airship-mock/agent-timeline.tsx @@ -0,0 +1,33 @@ +import { MOCK_TOOL_CALLS } from "#/content/mock-page"; + +/** + * The agent's tool calls, streaming. + * + * Rendered in Claude Code's own transcript grammar — a filled marker and a bold + * tool name for the call, an indented `⎿` elbow for its result — because that is + * exactly what airship's real overlay renders. Someone who has used the tool + * should recognise this before they read a word of it. + * + * Every row is present in the DOM from the start and revealed by the timeline; + * nothing is mounted or unmounted, so the transcript's height never jumps + * mid-stream. + */ +export function AgentTimeline() { + return ( +
+ {MOCK_TOOL_CALLS.map((call, index) => ( +
+
+ ● + {call.tool} + {call.args} +
+
+ ⌋ + {call.result} +
+
+ ))} +
+ ); +} diff --git a/apps/web/src/components/hero/airship-mock/bottom-bar.tsx b/apps/web/src/components/hero/airship-mock/bottom-bar.tsx new file mode 100644 index 0000000..3c2ac98 --- /dev/null +++ b/apps/web/src/components/hero/airship-mock/bottom-bar.tsx @@ -0,0 +1,70 @@ +import { EditorGlyph } from "#/components/hero/airship-mock/editor-glyph"; + +/** + * The floating bottom bar. + * + * Compact and understated on purpose: a small radius, a hairline border and a + * ring rather than a pill with a drop shadow. It is the one piece of chrome that + * is always on screen, and the version of it that announces itself gets tiring + * within a minute. + * + * Left to right: undo/redo, the tool group, inspect, the Edit|View toggle, and + * the pending-tweak counter — which is the only thing on the bar that moves. + */ +export function BottomBar() { + return ( +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Edit + + + + View + + + + {/* + Collapsed to zero width until the first tweak lands, so the bar has no + empty slot waiting to be filled — it simply grows. + */} + + + 1 + 2 + + +
+ ); +} diff --git a/apps/web/src/components/hero/airship-mock/composer-field.tsx b/apps/web/src/components/hero/airship-mock/composer-field.tsx new file mode 100644 index 0000000..4c92542 --- /dev/null +++ b/apps/web/src/components/hero/airship-mock/composer-field.tsx @@ -0,0 +1,40 @@ +import { EditorGlyph } from "#/components/hero/airship-mock/editor-glyph"; +import { MOCK_PROMPT, MOCK_SELECTION } from "#/content/mock-page"; + +/** + * Where you say what you want. + * + * One bordered box holding the selection chip, the prompt and the send button — + * which is the shape of airship's real composer, and the shape of the claim: the + * chip says *what*, the sentence says *what about it*, and that is the entire + * interface. + * + * The prompt is real text clipped from the right by the timeline rather than a + * string assembled by a timer. That keeps the typing honest under the pause + * button and under `prefers-reduced-motion`, and means it costs nothing when the + * hero is scrolled out of view. + */ +export function ComposerField() { + return ( +
+
+
+ + {MOCK_SELECTION.tag} + × + +
+ +
+ Describe the change… + {MOCK_PROMPT} + +
+ + + + +
+
+ ); +} diff --git a/apps/web/src/components/hero/airship-mock/design-dock.tsx b/apps/web/src/components/hero/airship-mock/design-dock.tsx new file mode 100644 index 0000000..62ff265 --- /dev/null +++ b/apps/web/src/components/hero/airship-mock/design-dock.tsx @@ -0,0 +1,214 @@ +import { AgentTimeline } from "#/components/hero/airship-mock/agent-timeline"; +import { ComposerField } from "#/components/hero/airship-mock/composer-field"; +import { DiffCard } from "#/components/hero/airship-mock/diff-card"; +import { EditorGlyph } from "#/components/hero/airship-mock/editor-glyph"; +import { + NumField, + PaintRow, + Segmented, + Select, + SwapValue, +} from "#/components/hero/airship-mock/inspector-controls"; +import { InspectorSection } from "#/components/hero/airship-mock/inspector-section"; +import { MOCK_PROMPT, MOCK_SELECTION, MOCK_STATUS } from "#/content/mock-page"; +import { TWEAKS } from "#/content/timeline"; + +/* + * The nine align cells, in the real panel's order: horizontal, vertical, then + * distribute-and-tidy. + */ +const ALIGN_GROUPS = [ + ["alignLeft", "alignCenterH", "alignRight"], + ["alignTop", "alignCenterV", "alignBottom"], + ["distributeH", "distributeV", "tidy"], +] as const; + +const TABS = [ + { glyph: "logo", label: "Agent" }, + { glyph: "design", label: "Edit" }, + { glyph: "code", label: "CSS" }, + { glyph: "layers", label: "DOM" }, +] as const; + +/** + * One dock, four tabs. + * + * ── A deliberate departure from the real editor ────────────────────────────── + * airship actually runs TWO docks: chat on the left at 340px, inspector on the + * right at 360px. This mock folds the chat in as an *Agent* tab instead. + * + * That is a simplification for the hero specifically, and it is worth being + * honest about. Two 200px panels flanking a 285px strip of page reads as clutter + * at this render scale, and it splits attention exactly where the page is trying + * to make one point. One panel with the agent in front says the same thing more + * quietly — and the Edit / CSS / DOM tabs beside it still show that a full + * inspector is part of the product. + * + * Everything INSIDE the tabs is transcribed 1:1 as usual. It is the arrangement + * that is editorial, not the components. + */ +export function DesignDock() { + return ( +
+
+ + + Airship + + + + + + + + + +
+ + {/* + Empty and filled states stack in one relative box and cross-fade, so + neither ever reflows the dock. + */} +
+
+ + Ask airship to change anything + + Pick an element first to scope it. + +
+ +
+
+ {TABS.map((tab) => ( + + + {tab.label} + + ))} +
+ + {/* Both tab bodies stacked; the timeline decides which is in front. */} +
+
+
+
{MOCK_PROMPT}
+ +
+ + +
+ + {/* Both labels in one grid cell, so the row does not + re-wrap when "Applying…" becomes "Done". */} + + + {MOCK_STATUS.working} + + {MOCK_STATUS.done} + +
+ + +
+
+ + +
+ +
+
+ + + {MOCK_SELECTION.sourceFile}:{MOCK_SELECTION.sourceLine} + + + +
+ {ALIGN_GROUPS.map((group) => ( + + {group.map((glyph) => ( + + + + ))} + + ))} +
+ + +
+ + +
+
+ + + + + + +
+ + + } + /> +
+
+ Blend +