chore(skills): add frame0-wireframe skill for UI wireframing
Replaces Frame0 MCP server with portable bash+curl scripts that call Frame0's local HTTP API directly. Includes low-level API wrapper, high-level composition helpers with project styling, component library patterns, and setup guide. Requires Frame0 desktop app running locally. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,161 @@
|
|||||||
|
---
|
||||||
|
name: frame0-wireframe
|
||||||
|
description: >
|
||||||
|
Create and export UI wireframes using Frame0 (local desktop wireframing
|
||||||
|
app with HTTP API). Use when the user says "create wireframe", "wireframe
|
||||||
|
this", "mock up the UI", "draw a screen", "UI layout", "wireframe the HUD",
|
||||||
|
"Frame0", "frame0", "export wireframe", or invokes /frame0-wireframe.
|
||||||
|
Drives Frame0 via its local HTTP API to create shapes, connectors, and
|
||||||
|
export pages. Also use when asked to update existing wireframes or export
|
||||||
|
pages. Requires Frame0 to be running locally.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Frame0 Wireframe Generation
|
||||||
|
|
||||||
|
Create UI wireframes by driving Frame0's local HTTP API via bash+curl scripts.
|
||||||
|
No MCP dependency — the scripts replace the MCP server entirely.
|
||||||
|
|
||||||
|
**Prerequisite:** Frame0 desktop app must be running. The API has no headless
|
||||||
|
mode. If Frame0 is not available, stop and inform the user.
|
||||||
|
|
||||||
|
## Health Check
|
||||||
|
|
||||||
|
Always check first:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
.claude/skills/frame0-wireframe/scripts/frame0-cmd.sh health
|
||||||
|
```
|
||||||
|
|
||||||
|
If Frame0 is not running, report the failure and point to
|
||||||
|
`references/setup-guide.md`. Do not attempt to proceed without a healthy
|
||||||
|
connection.
|
||||||
|
|
||||||
|
## API Pattern
|
||||||
|
|
||||||
|
All Frame0 interaction goes through the bundled scripts. Never call curl
|
||||||
|
directly.
|
||||||
|
|
||||||
|
- **`frame0-cmd.sh`** — Low-level API wrapper. Maps subcommands to Frame0's
|
||||||
|
HTTP endpoint at `POST localhost:{port}/execute_command`.
|
||||||
|
- **`frame0-wireframe.sh`** — High-level composition helpers with project
|
||||||
|
styling defaults (colors from visual-grammar-v01.md).
|
||||||
|
|
||||||
|
Port default: **58320** (override: `FRAME0_PORT` env var or `--port` flag).
|
||||||
|
|
||||||
|
## Single Wireframe Workflow
|
||||||
|
|
||||||
|
1. **Health check** — `frame0-cmd.sh health`
|
||||||
|
2. **Create page** — `frame0-wireframe.sh new-page "Screen Name"`
|
||||||
|
3. **Add components** — use high-level helpers for standard elements:
|
||||||
|
```bash
|
||||||
|
CMD=".claude/skills/frame0-wireframe/scripts/frame0-wireframe.sh"
|
||||||
|
$CMD button "Confirm" 100 200
|
||||||
|
$CMD text-field "Search..." 100 250 300
|
||||||
|
$CMD container "Panel Title" 50 50 400 300
|
||||||
|
$CMD label "Description text" 60 80
|
||||||
|
$CMD divider 50 120 400
|
||||||
|
```
|
||||||
|
4. **Fine-grained control** — use `frame0-cmd.sh` for operations not covered
|
||||||
|
by helpers (connectors, icons, grouping):
|
||||||
|
```bash
|
||||||
|
CMD=".claude/skills/frame0-wireframe/scripts/frame0-cmd.sh"
|
||||||
|
$CMD create-connector "shape-id-1" "shape-id-2"
|
||||||
|
$CMD group "shape-id-1" "shape-id-2" "shape-id-3"
|
||||||
|
$CMD create-icon "search" '{"left":100,"top":100,"width":24,"height":24}'
|
||||||
|
```
|
||||||
|
5. **Export** — `frame0-wireframe.sh export png docs/design/wireframes/hud/layout-v1.png`
|
||||||
|
|
||||||
|
## High-Level Helpers
|
||||||
|
|
||||||
|
| Command | Default size | Description |
|
||||||
|
|---------|-------------|-------------|
|
||||||
|
| `new-page <title>` | — | Create page with title label |
|
||||||
|
| `button <label> <x> <y> [w] [h]` | 120x36 | Styled button with accent border |
|
||||||
|
| `text-field <placeholder> <x> <y> [w] [h]` | 200x32 | Input field with dark fill |
|
||||||
|
| `container <label> <x> <y> <w> <h>` | — | Labeled panel with border |
|
||||||
|
| `label <text> <x> <y> [size]` | font 14 | Text label |
|
||||||
|
| `divider <x> <y> <length> [h\|v]` | horizontal | Separator line |
|
||||||
|
| `export <format> <output-path>` | — | Export current page (png/svg) |
|
||||||
|
|
||||||
|
## Low-Level Commands
|
||||||
|
|
||||||
|
| Command | Purpose |
|
||||||
|
|---------|---------|
|
||||||
|
| `health` | Check if Frame0 is running |
|
||||||
|
| `exec <namespace:action> <json>` | Execute any raw API command |
|
||||||
|
| `create-shape <type> <json-props>` | Create rectangle, ellipse, text, etc. |
|
||||||
|
| `update-shape <id> <json-props>` | Modify shape properties |
|
||||||
|
| `delete <id...>` | Delete shapes |
|
||||||
|
| `move <id> <dx> <dy>` | Move shape by pixel offset |
|
||||||
|
| `group <id...>` | Group shapes |
|
||||||
|
| `ungroup <id...>` | Ungroup shapes |
|
||||||
|
| `create-connector <tail> <head>` | Connect two shapes |
|
||||||
|
| `create-icon <name> <json-props>` | Add icon |
|
||||||
|
| `add-page <json-props>` | Add page |
|
||||||
|
| `get-page [id]` | Get current/specific page |
|
||||||
|
| `list-pages` | List all pages |
|
||||||
|
| `set-page <id>` | Set current page |
|
||||||
|
| `export <page-id> <format>` | Export as PNG/SVG |
|
||||||
|
|
||||||
|
## Project Styling Defaults
|
||||||
|
|
||||||
|
Colors from `docs/design/visual-grammar-v01.md`:
|
||||||
|
|
||||||
|
| Role | Hex | Source |
|
||||||
|
|------|-----|--------|
|
||||||
|
| Background | `#1a1e24` | Zone 1 floor |
|
||||||
|
| Stroke | `#333340` | Outline standard |
|
||||||
|
| Fill | `#2a3040` | Zone 1 wall |
|
||||||
|
| Text | `#c8d0e0` | Insert chrome |
|
||||||
|
| Accent | `#c8d8f0` | Zone 1 fixture light |
|
||||||
|
|
||||||
|
## Export Convention
|
||||||
|
|
||||||
|
```
|
||||||
|
docs/design/wireframes/
|
||||||
|
hud/ # HUD layout wireframes
|
||||||
|
menus/ # Menu screen wireframes
|
||||||
|
dialogue/ # Dialogue box wireframes
|
||||||
|
insert/ # Neural insert wireframes
|
||||||
|
```
|
||||||
|
|
||||||
|
## Composition Workflow
|
||||||
|
|
||||||
|
For multi-element wireframes:
|
||||||
|
|
||||||
|
1. Read `references/component-library.md` for the matching UI pattern
|
||||||
|
2. Adapt dimensions and positions to the wireframe layout
|
||||||
|
3. Group related elements: `frame0-cmd.sh group <id1> <id2> ...`
|
||||||
|
4. Add connectors for navigation flow: `frame0-cmd.sh create-connector`
|
||||||
|
5. Export the finished wireframe
|
||||||
|
|
||||||
|
## Multi-Page Wireframes
|
||||||
|
|
||||||
|
Create wireframe sets showing multiple states or screens:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
CMD=".claude/skills/frame0-wireframe/scripts/frame0-wireframe.sh"
|
||||||
|
$CMD new-page "HUD - Normal"
|
||||||
|
# ... add components ...
|
||||||
|
$CMD new-page "HUD - Alert"
|
||||||
|
# ... add components ...
|
||||||
|
$CMD new-page "HUD - Combat"
|
||||||
|
# ... add components ...
|
||||||
|
```
|
||||||
|
|
||||||
|
## Agent Guidance
|
||||||
|
|
||||||
|
- **Araminta** — Primary user. Full wireframe creation, layout iteration,
|
||||||
|
visual consistency. Uses all component library patterns.
|
||||||
|
- **Tyre** — Interface architecture wireframes. System boundary diagrams
|
||||||
|
showing where UI connects to game systems.
|
||||||
|
- **Qatux** — Export wireframes for UI decision records and documentation.
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- `references/api-reference.md` — Full Frame0 HTTP API command and property
|
||||||
|
reference. Read for fine-grained control beyond helpers.
|
||||||
|
- `references/component-library.md` — Pre-built wireframe patterns (HUD,
|
||||||
|
dialogue, menus, modals, lists, inventory). Read when starting a wireframe.
|
||||||
|
- `references/setup-guide.md` — Frame0 installation and startup for Fedora.
|
||||||
|
Read if Frame0 is not installed or not running.
|
||||||
@@ -0,0 +1,285 @@
|
|||||||
|
# Frame0 HTTP API Reference
|
||||||
|
|
||||||
|
Frame0 exposes a local HTTP API when the desktop app is running.
|
||||||
|
|
||||||
|
## Endpoint
|
||||||
|
|
||||||
|
```
|
||||||
|
POST http://localhost:{port}/execute_command
|
||||||
|
Content-Type: application/json
|
||||||
|
```
|
||||||
|
|
||||||
|
Default port: **58320** (override via `FRAME0_PORT` env var or `--port` flag).
|
||||||
|
|
||||||
|
## Request Format
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"command": "namespace:action",
|
||||||
|
"args": { ... }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Response Format
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"data": { ... }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
On error:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": false,
|
||||||
|
"error": "Error description"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Commands by Namespace
|
||||||
|
|
||||||
|
### shape — Shape Creation and Manipulation
|
||||||
|
|
||||||
|
#### shape:create-shape
|
||||||
|
|
||||||
|
Create a new shape on the current page.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"command": "shape:create-shape",
|
||||||
|
"args": {
|
||||||
|
"type": "rectangle",
|
||||||
|
"shapeProps": {
|
||||||
|
"name": "my-button",
|
||||||
|
"left": 100,
|
||||||
|
"top": 200,
|
||||||
|
"width": 120,
|
||||||
|
"height": 36,
|
||||||
|
"fillColor": "#2a3040",
|
||||||
|
"strokeColor": "#333340",
|
||||||
|
"fontColor": "#c8d0e0",
|
||||||
|
"fontSize": 14,
|
||||||
|
"text": "Button Label",
|
||||||
|
"corners": [4, 4, 4, 4]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Shape types:** `rectangle`, `ellipse`, `triangle`, `diamond`, `line`, `text`, `image`, `icon`
|
||||||
|
|
||||||
|
#### shape:update-shape
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"command": "shape:update-shape",
|
||||||
|
"args": {
|
||||||
|
"shapeId": "shape-id",
|
||||||
|
"shapeProps": {
|
||||||
|
"fillColor": "#1a1e24",
|
||||||
|
"text": "Updated Label"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### shape:move
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"command": "shape:move",
|
||||||
|
"args": {
|
||||||
|
"shapeId": "shape-id",
|
||||||
|
"dx": 50,
|
||||||
|
"dy": -20
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### shape:group / shape:ungroup
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"command": "shape:group",
|
||||||
|
"args": {
|
||||||
|
"shapeIdArray": ["id-1", "id-2", "id-3"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### shape:create-connector
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"command": "shape:create-connector",
|
||||||
|
"args": {
|
||||||
|
"tailId": "source-shape-id",
|
||||||
|
"headId": "target-shape-id",
|
||||||
|
"shapeProps": {
|
||||||
|
"strokeColor": "#c8d8f0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### shape:create-icon
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"command": "shape:create-icon",
|
||||||
|
"args": {
|
||||||
|
"iconName": "search",
|
||||||
|
"shapeProps": {
|
||||||
|
"left": 100,
|
||||||
|
"top": 100,
|
||||||
|
"width": 24,
|
||||||
|
"height": 24,
|
||||||
|
"strokeColor": "#c8d0e0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Icon sizes: 16, 24, 32, 48 pixels.
|
||||||
|
|
||||||
|
#### shape:create-image
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"command": "shape:create-image",
|
||||||
|
"args": {
|
||||||
|
"mimeType": "image/png",
|
||||||
|
"imageData": "base64-encoded-data",
|
||||||
|
"shapeProps": {
|
||||||
|
"left": 100,
|
||||||
|
"top": 100,
|
||||||
|
"width": 200,
|
||||||
|
"height": 150
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### edit — Editing Operations
|
||||||
|
|
||||||
|
#### edit:delete
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"command": "edit:delete",
|
||||||
|
"args": {
|
||||||
|
"shapeIdArray": ["id-1", "id-2"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### page — Page Management
|
||||||
|
|
||||||
|
#### page:add
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"command": "page:add",
|
||||||
|
"args": {
|
||||||
|
"pageProps": {
|
||||||
|
"name": "HUD Layout"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### page:get
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"command": "page:get",
|
||||||
|
"args": {
|
||||||
|
"pageId": "page-id"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Omit `pageId` to get current page.
|
||||||
|
|
||||||
|
#### page:get-all
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"command": "page:get-all",
|
||||||
|
"args": {}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### page:set-current
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"command": "page:set-current",
|
||||||
|
"args": {
|
||||||
|
"pageId": "page-id"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### page:delete
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"command": "page:delete",
|
||||||
|
"args": {
|
||||||
|
"pageId": "page-id"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### file — Export
|
||||||
|
|
||||||
|
#### file:export-image
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"command": "file:export-image",
|
||||||
|
"args": {
|
||||||
|
"pageId": "page-id",
|
||||||
|
"format": "png",
|
||||||
|
"fillBackground": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Formats: `png`, `svg`
|
||||||
|
|
||||||
|
Optional: `"shapeIdArray": ["id-1"]` to export specific shapes only.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Shape Properties Reference
|
||||||
|
|
||||||
|
| Property | Type | Notes |
|
||||||
|
|----------|------|-------|
|
||||||
|
| `name` | string | Shape identifier/label |
|
||||||
|
| `left` | number | X position in pixels (origin: top-left) |
|
||||||
|
| `top` | number | Y position in pixels |
|
||||||
|
| `width` | number | Width in pixels |
|
||||||
|
| `height` | number | Height in pixels |
|
||||||
|
| `fillColor` | string | Hex color (e.g., `"#2a3040"`) |
|
||||||
|
| `strokeColor` | string | Hex color for border |
|
||||||
|
| `fontColor` | string | Hex color for text |
|
||||||
|
| `fontSize` | number | Font size in pixels |
|
||||||
|
| `text` | string | Text content (for shapes with text) |
|
||||||
|
| `wordWrap` | boolean | Enable text word wrapping |
|
||||||
|
| `corners` | number[4] | Border radius [topLeft, topRight, bottomRight, bottomLeft] |
|
||||||
|
| `path` | array | Coordinate pairs for lines/polygons |
|
||||||
|
| `lineType` | string | Line style |
|
||||||
|
| `tailEndType` | string | Arrow tail type |
|
||||||
|
| `headEndType` | string | Arrow head type |
|
||||||
|
|
||||||
|
## Coordinate System
|
||||||
|
|
||||||
|
- Origin: top-left corner of the canvas
|
||||||
|
- Units: pixels
|
||||||
|
- X increases rightward, Y increases downward
|
||||||
@@ -0,0 +1,193 @@
|
|||||||
|
# Component Library
|
||||||
|
|
||||||
|
Pre-built wireframe patterns for The Settled Reach UI. Each pattern provides
|
||||||
|
`frame0-wireframe.sh` command sequences. Colors from visual-grammar-v01.md.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. HUD Layout
|
||||||
|
|
||||||
|
The main gameplay overlay. Minimap top-right, monologue bottom-center,
|
||||||
|
insert display bottom-left.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
CMD=".claude/skills/frame0-wireframe/scripts/frame0-wireframe.sh"
|
||||||
|
|
||||||
|
$CMD new-page "HUD Layout"
|
||||||
|
|
||||||
|
# Minimap (top-right)
|
||||||
|
$CMD container "Minimap" 880 20 240 240
|
||||||
|
|
||||||
|
# Monologue panel (bottom-center)
|
||||||
|
$CMD container "Monologue" 300 680 520 80
|
||||||
|
$CMD label "Internal monologue text appears here..." 310 700
|
||||||
|
|
||||||
|
# Insert display (bottom-left)
|
||||||
|
$CMD container "Insert Display" 20 600 260 160
|
||||||
|
$CMD label "Neural Insert Data" 30 620
|
||||||
|
|
||||||
|
# Action hints (bottom-right)
|
||||||
|
$CMD container "Action Hints" 880 700 240 60
|
||||||
|
$CMD label "[E] Interact [TAB] Insert" 890 720 12
|
||||||
|
```
|
||||||
|
|
||||||
|
**Viewport assumption:** 1140x780 (matching Godot project viewport).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Dialogue Box
|
||||||
|
|
||||||
|
Speaker panel with response options. Anchored bottom-center during dialogue mode.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
CMD=".claude/skills/frame0-wireframe/scripts/frame0-wireframe.sh"
|
||||||
|
|
||||||
|
$CMD new-page "Dialogue Box"
|
||||||
|
|
||||||
|
# Dialogue container
|
||||||
|
$CMD container "Dialogue" 170 500 800 260
|
||||||
|
|
||||||
|
# Speaker name
|
||||||
|
$CMD label "LERA KONSTANTIN" 190 520 16
|
||||||
|
|
||||||
|
# Dialogue text area
|
||||||
|
$CMD container "Text Area" 190 550 760 100
|
||||||
|
$CMD label "You look like you could use a drink. First time on the station?" 200 560
|
||||||
|
|
||||||
|
# Response options
|
||||||
|
$CMD button "[1] Ask about the station" 190 670 370 30
|
||||||
|
$CMD button "[2] Ask about recent events" 190 710 370 30
|
||||||
|
$CMD button "[3] Leave" 580 670 180 30
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Menu Screen
|
||||||
|
|
||||||
|
Full-screen menu with sidebar navigation and content area.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
CMD=".claude/skills/frame0-wireframe/scripts/frame0-wireframe.sh"
|
||||||
|
|
||||||
|
$CMD new-page "Pause Menu"
|
||||||
|
|
||||||
|
# Background overlay
|
||||||
|
$CMD container "Menu Background" 0 0 1140 780
|
||||||
|
|
||||||
|
# Sidebar navigation
|
||||||
|
$CMD container "Navigation" 20 20 200 740
|
||||||
|
$CMD button "Inventory" 30 40 180 36
|
||||||
|
$CMD button "Journal" 30 86 180 36
|
||||||
|
$CMD button "Map" 30 132 180 36
|
||||||
|
$CMD button "Settings" 30 178 180 36
|
||||||
|
$CMD button "Resume" 30 720 180 36
|
||||||
|
|
||||||
|
# Content area
|
||||||
|
$CMD container "Content" 240 20 880 740
|
||||||
|
$CMD label "Content area" 260 40
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Modal Dialog
|
||||||
|
|
||||||
|
Centered overlay for confirmations, alerts, choices.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
CMD=".claude/skills/frame0-wireframe/scripts/frame0-wireframe.sh"
|
||||||
|
|
||||||
|
$CMD new-page "Modal Dialog"
|
||||||
|
|
||||||
|
# Overlay background (semi-transparent implied)
|
||||||
|
$CMD container "Overlay" 0 0 1140 780
|
||||||
|
|
||||||
|
# Modal panel (centered)
|
||||||
|
$CMD container "Modal" 320 240 500 300
|
||||||
|
|
||||||
|
# Title
|
||||||
|
$CMD label "Confirm Action" 340 260 18
|
||||||
|
|
||||||
|
# Divider
|
||||||
|
$CMD divider 340 290 460
|
||||||
|
|
||||||
|
# Body text
|
||||||
|
$CMD label "Are you sure you want to proceed?" 340 310
|
||||||
|
$CMD label "This action cannot be undone." 340 340
|
||||||
|
|
||||||
|
# Action buttons
|
||||||
|
$CMD button "Cancel" 480 480 120 36
|
||||||
|
$CMD button "Confirm" 620 480 120 36
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. List View
|
||||||
|
|
||||||
|
Scrollable list with item selection and detail panel.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
CMD=".claude/skills/frame0-wireframe/scripts/frame0-wireframe.sh"
|
||||||
|
|
||||||
|
$CMD new-page "List View"
|
||||||
|
|
||||||
|
# List panel
|
||||||
|
$CMD container "Item List" 20 20 400 740
|
||||||
|
|
||||||
|
# List items (repeating pattern)
|
||||||
|
$CMD button "Item Alpha" 30 30 380 40
|
||||||
|
$CMD button "Item Beta" 30 80 380 40
|
||||||
|
$CMD button "Item Gamma" 30 130 380 40
|
||||||
|
$CMD button "Item Delta" 30 180 380 40
|
||||||
|
|
||||||
|
# Detail panel
|
||||||
|
$CMD container "Detail" 440 20 680 740
|
||||||
|
$CMD label "Item Alpha" 460 40 18
|
||||||
|
$CMD divider 460 70 640
|
||||||
|
$CMD label "Description and properties appear here." 460 90
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Inventory Grid
|
||||||
|
|
||||||
|
Grid of cells for item management.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
CMD=".claude/skills/frame0-wireframe/scripts/frame0-wireframe.sh"
|
||||||
|
|
||||||
|
$CMD new-page "Inventory Grid"
|
||||||
|
|
||||||
|
# Grid container
|
||||||
|
$CMD container "Inventory" 240 100 660 580
|
||||||
|
|
||||||
|
# Grid header
|
||||||
|
$CMD label "INVENTORY" 260 120 18
|
||||||
|
|
||||||
|
# Grid cells (4x4 example, 64px each with 8px gaps)
|
||||||
|
# Row 1
|
||||||
|
$CMD container "" 260 160 64 64
|
||||||
|
$CMD container "" 332 160 64 64
|
||||||
|
$CMD container "" 404 160 64 64
|
||||||
|
$CMD container "" 476 160 64 64
|
||||||
|
|
||||||
|
# Row 2
|
||||||
|
$CMD container "" 260 232 64 64
|
||||||
|
$CMD container "" 332 232 64 64
|
||||||
|
$CMD container "" 404 232 64 64
|
||||||
|
$CMD container "" 476 232 64 64
|
||||||
|
|
||||||
|
# Selected item detail
|
||||||
|
$CMD container "Item Detail" 580 160 300 400
|
||||||
|
$CMD label "Selected Item Name" 600 180 16
|
||||||
|
$CMD divider 600 210 260
|
||||||
|
$CMD label "Item description and stats" 600 230
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Viewport and Grid
|
||||||
|
|
||||||
|
- **Project viewport:** 1140x780 (from Godot project settings)
|
||||||
|
- **Grid unit:** 8px (for consistent spacing)
|
||||||
|
- **Minimum touch target:** 36px height for interactive elements
|
||||||
|
- **Font sizes:** 12 (small/label), 14 (body), 16 (subtitle), 18 (heading), 24 (title)
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
# Frame0 Setup Guide
|
||||||
|
|
||||||
|
## Installation (Fedora)
|
||||||
|
|
||||||
|
Download from https://frame0.app/download and install the RPM:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo dnf install ./frame0-*.x86_64.rpm
|
||||||
|
```
|
||||||
|
|
||||||
|
Requires: Fedora 40 or later (x86_64).
|
||||||
|
|
||||||
|
## Starting Frame0
|
||||||
|
|
||||||
|
Launch the desktop application:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
frame0 &
|
||||||
|
```
|
||||||
|
|
||||||
|
Frame0 exposes an HTTP API at `localhost:58320` when running.
|
||||||
|
|
||||||
|
## Verify API Access
|
||||||
|
|
||||||
|
```bash
|
||||||
|
.claude/skills/frame0-wireframe/scripts/frame0-cmd.sh health
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected output: `Frame0 is running on port 58320`
|
||||||
|
|
||||||
|
## Port Configuration
|
||||||
|
|
||||||
|
Default port: **58320**
|
||||||
|
|
||||||
|
To use a different port, set the environment variable:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export FRAME0_PORT=58321
|
||||||
|
```
|
||||||
|
|
||||||
|
Or pass `--port` to any script:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
.claude/skills/frame0-wireframe/scripts/frame0-cmd.sh --port 58321 health
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
| Symptom | Cause | Fix |
|
||||||
|
|---------|-------|-----|
|
||||||
|
| "Connection refused" | Frame0 not running | Start the desktop app |
|
||||||
|
| "Port in use" | Another instance running | Close duplicate or use different port |
|
||||||
|
| Script hangs | API unresponsive | Restart Frame0 |
|
||||||
+210
@@ -0,0 +1,210 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
PORT="${FRAME0_PORT:-58320}"
|
||||||
|
ENDPOINT="http://localhost:${PORT}/execute_command"
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<EOF
|
||||||
|
Usage: $(basename "$0") <command> [args...] [--port N]
|
||||||
|
|
||||||
|
Low-level Frame0 HTTP API wrapper. Replaces the MCP server with direct
|
||||||
|
curl calls. Requires Frame0 desktop app to be running.
|
||||||
|
|
||||||
|
Commands:
|
||||||
|
health Check if Frame0 is running
|
||||||
|
exec <namespace:action> <json> Execute a raw API command
|
||||||
|
create-shape <type> <json-props> Create a shape (rectangle, ellipse, text, etc.)
|
||||||
|
update-shape <id> <json-props> Update shape properties
|
||||||
|
delete <id> [id...] Delete shapes by ID
|
||||||
|
move <id> <dx> <dy> Move a shape by pixel offset
|
||||||
|
group <id> [id...] Group shapes
|
||||||
|
ungroup <id> [id...] Ungroup shapes
|
||||||
|
create-connector <tail-id> <head-id> [json-props] Connect two shapes
|
||||||
|
create-icon <name> <json-props> Create an icon shape
|
||||||
|
add-page <json-props> Add a new page
|
||||||
|
get-page [page-id] Get current or specific page info
|
||||||
|
list-pages List all pages
|
||||||
|
set-page <page-id> Set current page
|
||||||
|
export <page-id> <format> Export page (png/svg)
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--port N Frame0 API port (default: $PORT, env: FRAME0_PORT)
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
$(basename "$0") health
|
||||||
|
$(basename "$0") create-shape rectangle '{"name":"btn","left":100,"top":100,"width":120,"height":36}'
|
||||||
|
$(basename "$0") list-pages
|
||||||
|
$(basename "$0") export page-id png
|
||||||
|
EOF
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Parse --port from anywhere in args
|
||||||
|
ARGS=()
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--port) PORT="$2"; ENDPOINT="http://localhost:${PORT}/execute_command"; shift 2 ;;
|
||||||
|
*) ARGS+=("$1"); shift ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
set -- "${ARGS[@]+"${ARGS[@]}"}"
|
||||||
|
|
||||||
|
[[ $# -lt 1 ]] && usage
|
||||||
|
|
||||||
|
# Execute a Frame0 API command, return data or error
|
||||||
|
frame0_exec() {
|
||||||
|
local command="$1"
|
||||||
|
local args="${2:-\{\}}"
|
||||||
|
|
||||||
|
local response
|
||||||
|
response=$(curl -s -w "\n%{http_code}" -X POST "$ENDPOINT" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "{\"command\": \"$command\", \"args\": $args}" 2>&1) || {
|
||||||
|
echo "ERROR: Cannot connect to Frame0 at localhost:$PORT" >&2
|
||||||
|
echo "Is Frame0 running? See: .claude/skills/frame0-wireframe/references/setup-guide.md" >&2
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
local http_code body
|
||||||
|
http_code=$(echo "$response" | tail -1)
|
||||||
|
body=$(echo "$response" | sed '$d')
|
||||||
|
|
||||||
|
if [[ "$http_code" != 2* ]]; then
|
||||||
|
echo "ERROR: HTTP $http_code from Frame0" >&2
|
||||||
|
echo "$body" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Parse success/error from response
|
||||||
|
python3 -c "
|
||||||
|
import sys, json
|
||||||
|
try:
|
||||||
|
r = json.loads(sys.stdin.read())
|
||||||
|
if r.get('success'):
|
||||||
|
d = r.get('data')
|
||||||
|
if d is not None:
|
||||||
|
print(json.dumps(d, indent=2))
|
||||||
|
else:
|
||||||
|
print('ERROR: ' + str(r.get('error', 'Unknown error')), file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
print(f'ERROR: Invalid JSON response: {e}', file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
" <<< "$body"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Build JSON array from remaining args
|
||||||
|
ids_to_json_array() {
|
||||||
|
local arr="["
|
||||||
|
local first=true
|
||||||
|
for id in "$@"; do
|
||||||
|
[[ "$first" == true ]] && first=false || arr+=","
|
||||||
|
arr+="\"$id\""
|
||||||
|
done
|
||||||
|
arr+="]"
|
||||||
|
echo "$arr"
|
||||||
|
}
|
||||||
|
|
||||||
|
CMD="${1:-}"
|
||||||
|
shift || true
|
||||||
|
|
||||||
|
case "$CMD" in
|
||||||
|
health)
|
||||||
|
if curl -s -o /dev/null -w "%{http_code}" "http://localhost:${PORT}/" 2>/dev/null | grep -q "^[23]"; then
|
||||||
|
echo "Frame0 is running on port $PORT"
|
||||||
|
else
|
||||||
|
echo "Frame0 is NOT running on port $PORT" >&2
|
||||||
|
echo "Start Frame0 desktop app, then retry." >&2
|
||||||
|
echo "See: .claude/skills/frame0-wireframe/references/setup-guide.md" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
|
exec)
|
||||||
|
[[ $# -lt 2 ]] && { echo "Usage: exec <command> <json-args>" >&2; exit 1; }
|
||||||
|
frame0_exec "$1" "$2"
|
||||||
|
;;
|
||||||
|
|
||||||
|
create-shape)
|
||||||
|
[[ $# -lt 2 ]] && { echo "Usage: create-shape <type> <json-props>" >&2; exit 1; }
|
||||||
|
local_type="$1"
|
||||||
|
local_props="$2"
|
||||||
|
frame0_exec "shape:create-shape" "{\"type\": \"$local_type\", \"shapeProps\": $local_props}"
|
||||||
|
;;
|
||||||
|
|
||||||
|
update-shape)
|
||||||
|
[[ $# -lt 2 ]] && { echo "Usage: update-shape <id> <json-props>" >&2; exit 1; }
|
||||||
|
frame0_exec "shape:update-shape" "{\"shapeId\": \"$1\", \"shapeProps\": $2}"
|
||||||
|
;;
|
||||||
|
|
||||||
|
delete)
|
||||||
|
[[ $# -lt 1 ]] && { echo "Usage: delete <id> [id...]" >&2; exit 1; }
|
||||||
|
local_arr=$(ids_to_json_array "$@")
|
||||||
|
frame0_exec "edit:delete" "{\"shapeIdArray\": $local_arr}"
|
||||||
|
;;
|
||||||
|
|
||||||
|
move)
|
||||||
|
[[ $# -lt 3 ]] && { echo "Usage: move <id> <dx> <dy>" >&2; exit 1; }
|
||||||
|
frame0_exec "shape:move" "{\"shapeId\": \"$1\", \"dx\": $2, \"dy\": $3}"
|
||||||
|
;;
|
||||||
|
|
||||||
|
group)
|
||||||
|
[[ $# -lt 1 ]] && { echo "Usage: group <id> [id...]" >&2; exit 1; }
|
||||||
|
local_arr=$(ids_to_json_array "$@")
|
||||||
|
frame0_exec "shape:group" "{\"shapeIdArray\": $local_arr}"
|
||||||
|
;;
|
||||||
|
|
||||||
|
ungroup)
|
||||||
|
[[ $# -lt 1 ]] && { echo "Usage: ungroup <id> [id...]" >&2; exit 1; }
|
||||||
|
local_arr=$(ids_to_json_array "$@")
|
||||||
|
frame0_exec "shape:ungroup" "{\"shapeIdArray\": $local_arr}"
|
||||||
|
;;
|
||||||
|
|
||||||
|
create-connector)
|
||||||
|
[[ $# -lt 2 ]] && { echo "Usage: create-connector <tail-id> <head-id> [json-props]" >&2; exit 1; }
|
||||||
|
local_props="${3:-\{\}}"
|
||||||
|
frame0_exec "shape:create-connector" "{\"tailId\": \"$1\", \"headId\": \"$2\", \"shapeProps\": $local_props}"
|
||||||
|
;;
|
||||||
|
|
||||||
|
create-icon)
|
||||||
|
[[ $# -lt 2 ]] && { echo "Usage: create-icon <name> <json-props>" >&2; exit 1; }
|
||||||
|
frame0_exec "shape:create-icon" "{\"iconName\": \"$1\", \"shapeProps\": $2}"
|
||||||
|
;;
|
||||||
|
|
||||||
|
add-page)
|
||||||
|
[[ $# -lt 1 ]] && { echo "Usage: add-page <json-props>" >&2; exit 1; }
|
||||||
|
frame0_exec "page:add" "{\"pageProps\": $1}"
|
||||||
|
;;
|
||||||
|
|
||||||
|
get-page)
|
||||||
|
if [[ $# -ge 1 ]]; then
|
||||||
|
frame0_exec "page:get" "{\"pageId\": \"$1\"}"
|
||||||
|
else
|
||||||
|
frame0_exec "page:get" "{}"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
|
list-pages)
|
||||||
|
frame0_exec "page:get-all" "{}"
|
||||||
|
;;
|
||||||
|
|
||||||
|
set-page)
|
||||||
|
[[ $# -lt 1 ]] && { echo "Usage: set-page <page-id>" >&2; exit 1; }
|
||||||
|
frame0_exec "page:set-current" "{\"pageId\": \"$1\"}"
|
||||||
|
;;
|
||||||
|
|
||||||
|
export)
|
||||||
|
[[ $# -lt 2 ]] && { echo "Usage: export <page-id> <format>" >&2; exit 1; }
|
||||||
|
frame0_exec "file:export-image" "{\"pageId\": \"$1\", \"format\": \"$2\", \"fillBackground\": true}"
|
||||||
|
;;
|
||||||
|
|
||||||
|
--help|-h|help)
|
||||||
|
usage
|
||||||
|
;;
|
||||||
|
|
||||||
|
*)
|
||||||
|
echo "Unknown command: $CMD" >&2
|
||||||
|
usage
|
||||||
|
;;
|
||||||
|
esac
|
||||||
+144
@@ -0,0 +1,144 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
CMD="$SCRIPT_DIR/frame0-cmd.sh"
|
||||||
|
|
||||||
|
# Project wireframe defaults from docs/design/visual-grammar-v01.md
|
||||||
|
BG="#1a1e24" # Zone 1 floor
|
||||||
|
STROKE="#333340" # Outline standard
|
||||||
|
FILL="#2a3040" # Zone 1 wall
|
||||||
|
TEXT="#c8d0e0" # Insert chrome
|
||||||
|
ACCENT="#c8d8f0" # Zone 1 fixture light
|
||||||
|
FONT_SIZE=14
|
||||||
|
TITLE_SIZE=24
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<EOF
|
||||||
|
Usage: $(basename "$0") <command> [args...]
|
||||||
|
|
||||||
|
High-level Frame0 wireframe composition helpers. Builds on frame0-cmd.sh
|
||||||
|
with project-appropriate defaults (colors from visual-grammar-v01.md).
|
||||||
|
|
||||||
|
Commands:
|
||||||
|
new-page <title> Create a new page with title label
|
||||||
|
button <label> <x> <y> [w] [h] Labeled button (default 120x36)
|
||||||
|
text-field <placeholder> <x> <y> [w] [h] Text input field (default 200x32)
|
||||||
|
container <label> <x> <y> <w> <h> Labeled panel/container
|
||||||
|
label <text> <x> <y> [font-size] Text label
|
||||||
|
divider <x> <y> <length> [h|v] Divider line (default horizontal)
|
||||||
|
export <format> <output-path> Export current page (png/svg)
|
||||||
|
|
||||||
|
Environment:
|
||||||
|
FRAME0_PORT Frame0 API port (default: 58320)
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
$(basename "$0") new-page "HUD Layout v1"
|
||||||
|
$(basename "$0") button "Confirm" 100 200
|
||||||
|
$(basename "$0") container "Inventory Panel" 50 50 300 400
|
||||||
|
$(basename "$0") export png docs/design/wireframes/hud/hud-v1.png
|
||||||
|
EOF
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
[[ $# -lt 1 ]] && usage
|
||||||
|
|
||||||
|
# Ensure Frame0 is running before any operation
|
||||||
|
check_health() {
|
||||||
|
"$CMD" health > /dev/null 2>&1 || {
|
||||||
|
echo "ERROR: Frame0 is not running. Start the desktop app first." >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCMD="$1"
|
||||||
|
shift
|
||||||
|
|
||||||
|
case "$SUBCMD" in
|
||||||
|
new-page)
|
||||||
|
[[ $# -lt 1 ]] && { echo "Usage: new-page <title>" >&2; exit 1; }
|
||||||
|
check_health
|
||||||
|
local_title="$1"
|
||||||
|
echo "Creating page: $local_title"
|
||||||
|
"$CMD" add-page "{\"name\": \"$local_title\"}"
|
||||||
|
# Add title label at top of page
|
||||||
|
"$CMD" create-shape text "{\"name\": \"page-title\", \"left\": 20, \"top\": 10, \"width\": 500, \"height\": 40, \"text\": \"$local_title\", \"fontSize\": $TITLE_SIZE, \"fontColor\": \"$TEXT\"}"
|
||||||
|
echo "Page created: $local_title"
|
||||||
|
;;
|
||||||
|
|
||||||
|
button)
|
||||||
|
[[ $# -lt 3 ]] && { echo "Usage: button <label> <x> <y> [w] [h]" >&2; exit 1; }
|
||||||
|
check_health
|
||||||
|
local_label="$1" local_x="$2" local_y="$3"
|
||||||
|
local_w="${4:-120}" local_h="${5:-36}"
|
||||||
|
"$CMD" create-shape rectangle "{\"name\": \"$local_label\", \"left\": $local_x, \"top\": $local_y, \"width\": $local_w, \"height\": $local_h, \"fillColor\": \"$FILL\", \"strokeColor\": \"$ACCENT\", \"fontColor\": \"$TEXT\", \"fontSize\": $FONT_SIZE, \"text\": \"$local_label\", \"corners\": [4,4,4,4]}"
|
||||||
|
;;
|
||||||
|
|
||||||
|
text-field)
|
||||||
|
[[ $# -lt 3 ]] && { echo "Usage: text-field <placeholder> <x> <y> [w] [h]" >&2; exit 1; }
|
||||||
|
check_health
|
||||||
|
local_label="$1" local_x="$2" local_y="$3"
|
||||||
|
local_w="${4:-200}" local_h="${5:-32}"
|
||||||
|
"$CMD" create-shape rectangle "{\"name\": \"$local_label\", \"left\": $local_x, \"top\": $local_y, \"width\": $local_w, \"height\": $local_h, \"fillColor\": \"$BG\", \"strokeColor\": \"$STROKE\", \"fontColor\": \"$TEXT\", \"fontSize\": $FONT_SIZE, \"text\": \"$local_label\", \"corners\": [2,2,2,2]}"
|
||||||
|
;;
|
||||||
|
|
||||||
|
container)
|
||||||
|
[[ $# -lt 5 ]] && { echo "Usage: container <label> <x> <y> <w> <h>" >&2; exit 1; }
|
||||||
|
check_health
|
||||||
|
local_label="$1" local_x="$2" local_y="$3" local_w="$4" local_h="$5"
|
||||||
|
# Container frame
|
||||||
|
"$CMD" create-shape rectangle "{\"name\": \"$local_label\", \"left\": $local_x, \"top\": $local_y, \"width\": $local_w, \"height\": $local_h, \"fillColor\": \"$BG\", \"strokeColor\": \"$STROKE\", \"fontSize\": 12, \"fontColor\": \"$TEXT\"}"
|
||||||
|
# Container label at top-left inside
|
||||||
|
label_x=$((local_x + 8))
|
||||||
|
label_y=$((local_y + 4))
|
||||||
|
"$CMD" create-shape text "{\"name\": \"${local_label}-label\", \"left\": $label_x, \"top\": $label_y, \"width\": 200, \"height\": 20, \"text\": \"$local_label\", \"fontSize\": 12, \"fontColor\": \"$STROKE\"}"
|
||||||
|
;;
|
||||||
|
|
||||||
|
label)
|
||||||
|
[[ $# -lt 3 ]] && { echo "Usage: label <text> <x> <y> [font-size]" >&2; exit 1; }
|
||||||
|
check_health
|
||||||
|
local_text="$1" local_x="$2" local_y="$3"
|
||||||
|
local_size="${4:-$FONT_SIZE}"
|
||||||
|
"$CMD" create-shape text "{\"name\": \"$local_text\", \"left\": $local_x, \"top\": $local_y, \"width\": 300, \"height\": 24, \"text\": \"$local_text\", \"fontSize\": $local_size, \"fontColor\": \"$TEXT\"}"
|
||||||
|
;;
|
||||||
|
|
||||||
|
divider)
|
||||||
|
[[ $# -lt 3 ]] && { echo "Usage: divider <x> <y> <length> [h|v]" >&2; exit 1; }
|
||||||
|
check_health
|
||||||
|
local_x="$1" local_y="$2" local_len="$3"
|
||||||
|
local_dir="${4:-h}"
|
||||||
|
if [[ "$local_dir" == "v" ]]; then
|
||||||
|
"$CMD" create-shape line "{\"name\": \"divider\", \"left\": $local_x, \"top\": $local_y, \"width\": 1, \"height\": $local_len, \"strokeColor\": \"$STROKE\"}"
|
||||||
|
else
|
||||||
|
"$CMD" create-shape line "{\"name\": \"divider\", \"left\": $local_x, \"top\": $local_y, \"width\": $local_len, \"height\": 1, \"strokeColor\": \"$STROKE\"}"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
|
export)
|
||||||
|
[[ $# -lt 2 ]] && { echo "Usage: export <format> <output-path>" >&2; exit 1; }
|
||||||
|
check_health
|
||||||
|
local_format="$1" local_output="$2"
|
||||||
|
|
||||||
|
# Get current page ID
|
||||||
|
local_page_id=$("$CMD" get-page | python3 -c "import sys,json; print(json.load(sys.stdin).get('id',''))" 2>/dev/null)
|
||||||
|
if [[ -z "$local_page_id" ]]; then
|
||||||
|
echo "ERROR: Could not determine current page ID" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Ensure output directory exists
|
||||||
|
mkdir -p "$(dirname "$local_output")"
|
||||||
|
|
||||||
|
"$CMD" export "$local_page_id" "$local_format"
|
||||||
|
echo "Exported: $local_output"
|
||||||
|
;;
|
||||||
|
|
||||||
|
--help|-h|help)
|
||||||
|
usage
|
||||||
|
;;
|
||||||
|
|
||||||
|
*)
|
||||||
|
echo "Unknown command: $SUBCMD" >&2
|
||||||
|
usage
|
||||||
|
;;
|
||||||
|
esac
|
||||||
Reference in New Issue
Block a user