diff --git a/41e20f-wedding-table-seating-planner/CardListing/6241aa1f-0a8a-45f3-9b8b-57ac600070c4.json b/41e20f-wedding-table-seating-planner/CardListing/6241aa1f-0a8a-45f3-9b8b-57ac600070c4.json index a4372996..9d53afde 100644 --- a/41e20f-wedding-table-seating-planner/CardListing/6241aa1f-0a8a-45f3-9b8b-57ac600070c4.json +++ b/41e20f-wedding-table-seating-planner/CardListing/6241aa1f-0a8a-45f3-9b8b-57ac600070c4.json @@ -9,7 +9,7 @@ "type": "card", "attributes": { "name": "Table Seating Planner", - "summary": "The TableSeatingPlanner is a card definition designed to organize and manage seating arrangements for events. It captures key event details such as the event title, date, venue, and hosts. The planner links to guests, tables, and fixtures involved in the event setup. It includes a floor plan with configurable positioning, dimensions, opacity, and lock status to visualize seating arrangements. Additionally, it supports messaging features like invitation and seating messages, and facilitates poster creation with customizable poster images, prompts, and aspect ratios. The primary purpose is to assist event planners in designing, visualizing, and organizing seating plans for successful gatherings.", + "summary": "Seating charts are the most stressful part of planning an event — dozens of guests, family politics, and a floor plan that keeps changing right up to the big day. Table Seating Planner takes the pain out of it: lay out your venue with drag-and-drop tables and fixtures (or import your own floor plan), then seat everyone with a simple drag.\n\nOpen the demo to see it shine: ask the AI assistant to arrange the seats and watch every guest find a place, step through the guided setup wizard to start an event of your own, then carry your theme through to matching invitations and posters for the big day.", "cardInfo": { "name": "Table Seating Planner", "notes": null, @@ -170,17 +170,27 @@ }, "images.0": { "links": { - "self": "../Screenshots/85c8a56e-d0ee-414d-8d7f-ec94c7aff8c8-b6ba6f10.png" + "self": "../Screenshots/85c8a56e-d0ee-414d-8d7f-ec94c7aff8c8-81ceb0df.png" } }, "images.1": { "links": { - "self": "../Screenshots/8b596412-e2ed-4d38-877b-40d12c1876dc-940f80d5.png" + "self": "../Screenshots/setup-wizard-c0a3fa49.png" } }, "images.2": { "links": { - "self": "../Screenshots/36305cb1-39a5-4a95-b594-ab4af32fa883-e6851a31.png" + "self": "../Screenshots/add-element-d340bad2.png" + } + }, + "images.3": { + "links": { + "self": "../Screenshots/seating-print-05985ab6.png" + } + }, + "images.4": { + "links": { + "self": "../Screenshots/ai-poster-9d32df2e.png" } }, "skills.0": { @@ -193,6 +203,26 @@ "self": null } }, + "categories.0": { + "links": { + "self": "../../Category/creative-projects" + } + }, + "categories.1": { + "links": { + "self": "../../Category/ai-automation" + } + }, + "cardInfo.theme": { + "links": { + "self": null + } + }, + "cardInfo.cardThumbnail": { + "links": { + "self": "../ListingThumbnails/table-seating-b62d23ba.png" + } + }, "examples.0": { "links": { "self": "../TableSeatingPlanner/85c8a56e-d0ee-414d-8d7f-ec94c7aff8c8" @@ -208,101 +238,81 @@ "self": "../TableSeatingPlanner/36305cb1-39a5-4a95-b594-ab4af32fa883" } }, - "examples.3": { + "supportingCards.0": { "links": { "self": "../Guest/andy" } }, - "examples.4": { + "supportingCards.1": { "links": { "self": "../Guest/cheche" } }, - "examples.5": { + "supportingCards.2": { "links": { "self": "../Guest/chuan" } }, - "examples.6": { + "supportingCards.3": { "links": { "self": "../Guest/justin" } }, - "examples.7": { + "supportingCards.4": { "links": { "self": "../Guest/nana" } }, - "examples.8": { + "supportingCards.5": { "links": { "self": "../Guest/richy" } }, - "examples.9": { + "supportingCards.6": { "links": { "self": "../Guest/robin" } }, - "examples.10": { + "supportingCards.7": { "links": { "self": "../Guest/yvonne" } }, - "examples.11": { + "supportingCards.8": { "links": { "self": "../Host/amy" } }, - "examples.12": { + "supportingCards.9": { "links": { "self": "../Host/lucas" } }, - "examples.13": { + "supportingCards.10": { "links": { "self": "../LayoutTemplate/ceremony" } }, - "examples.14": { + "supportingCards.11": { "links": { "self": "../LayoutTemplate/reception" } }, - "examples.15": { + "supportingCards.12": { "links": { "self": "../Theme/double-happiness-banquet" } }, - "examples.16": { + "supportingCards.13": { "links": { "self": "../Theme/italian-cathedral" } }, - "examples.17": { - "links": { - "self": "../../index" - } - }, - "categories.0": { - "links": { - "self": "../../Category/creative-projects" - } - }, - "categories.1": { + "supportingCards.14": { "links": { - "self": "../../Category/ai-automation" - } - }, - "cardInfo.theme": { - "links": { - "self": null - } - }, - "cardInfo.cardThumbnail": { - "links": { - "self": "../ListingThumbnails/table-seating-b62d23ba.png" + "self": "../Theme/parisian" } } } } -} \ No newline at end of file +} diff --git a/41e20f-wedding-table-seating-planner/Screenshots/36305cb1-39a5-4a95-b594-ab4af32fa883-e6851a31.png b/41e20f-wedding-table-seating-planner/Screenshots/36305cb1-39a5-4a95-b594-ab4af32fa883-e6851a31.png deleted file mode 100644 index 714b75f0..00000000 Binary files a/41e20f-wedding-table-seating-planner/Screenshots/36305cb1-39a5-4a95-b594-ab4af32fa883-e6851a31.png and /dev/null differ diff --git a/41e20f-wedding-table-seating-planner/Screenshots/85c8a56e-d0ee-414d-8d7f-ec94c7aff8c8-81ceb0df.png b/41e20f-wedding-table-seating-planner/Screenshots/85c8a56e-d0ee-414d-8d7f-ec94c7aff8c8-81ceb0df.png new file mode 100644 index 00000000..39349f8c Binary files /dev/null and b/41e20f-wedding-table-seating-planner/Screenshots/85c8a56e-d0ee-414d-8d7f-ec94c7aff8c8-81ceb0df.png differ diff --git a/41e20f-wedding-table-seating-planner/Screenshots/85c8a56e-d0ee-414d-8d7f-ec94c7aff8c8-b6ba6f10.png b/41e20f-wedding-table-seating-planner/Screenshots/85c8a56e-d0ee-414d-8d7f-ec94c7aff8c8-b6ba6f10.png deleted file mode 100644 index f39a7174..00000000 Binary files a/41e20f-wedding-table-seating-planner/Screenshots/85c8a56e-d0ee-414d-8d7f-ec94c7aff8c8-b6ba6f10.png and /dev/null differ diff --git a/41e20f-wedding-table-seating-planner/Screenshots/8b596412-e2ed-4d38-877b-40d12c1876dc-940f80d5.png b/41e20f-wedding-table-seating-planner/Screenshots/8b596412-e2ed-4d38-877b-40d12c1876dc-940f80d5.png deleted file mode 100644 index 11705e35..00000000 Binary files a/41e20f-wedding-table-seating-planner/Screenshots/8b596412-e2ed-4d38-877b-40d12c1876dc-940f80d5.png and /dev/null differ diff --git a/41e20f-wedding-table-seating-planner/Screenshots/add-element-d340bad2.png b/41e20f-wedding-table-seating-planner/Screenshots/add-element-d340bad2.png new file mode 100644 index 00000000..5579d12c Binary files /dev/null and b/41e20f-wedding-table-seating-planner/Screenshots/add-element-d340bad2.png differ diff --git a/41e20f-wedding-table-seating-planner/Screenshots/ai-poster-9d32df2e.png b/41e20f-wedding-table-seating-planner/Screenshots/ai-poster-9d32df2e.png new file mode 100644 index 00000000..fa9bf203 Binary files /dev/null and b/41e20f-wedding-table-seating-planner/Screenshots/ai-poster-9d32df2e.png differ diff --git a/41e20f-wedding-table-seating-planner/Screenshots/seating-print-05985ab6.png b/41e20f-wedding-table-seating-planner/Screenshots/seating-print-05985ab6.png new file mode 100644 index 00000000..677cbd5c Binary files /dev/null and b/41e20f-wedding-table-seating-planner/Screenshots/seating-print-05985ab6.png differ diff --git a/41e20f-wedding-table-seating-planner/Screenshots/setup-wizard-c0a3fa49.png b/41e20f-wedding-table-seating-planner/Screenshots/setup-wizard-c0a3fa49.png new file mode 100644 index 00000000..40cd1183 Binary files /dev/null and b/41e20f-wedding-table-seating-planner/Screenshots/setup-wizard-c0a3fa49.png differ diff --git a/41e20f-wedding-table-seating-planner/Spec/039f23ea-f61a-4a68-a322-6ff5acb17bf7.json b/41e20f-wedding-table-seating-planner/Spec/039f23ea-f61a-4a68-a322-6ff5acb17bf7.json index 0ddd75b4..c289d085 100644 --- a/41e20f-wedding-table-seating-planner/Spec/039f23ea-f61a-4a68-a322-6ff5acb17bf7.json +++ b/41e20f-wedding-table-seating-planner/Spec/039f23ea-f61a-4a68-a322-6ff5acb17bf7.json @@ -2,7 +2,7 @@ "data": { "type": "card", "attributes": { - "readMe": null, + "readMe": "# SetupWizard\n\n## Summary\n\nFirst-run setup wizard that the isolated planner shows over the canvas until the plan has content. It is a thin wrapper composing the shared catalog `` component (`@cardstack/catalog/aef6db-stepper/stepper`), which supplies the modal scrim, vertical step rail, heading, and Back/Next navigation; this file owns only the four domain steps — event details (name, date, venue), hosts (the couple), guests, and finally starting from a layout template (previewed with LayoutPreview) or a blank canvas — plus the gold corner ornament, expressed as `StepperStep` descriptors with per-step `isComplete` checks. It talks to the host planner only through args: every mutation happens via the callbacks you pass in, and the planner's `hosts` / `guests` linksToMany field components are passed as `@hostsField` / `@guestsField` so newly added people render as fitted cards inside the wizard. Every step is mandatory — Next stays disabled until the step is complete (all three event fields, at least one host, at least one guest) and there is no Skip; the ✕ close (and the final \"Start blank\" finish action, both wired to `@onSkip`) is the one escape hatch, and dismissal isn't persisted, so the wizard reopens the next time an incomplete planner is opened.\n\n## Import\n\n```js\nimport SetupWizard from '../components/setup-wizard';\n```\n\n## Template Usage\n\nAs invoked from the isolated planner view:\n\n```gjs\n\n```", "ref": { "module": "../components/setup-wizard", "name": "default" diff --git a/41e20f-wedding-table-seating-planner/Spec/0ec5868a-22d5-46ff-9ff0-4018ebbd3d57.json b/41e20f-wedding-table-seating-planner/Spec/0ec5868a-22d5-46ff-9ff0-4018ebbd3d57.json index b0442dad..bbd57a11 100644 --- a/41e20f-wedding-table-seating-planner/Spec/0ec5868a-22d5-46ff-9ff0-4018ebbd3d57.json +++ b/41e20f-wedding-table-seating-planner/Spec/0ec5868a-22d5-46ff-9ff0-4018ebbd3d57.json @@ -2,7 +2,7 @@ "data": { "type": "card", "attributes": { - "readMe": null, + "readMe": "# ApplySeatingPlanCommand\n\n## Summary\n\nApplies a complete seating arrangement to a Table Seating Planner card: it loads the planner with `GetCardCommand`, expands each party-head guest id into the full party (companions linked via `parentGuest`), and writes `seatedGuests` on every table — tables omitted from the plan are emptied. The plan is validated deterministically: out-of-range table indexes and unknown party ids are skipped and reported, per-table capacity is enforced (overflow is trimmed, and `section` grids are regrown to fit), and leftover parties are auto-seated by VIP status and category priority while honouring `reservedCategories`. Input is an `ApplySeatingPlanInput` card (`plannerCardId`, `assignments` of `TableAssignmentField`, optional `summary`); output is an `ApplySeatingPlanResult` (`seatedCount`, `message`). It makes no AI call itself — it is the deterministic half of the AI seating flow.\n\n## Import\n\n```js\nimport {\n ApplySeatingPlanCommand,\n TableAssignmentField,\n} from '../commands/apply-seating-plan-command';\n```\n\n## Usage\n\nIn this package the command is not called from a component. It is registered on the Seat Arranger skill card (`Skill/seat-arranger.json`), and the AI assistant executes it after reasoning out the arrangement — the planner opens that room with `UseAiAssistantCommand` (see `components/tsp.gts`):\n\n```json\n{\n \"codeRef\": {\n \"name\": \"ApplySeatingPlanCommand\",\n \"module\": \"../commands/apply-seating-plan-command\"\n },\n \"requiresApproval\": false\n}\n```\n\nInvoked directly, the input shape is:\n\n```js\nlet ctx = this.args.context?.commandContext;\nlet result = await new ApplySeatingPlanCommand(ctx).execute({\n plannerCardId: planner.id,\n assignments: [\n new TableAssignmentField({ tableIndex: 0, partyIds: [avaId, noahId] }),\n new TableAssignmentField({ tableIndex: 1, partyIds: [miaId] }),\n ],\n summary: 'Family up front, friends by the dance floor.',\n});\n```\n", "ref": { "name": "ApplySeatingPlanCommand", "module": "../commands/apply-seating-plan-command" @@ -42,4 +42,4 @@ } } } -} \ No newline at end of file +} diff --git a/41e20f-wedding-table-seating-planner/Spec/10c3938c-b579-45cc-a849-4811b0fbc16b.json b/41e20f-wedding-table-seating-planner/Spec/10c3938c-b579-45cc-a849-4811b0fbc16b.json index edd0a911..d4555b91 100644 --- a/41e20f-wedding-table-seating-planner/Spec/10c3938c-b579-45cc-a849-4811b0fbc16b.json +++ b/41e20f-wedding-table-seating-planner/Spec/10c3938c-b579-45cc-a849-4811b0fbc16b.json @@ -2,7 +2,7 @@ "data": { "type": "card", "attributes": { - "readMe": null, + "readMe": "# PlaceCardView\n\n## Summary\n\nRenders a wedding place card: an optional uppercase event-title kicker, the guest name in large serif type, an accent rule, then the table name and an optional italic message, with an optional `@logoUrl` event logo as a faint centered watermark behind the content. It is the planner's stationery visual — the print sheet renders one per seated guest, so what prints matches this component exactly. Colors and fonts flow from the theme's semantic tokens with Parisian fallbacks. Sizing is container-query driven, and the guest name auto-fits its width by character count (the print sheet is hidden on screen, so real measurement isn't possible), so the same component reads well as a large isolated card, a small fitted tile, or a print-sheet cell.\n\n## Import\n\n```js\nimport { PlaceCardView } from '../components/card-views';\n```\n\n## Template Usage\n\nAs rendered on the planner's print sheet (all args optional; the name falls back to \"Guest Name\", and `@message` adds an italic closing line):\n\n```gjs\n\n```", "ref": { "module": "../components/card-views", "name": "PlaceCardView" diff --git a/41e20f-wedding-table-seating-planner/Spec/164db241-2fe0-45f9-a69f-9525c0475d1c.json b/41e20f-wedding-table-seating-planner/Spec/164db241-2fe0-45f9-a69f-9525c0475d1c.json index 405e590b..0c5132dc 100644 --- a/41e20f-wedding-table-seating-planner/Spec/164db241-2fe0-45f9-a69f-9525c0475d1c.json +++ b/41e20f-wedding-table-seating-planner/Spec/164db241-2fe0-45f9-a69f-9525c0475d1c.json @@ -2,7 +2,7 @@ "data": { "type": "card", "attributes": { - "readMe": null, + "readMe": "# Guest\n\n## Summary\nGuest models one invitee on the wedding guest list. It extends Person, so it inherits `fullName`, `photo`, and the computed `photoURL`, and adds a `category` enum (Bride's Family, Groom's Family, Close Friend, Friends, College, Colleagues, Others), a `vip` boolean, and a `parentGuest` link that is set on a +1 to point back at the guest who brings them. In the Table Seating Planner package, Guest instances are the people you drag onto tables: the planner links to them via `guests = linksToMany(Guest)`, and each Table field tracks its own `seatedGuests`.\n\n## Import\n```js\nimport { Guest } from '../guest';\n```\n\n## Usage\nGuest is a card, so consuming cards link to it rather than containing it — the same wiring the planner uses:\n\n```js\nimport { CardDef, field, linksToMany } from 'https://cardstack.com/base/card-api';\nimport { Guest } from '../guest';\n\nexport class SeatingChart extends CardDef {\n @field guests = linksToMany(() => Guest);\n}\n```\n\n## Template Usage\n```handlebars\n<@fields.guests @format='embedded' />\n```\nRenders each linked guest as an avatar row with name, category dot, VIP badge, and +1 attribution.", "ref": { "module": "../guest", "name": "Guest" @@ -19,9 +19,44 @@ } }, "relationships": { - "linkedExamples": { + "linkedExamples.0": { "links": { - "self": null + "self": "../Guest/andy" + } + }, + "linkedExamples.1": { + "links": { + "self": "../Guest/cheche" + } + }, + "linkedExamples.2": { + "links": { + "self": "../Guest/chuan" + } + }, + "linkedExamples.3": { + "links": { + "self": "../Guest/justin" + } + }, + "linkedExamples.4": { + "links": { + "self": "../Guest/nana" + } + }, + "linkedExamples.5": { + "links": { + "self": "../Guest/richy" + } + }, + "linkedExamples.6": { + "links": { + "self": "../Guest/robin" + } + }, + "linkedExamples.7": { + "links": { + "self": "../Guest/yvonne" } }, "cardInfo.cardThumbnail": { @@ -42,4 +77,4 @@ } } } -} \ No newline at end of file +} diff --git a/41e20f-wedding-table-seating-planner/Spec/1866ca76-7df0-4cd9-a966-b075b1164db2.json b/41e20f-wedding-table-seating-planner/Spec/1866ca76-7df0-4cd9-a966-b075b1164db2.json index 2173d56b..8be6c6c5 100644 --- a/41e20f-wedding-table-seating-planner/Spec/1866ca76-7df0-4cd9-a966-b075b1164db2.json +++ b/41e20f-wedding-table-seating-planner/Spec/1866ca76-7df0-4cd9-a966-b075b1164db2.json @@ -2,7 +2,7 @@ "data": { "type": "card", "attributes": { - "readMe": null, + "readMe": "# categoryLabel\n\n## Summary\n\nReturns the display label for a guest-category value (e.g. `brides-family` → \"Bride's Family\") from the `GUEST_CATEGORIES` list, or an empty string when the value is missing or unknown. The planner uses it wherever a guest's category is shown as text — the category chips in the Guest card views and the guest roster, legend, and hover popover in the main planner component (`components/tsp.gts`).\n\n## Import\n\n```js\nimport { categoryLabel } from '../utils/index';\n```\n\n## Usage\n\n```js\ncategoryLabel(value: string | null | undefined): string\n\ncategoryLabel('brides-family'); // \"Bride's Family\"\n```", "ref": { "module": "../utils/index", "name": "categoryLabel" @@ -42,4 +42,4 @@ } } } -} \ No newline at end of file +} diff --git a/41e20f-wedding-table-seating-planner/Spec/2fe0c5f9-669f-4525-8047-5d1cec55c21d.json b/41e20f-wedding-table-seating-planner/Spec/2fe0c5f9-669f-4525-8047-5d1cec55c21d.json index 0435537c..c84ce584 100644 --- a/41e20f-wedding-table-seating-planner/Spec/2fe0c5f9-669f-4525-8047-5d1cec55c21d.json +++ b/41e20f-wedding-table-seating-planner/Spec/2fe0c5f9-669f-4525-8047-5d1cec55c21d.json @@ -2,7 +2,7 @@ "data": { "type": "card", "attributes": { - "readMe": null, + "readMe": "# FixtureGlyph\n\n## Summary\n\nAn SVG glyph for a floor-plan fixture, chosen by `@kind`: plant, tree, balloon, stage, red-carpet, dance-floor, arch, projector, cake, bar, curved-wall, rect-decor, round-decor, or circle-cluster, with a dashed placeholder box for unknown kinds. `@color` tints the drawing and `@pattern` ('solid' | 'outline' | 'soft') controls the fill — outline removes it, soft renders it at 45% opacity. Strokes use `vector-effect: non-scaling-stroke`, so line weight stays crisp whether it is stretched across the canvas, shrunk into LayoutPreview, or shown as a picker swatch.\n\n## Import\n\n```js\nimport FixtureGlyph from '../components/fixture-glyph';\n```\n\n## Template Usage\n\n```gjs\n\n```", "ref": { "module": "../components/fixture-glyph", "name": "default" @@ -42,4 +42,4 @@ } } } -} \ No newline at end of file +} diff --git a/41e20f-wedding-table-seating-planner/Spec/39355975-9e51-49d1-9b77-88bcdb7c5feb.json b/41e20f-wedding-table-seating-planner/Spec/39355975-9e51-49d1-9b77-88bcdb7c5feb.json index 397a1737..d90f9254 100644 --- a/41e20f-wedding-table-seating-planner/Spec/39355975-9e51-49d1-9b77-88bcdb7c5feb.json +++ b/41e20f-wedding-table-seating-planner/Spec/39355975-9e51-49d1-9b77-88bcdb7c5feb.json @@ -2,7 +2,7 @@ "data": { "type": "card", "attributes": { - "readMe": null, + "readMe": "# TableCardView\n\n## Summary\n\nRenders a table-number card: an optional uppercase event-title kicker, the table name in large serif type flanked by stationery rules, and an optional italic `@accent` line, with an optional `@logoUrl` event logo as a faint centered watermark. Like PlaceCardView it is a planner stationery visual — the print sheet renders one as the header cell for each table, so print output matches this component exactly, and its colors and fonts flow from the theme's semantic tokens with Parisian fallbacks. Container-query sizing plus character-count auto-fitting of the name let it scale from an isolated card down to a print-sheet cell.\n\n## Import\n\n```js\nimport { TableCardView } from '../components/card-views';\n```\n\n## Template Usage\n\nAs rendered on the planner's print sheet (all args optional; the name falls back to \"Table\"):\n\n```gjs\n\n```", "ref": { "module": "../components/card-views", "name": "TableCardView" diff --git a/41e20f-wedding-table-seating-planner/Spec/412fe0c5-f966-4f95-a5c0-475d1cec55c2.json b/41e20f-wedding-table-seating-planner/Spec/412fe0c5-f966-4f95-a5c0-475d1cec55c2.json index ffe5d14b..05c55fca 100644 --- a/41e20f-wedding-table-seating-planner/Spec/412fe0c5-f966-4f95-a5c0-475d1cec55c2.json +++ b/41e20f-wedding-table-seating-planner/Spec/412fe0c5-f966-4f95-a5c0-475d1cec55c2.json @@ -2,7 +2,7 @@ "data": { "type": "card", "attributes": { - "readMe": null, + "readMe": "# TableSeatingPlannerFitted\n\n## Summary\n\nThe compact card formats for the Table Seating Planner. One component renders all the fitted breakpoints, switched by container queries: a badge (✦ mark, event title, guest count), a strip (title, venue or table count, and a seated tag), a tile (a LayoutPreview floor-plan hero above the title and stats), and a wide card (title, date, venue, and stat pills beside a floor-plan panel). The title falls back to \"Seating Plan\", and the seated count is de-duplicated across tables. When the planner has no linked theme (`cardInfo.theme`), a `.tsp-default-theme` class pins the semantic tokens to the package's Parisian palette so app-level defaults can't restyle the card; with a linked theme the class is omitted and the theme's tokens flow through.\n\n## Import\n\n```js\nimport { TableSeatingPlannerFitted } from '../components/tsp';\n```\n\n## Template Usage\n\nNot typically invoked directly — the card definition assigns it as both the fitted and embedded formats in `table-seating-planner.gts`:\n\n```js\nTableSeatingPlanner.embedded = TableSeatingPlannerFitted;\nTableSeatingPlanner.fitted = TableSeatingPlannerFitted;\n```", "ref": { "module": "../components/tsp", "name": "TableSeatingPlannerFitted" @@ -42,4 +42,4 @@ } } } -} \ No newline at end of file +} diff --git a/41e20f-wedding-table-seating-planner/Spec/4e5135f4-4fd2-4d7d-9d2b-415b227ae77f.json b/41e20f-wedding-table-seating-planner/Spec/4e5135f4-4fd2-4d7d-9d2b-415b227ae77f.json index cfeeede2..a85fb02f 100644 --- a/41e20f-wedding-table-seating-planner/Spec/4e5135f4-4fd2-4d7d-9d2b-415b227ae77f.json +++ b/41e20f-wedding-table-seating-planner/Spec/4e5135f4-4fd2-4d7d-9d2b-415b227ae77f.json @@ -2,7 +2,7 @@ "data": { "type": "card", "attributes": { - "readMe": null, + "readMe": "# ArrangeSeatsCommand\n\n## Summary\n\nAsks an LLM to work out a full seating arrangement from a JSON payload of parties, tables, and a free-text instruction, using a system prompt that encodes the seating rules: parties are never split, capacity and `reservedCategories` are respected, VIP and category closeness drive table prominence, and the planner's instruction outranks every default. Input is an `ArrangeInput` card (`userPrompt` — the JSON payload as a string — and optional `llmModel`, default `anthropic/claude-sonnet-4.6`); output is an `ArrangeResult` whose `output` field carries the model's raw JSON (`assignments`, `seatZones`, `resizes`, `rationale`, `summary`). The AI call goes through `OneShotLlmRequestCommand` rather than the OpenRouter proxy directly, and the command does not validate the model's answer — the caller checks capacities before applying it. Note that the shipped planner UI does not invoke this command: it launches the AI assistant with the Seat Arranger skill (which applies the plan via Apply Seating Plan), so this command is the one-shot alternative to that flow.\n\n## Import\n\n```js\nimport { ArrangeSeatsCommand } from '../commands/arrange-seats-command.gts';\n```\n\n## Usage\n\n```js\nlet ctx = this.args.context?.commandContext;\nlet result = await new ArrangeSeatsCommand(ctx).execute({\n userPrompt: JSON.stringify({ parties, tables, instruction }),\n});\nlet plan = JSON.parse(result.output);\n```\n\nThe payload shapes match the system prompt: each party is `{ id, name, size, members, category, vip }` (id is the party head's guest card id) and each table is `{ id, name, capacity, reservedCategories, vip, prominence, shape }` plus `rows` and `cols` for `section` grids.\n", "ref": { "name": "ArrangeSeatsCommand", "module": "../commands/arrange-seats-command.gts" @@ -42,4 +42,4 @@ } } } -} \ No newline at end of file +} diff --git a/41e20f-wedding-table-seating-planner/Spec/5cd9e966-b075-4116-8db2-412fe0c5f966.json b/41e20f-wedding-table-seating-planner/Spec/5cd9e966-b075-4116-8db2-412fe0c5f966.json index 3d0edc79..7fa1515c 100644 --- a/41e20f-wedding-table-seating-planner/Spec/5cd9e966-b075-4116-8db2-412fe0c5f966.json +++ b/41e20f-wedding-table-seating-planner/Spec/5cd9e966-b075-4116-8db2-412fe0c5f966.json @@ -2,7 +2,7 @@ "data": { "type": "card", "attributes": { - "readMe": null, + "readMe": "# Person\n\n## Summary\nPerson is the shared base card for people in the Table Seating Planner package. It holds a `fullName` string, a `photo` image source, and a computed `photoURL` derived from that photo; its `title` falls back to \"Unnamed Person\" when the name is blank. Guest and Host both extend Person, inheriting these identity fields plus the avatar-row embedded view. The package ships no standalone Person instances — it exists to be subclassed.\n\n## Import\n```js\nimport { Person } from '../person';\n```\n\n## Usage\nPerson is a card, so link to it (or, more commonly, extend it the way Guest and Host do):\n\n```js\nimport { CardDef, field, linksTo } from 'https://cardstack.com/base/card-api';\nimport { Person } from '../person';\n\nexport class ContactCard extends CardDef {\n @field contact = linksTo(Person);\n}\n```\n\n## Template Usage\n```handlebars\n<@fields.contact @format='embedded' />\n```\nRenders the person as a compact row with a photo (or initials) avatar and their name.", "ref": { "module": "../person", "name": "Person" @@ -42,4 +42,4 @@ } } } -} \ No newline at end of file +} diff --git a/41e20f-wedding-table-seating-planner/Spec/669f9525-c047-4d1c-ac55-c21d4fa59e6b.json b/41e20f-wedding-table-seating-planner/Spec/669f9525-c047-4d1c-ac55-c21d4fa59e6b.json index 8c2c6a21..bba2664c 100644 --- a/41e20f-wedding-table-seating-planner/Spec/669f9525-c047-4d1c-ac55-c21d4fa59e6b.json +++ b/41e20f-wedding-table-seating-planner/Spec/669f9525-c047-4d1c-ac55-c21d4fa59e6b.json @@ -2,7 +2,7 @@ "data": { "type": "card", "attributes": { - "readMe": null, + "readMe": "# AnalyzeFloorPlanCommand\n\n## Summary\n\nReads a venue floor-plan image and reproduces it as structured layout JSON: tables (shape, seat count, seating style, position, rotation), seating sections, standalone seats, and fixtures such as stages, dance floors, and arches. Input is an `AnalyzeInput` card (`imageUrl` as a data URL, `planRect` JSON bounds, optional `instruction` and `llmModel`); output is an `AnalyzeResult` card whose `output` field holds the model's raw JSON text. The command sends the image and a detailed tracing prompt to a vision model (default `anthropic/claude-opus-4.8`) through the OpenRouter proxy via `SendRequestViaProxyCommand`. The prompt assumes a red 20x20 reference grid is drawn over the image — the planner component overlays one before invoking.\n\n## Import\n\n```js\nimport { AnalyzeFloorPlanCommand } from '../commands/analyze-floor-plan-command';\n```\n\n## Usage\n\nAs invoked from the planner's floor-plan import flow (`components/tsp.gts`):\n\n```js\nlet ctx = this.args.context?.commandContext;\nlet out = await new AnalyzeFloorPlanCommand(ctx).execute({\n imageUrl: griddedDataUrl,\n planRect: JSON.stringify({ x: 0, y: 0, w: 800, h: 600 }),\n});\nlet layout = JSON.parse(out.output);\n```\n\n`layout.tables` and `layout.fixtures` are then clamped to the plan rectangle and mapped onto the planner's `Table` and `Fixture` cards by the caller.\n", "ref": { "module": "../commands/analyze-floor-plan-command", "name": "AnalyzeFloorPlanCommand" @@ -42,4 +42,4 @@ } } } -} \ No newline at end of file +} diff --git a/41e20f-wedding-table-seating-planner/Spec/66b075b1-164d-4241-afe0-c5f9669f9525.json b/41e20f-wedding-table-seating-planner/Spec/66b075b1-164d-4241-afe0-c5f9669f9525.json index 7f1b3f0a..9d1b7df1 100644 --- a/41e20f-wedding-table-seating-planner/Spec/66b075b1-164d-4241-afe0-c5f9669f9525.json +++ b/41e20f-wedding-table-seating-planner/Spec/66b075b1-164d-4241-afe0-c5f9669f9525.json @@ -2,7 +2,7 @@ "data": { "type": "card", "attributes": { - "readMe": null, + "readMe": "# shortTableLabel\n\n## Summary\n\nDerives a compact badge label for a table from its name: the trailing number if the name ends in digits, otherwise the first two characters, falling back to `T` when the name is empty. Used to label table shapes on the floor-plan canvas in `components/tsp.gts` and in the Table card's fitted and embedded views.\n\n## Import\n\n```js\nimport { shortTableLabel } from '../utils/index';\n```\n\n## Usage\n\n```js\nshortTableLabel(name: string | null | undefined): string\n\nshortTableLabel('Table 12'); // '12'\n```", "ref": { "module": "../utils/index", "name": "shortTableLabel" @@ -42,4 +42,4 @@ } } } -} \ No newline at end of file +} diff --git a/41e20f-wedding-table-seating-planner/Spec/66ca767d-f05c-49e9-a6b0-75b1164db241.json b/41e20f-wedding-table-seating-planner/Spec/66ca767d-f05c-49e9-a6b0-75b1164db241.json index 28522cfb..659bc259 100644 --- a/41e20f-wedding-table-seating-planner/Spec/66ca767d-f05c-49e9-a6b0-75b1164db241.json +++ b/41e20f-wedding-table-seating-planner/Spec/66ca767d-f05c-49e9-a6b0-75b1164db241.json @@ -2,7 +2,7 @@ "data": { "type": "card", "attributes": { - "readMe": null, + "readMe": "# initialsOf\n\n## Summary\n\nComputes avatar initials from a full name: first and last word initials for multi-word names, the first two letters for single-word names, and `·` when the name is empty. Used for guest avatars throughout the planner — seat markers, drag chips, and the guest roster in `components/tsp.gts`, plus the Guest, Host, and Person card views.\n\n## Import\n\n```js\nimport { initialsOf } from '../utils/index';\n```\n\n## Usage\n\n```js\ninitialsOf(name: string | null | undefined): string\n\ninitialsOf('Ada Lovelace'); // 'AL'\n```", "ref": { "module": "../utils/index", "name": "initialsOf" @@ -42,4 +42,4 @@ } } } -} \ No newline at end of file +} diff --git a/41e20f-wedding-table-seating-planner/Spec/75b1164d-b241-4fe0-85f9-669f9525c047.json b/41e20f-wedding-table-seating-planner/Spec/75b1164d-b241-4fe0-85f9-669f9525c047.json index 4f35ea3b..0a24525e 100644 --- a/41e20f-wedding-table-seating-planner/Spec/75b1164d-b241-4fe0-85f9-669f9525c047.json +++ b/41e20f-wedding-table-seating-planner/Spec/75b1164d-b241-4fe0-85f9-669f9525c047.json @@ -2,7 +2,7 @@ "data": { "type": "card", "attributes": { - "readMe": null, + "readMe": "# InvitationPosterCommand\n\n## Summary\n\nGenerates a wedding-invitation poster image from a text prompt and saves it into a realm. It calls the image model `google/gemini-3.1-flash-image-preview` through the OpenRouter proxy via `SendRequestViaProxyCommand`, appending a full-bleed, exact-lettering directive and the chosen aspect ratio to the prompt, then extracts the returned `data:image/...` URL, recompresses it to WebP under about 500 KB when possible, and writes the bytes with `WriteBinaryFileCommand`. Input is a `PosterInput` card (`prompt`, `aspect` from `POSTER_ASPECTS`, `targetRealmIdentifier`, `targetPath`); output is a `PosterOutput` whose `imageUrl` is the saved file's identifier. The module also exports `POSTER_ASPECTS` and `PosterAspectField` for building an aspect picker.\n\n## Import\n\n```js\nimport {\n InvitationPosterCommand,\n POSTER_ASPECTS,\n} from '../commands/invitation-poster-command';\n```\n\n## Usage\n\nAs invoked from the planner's poster panel (`components/tsp.gts`):\n\n```js\nlet ctx = this.args.context?.commandContext;\nlet res = await new InvitationPosterCommand(ctx).execute({\n prompt: posterPrompt,\n aspect: '4:5',\n targetRealmIdentifier: this.realmUrl,\n targetPath: 'InvitationPoster',\n});\nlet savedFileUrl = res.imageUrl;\n```\n", "ref": { "module": "../commands/invitation-poster-command", "name": "InvitationPosterCommand" @@ -42,4 +42,4 @@ } } } -} \ No newline at end of file +} diff --git a/41e20f-wedding-table-seating-planner/Spec/767df05c-d9e9-46b0-b5b1-164db2412fe0.json b/41e20f-wedding-table-seating-planner/Spec/767df05c-d9e9-46b0-b5b1-164db2412fe0.json index 3a6b6900..20d40864 100644 --- a/41e20f-wedding-table-seating-planner/Spec/767df05c-d9e9-46b0-b5b1-164db2412fe0.json +++ b/41e20f-wedding-table-seating-planner/Spec/767df05c-d9e9-46b0-b5b1-164db2412fe0.json @@ -2,7 +2,7 @@ "data": { "type": "card", "attributes": { - "readMe": null, + "readMe": "# seatPoints\n\n## Summary\n\nComputes normalized seat positions (each coordinate a 0–1 fraction of the table's bounding box) for a table of the given shape (`round`, `oval`, `rect`, `square`, `curved`, `seat`) and seating style (`around`, `opposite`, `top`, `bottom`, `left`, `right`). The planner calls it in `components/tsp.gts` to place seat markers around each table on the floor-plan canvas, and in `components/layout-preview.gts` to render layout-template thumbnails.\n\n## Import\n\n```js\nimport { seatPoints } from '../utils/index';\n```\n\n## Usage\n\n```js\nseatPoints(shape: string, style: string, count: number): SeatPoint[]\n\nseatPoints('round', 'around', 8);\n```", "ref": { "module": "../utils/index", "name": "seatPoints" @@ -42,4 +42,4 @@ } } } -} \ No newline at end of file +} diff --git a/41e20f-wedding-table-seating-planner/Spec/7df05cd9-e966-4075-b116-4db2412fe0c5.json b/41e20f-wedding-table-seating-planner/Spec/7df05cd9-e966-4075-b116-4db2412fe0c5.json index cebab09f..f9134a39 100644 --- a/41e20f-wedding-table-seating-planner/Spec/7df05cd9-e966-4075-b116-4db2412fe0c5.json +++ b/41e20f-wedding-table-seating-planner/Spec/7df05cd9-e966-4075-b116-4db2412fe0c5.json @@ -2,13 +2,60 @@ "data": { "type": "card", "attributes": { - "readMe": null, + "readMe": "# Table\n\n## Summary\n\nTable models one seatable unit on the seating-plan canvas — a single banquet table or a whole `section` grid of individual seats. Its schema covers identity and seating configuration (`name`, a `shape` enum of round/oval/rect/square/curved/section/seat, `seatCount`, a `seatingStyle` enum of around/opposite/top/bottom/left/right, plus `rows`, `cols`, and a `seatOrder` enum used by section grids), canvas geometry (`x`, `y`, `width`, `height`, `rotation`, `z` — all numbers), and assignment state (`themeColor`, `reservedCategories` as a containsMany of guest-category values, `seatedGuests` as a linksToMany of Guest cards packed alongside `seatSlots`, plus `vip`, `rank`, `locked`, and `note`). Computed fields derive `seatedCount` from the seated guests and `title` from the name. The planner holds tables in a containsMany field, so each table carries its own seat assignments inside the plan.\n\n## Import\n\n```js\nimport { Table } from '../table';\n```\n\n## Usage as a Field\n\n```ts\n@field tables = containsMany(Table);\n```\n\n## Template Usage\n\n```handlebars\n{{#each @model.tables as |table|}}\n {{table.name}} — {{table.seatedCount}}/{{table.seatCount}} seated\n{{/each}}\n```\n\nEach entry also renders with its own embedded and fitted views via `<@fields.tables />`.\n", "ref": { "module": "../table", "name": "Table" }, "specType": "field", - "containedExamples": [], + "containedExamples": [ + { + "name": "Bride's Parents & Family", + "shape": "round", + "seatCount": 10, + "seatingStyle": "around", + "rows": null, + "cols": null, + "seatOrder": null, + "x": -180, + "y": -60, + "width": 130, + "height": 130, + "rotation": 0, + "z": 4, + "themeColor": "#c5a35c", + "reservedCategories": [ + "brides-family" + ], + "seatSlots": [], + "vip": true, + "rank": 1, + "locked": false, + "note": "Front of the ballroom, closest to the stage." + }, + { + "name": "Groom's Friends & Guest", + "shape": "section", + "seatCount": 18, + "seatingStyle": "around", + "rows": 3, + "cols": 6, + "seatOrder": "lr-tb", + "x": 88, + "y": 407, + "width": 330, + "height": 220, + "rotation": 0, + "z": 8, + "themeColor": "#c5a35c", + "reservedCategories": [], + "seatSlots": [], + "vip": false, + "rank": null, + "locked": false, + "note": null + } + ], "cardTitle": "Table", "cardDescription": null, "cardInfo": { @@ -39,7 +86,23 @@ "adoptsFrom": { "module": "@cardstack/base/spec", "name": "Spec" + }, + "fields": { + "containedExamples": [ + { + "adoptsFrom": { + "module": "../table", + "name": "Table" + } + }, + { + "adoptsFrom": { + "module": "../table", + "name": "Table" + } + } + ] } } } -} \ No newline at end of file +} diff --git a/41e20f-wedding-table-seating-planner/Spec/9f9525c0-475d-4cec-95c2-1d4fa59e6bda.json b/41e20f-wedding-table-seating-planner/Spec/9f9525c0-475d-4cec-95c2-1d4fa59e6bda.json index 1d78bddb..87309a6d 100644 --- a/41e20f-wedding-table-seating-planner/Spec/9f9525c0-475d-4cec-95c2-1d4fa59e6bda.json +++ b/41e20f-wedding-table-seating-planner/Spec/9f9525c0-475d-4cec-95c2-1d4fa59e6bda.json @@ -2,7 +2,7 @@ "data": { "type": "card", "attributes": { - "readMe": null, + "readMe": "# Layout Template\n\n## Summary\nLayoutTemplate is a reusable floor arrangement: a `name` plus `tables = containsMany(Table)` and `fixtures = containsMany(Fixture)` capturing the positioned tables and fixtures (dance floor, stage, bar, and so on) of a room setup, with computed `tableCount` and `seatCount` totals. In the Table Seating Planner, the template browser finds LayoutTemplate instances by card type and applies one to stamp its tables and fixtures onto the current plan; you can also save the plan's current arrangement back as a new template. The shipped examples cover a ceremony and a reception layout.\n\n## Import\n```js\nimport { LayoutTemplate } from '../layout-template';\n```\n\n## Usage\nLayoutTemplate is a card, so consuming cards link to it:\n\n```js\nimport { CardDef, field, linksTo } from 'https://cardstack.com/base/card-api';\nimport { LayoutTemplate } from '../layout-template';\n\nexport class VenueSetup extends CardDef {\n @field layout = linksTo(LayoutTemplate);\n}\n```\n\n## Template Usage\n```handlebars\n<@fields.layout @format='embedded' />\n```\nRenders the template as a thumbnail preview of its floor plan with table and seat counts.", "ref": { "module": "../layout-template", "name": "LayoutTemplate" @@ -19,9 +19,14 @@ } }, "relationships": { - "linkedExamples": { + "linkedExamples.0": { "links": { - "self": null + "self": "../LayoutTemplate/ceremony" + } + }, + "linkedExamples.1": { + "links": { + "self": "../LayoutTemplate/reception" } }, "cardInfo.cardThumbnail": { @@ -42,4 +47,4 @@ } } } -} \ No newline at end of file +} diff --git a/41e20f-wedding-table-seating-planner/Spec/b075b116-4db2-412f-a0c5-f9669f9525c0.json b/41e20f-wedding-table-seating-planner/Spec/b075b116-4db2-412f-a0c5-f9669f9525c0.json index c814f2f0..42c125c2 100644 --- a/41e20f-wedding-table-seating-planner/Spec/b075b116-4db2-412f-a0c5-f9669f9525c0.json +++ b/41e20f-wedding-table-seating-planner/Spec/b075b116-4db2-412f-a0c5-f9669f9525c0.json @@ -2,7 +2,7 @@ "data": { "type": "card", "attributes": { - "readMe": null, + "readMe": "# Host\n\n## Summary\nHost models someone hosting the event — a bride, groom, or one of their parents. It extends Person, inheriting `fullName`, `photo`, and the computed `photoURL`, and adds a `role` enum restricted to the six `HOST_ROLES` (Bride, Groom, and each of their mothers and fathers) shown beneath the name in its gold-badged embedded view; the module also exports the `HOST_ROLES` list and the `RoleField` enum field. The Table Seating Planner links hosts via `hosts = linksToMany(Host)` and joins their names into a computed `hostNames` string for the event header.\n\n## Import\n```js\nimport { Host } from '../host';\n```\n\n## Usage\nHost is a card, so consuming cards link to it — the same wiring the planner uses:\n\n```js\nimport { CardDef, field, linksToMany } from 'https://cardstack.com/base/card-api';\nimport { Host } from '../host';\n\nexport class EventHeader extends CardDef {\n @field hosts = linksToMany(() => Host);\n}\n```\n\n## Template Usage\n```handlebars\n<@fields.hosts @format='embedded' />\n```\nRenders each host as an avatar row with their name, role, and a \"Host\" badge.", "ref": { "module": "../host", "name": "Host" @@ -19,9 +19,14 @@ } }, "relationships": { - "linkedExamples": { + "linkedExamples.0": { "links": { - "self": null + "self": "../Host/amy" + } + }, + "linkedExamples.1": { + "links": { + "self": "../Host/lucas" } }, "cardInfo.cardThumbnail": { @@ -42,4 +47,4 @@ } } } -} \ No newline at end of file +} diff --git a/41e20f-wedding-table-seating-planner/Spec/b1164db2-412f-40c5-b966-9f9525c0475d.json b/41e20f-wedding-table-seating-planner/Spec/b1164db2-412f-40c5-b966-9f9525c0475d.json index 2b92fc4a..ced15113 100644 --- a/41e20f-wedding-table-seating-planner/Spec/b1164db2-412f-40c5-b966-9f9525c0475d.json +++ b/41e20f-wedding-table-seating-planner/Spec/b1164db2-412f-40c5-b966-9f9525c0475d.json @@ -2,7 +2,7 @@ "data": { "type": "card", "attributes": { - "readMe": null, + "readMe": "# LayoutPreview\n\n## Summary\n\nA read-only SVG miniature of a seating layout: table bodies drawn per shape (ellipses for round/oval, rounded rects, a swept path for curved tables) ringed by seat dots, with fixtures rendered through FixtureGlyph underneath. The viewBox is computed from the bounding box of everything passed in, plus padding, so any layout auto-fits its container. The planner uses it for template previews in the SetupWizard and snapshot popover, and as the figure on the printed seating chart.\n\n## Import\n\n```js\nimport LayoutPreview from '../components/layout-preview';\n```\n\n## Template Usage\n\n```gjs\n\n```", "ref": { "module": "../components/layout-preview", "name": "default" @@ -42,4 +42,4 @@ } } } -} \ No newline at end of file +} diff --git a/41e20f-wedding-table-seating-planner/Spec/c5f9669f-9525-4047-9d1c-ec55c21d4fa5.json b/41e20f-wedding-table-seating-planner/Spec/c5f9669f-9525-4047-9d1c-ec55c21d4fa5.json index 770cfb01..291f6c02 100644 --- a/41e20f-wedding-table-seating-planner/Spec/c5f9669f-9525-4047-9d1c-ec55c21d4fa5.json +++ b/41e20f-wedding-table-seating-planner/Spec/c5f9669f-9525-4047-9d1c-ec55c21d4fa5.json @@ -2,7 +2,7 @@ "data": { "type": "card", "attributes": { - "readMe": null, + "readMe": "# SeatingPlanPopover\n\n## Summary\n\nThe one popover shell every planner popover shares: a themed surface with a sticky header (kicker/title or a custom `:header` block, always capped with a ✕ close button), a scrollable `:body` that limits its height so it never runs off-screen, and an optional pinned `:foot` for actions. It wraps the catalog `` and owns sensible positioning defaults — kind 'edit', anchoring 'beside', placement 'right-start', elevation 'floating' — so most call sites only pass `@anchor` and `@onClose`. The planner uses it for table and fixture editors, tool panels, and centered, backdropped delete confirmations.\n\n## Import\n\n```js\nimport SeatingPlanPopover from '../components/seating-plan-popover';\n```\n\n## Template Usage\n\nA centered confirmation, as used by the planner's delete flow:\n\n```gjs\n\n <:body>\n

{{this.pendingDelete.detail}}

\n \n <:foot>\n \n \n\n```", "ref": { "module": "../components/seating-plan-popover", "name": "default" @@ -42,4 +42,4 @@ } } } -} \ No newline at end of file +} diff --git a/41e20f-wedding-table-seating-planner/Spec/ca767df0-5cd9-4966-b075-b1164db2412f.json b/41e20f-wedding-table-seating-planner/Spec/ca767df0-5cd9-4966-b075-b1164db2412f.json index ee6f0661..7c9788c3 100644 --- a/41e20f-wedding-table-seating-planner/Spec/ca767df0-5cd9-4966-b075-b1164db2412f.json +++ b/41e20f-wedding-table-seating-planner/Spec/ca767df0-5cd9-4966-b075-b1164db2412f.json @@ -2,7 +2,7 @@ "data": { "type": "card", "attributes": { - "readMe": null, + "readMe": "# sectionSeatPoints\n\n## Summary\n\nComputes an evenly spaced rows × cols grid of normalized seat positions for a section-type table, with seat numbering following the given `SeatOrder` (reading order, right-to-left, bottom-up, snake, or column-wise). Used in `components/tsp.gts` to lay out seats inside section blocks on the floor-plan canvas, and in `components/layout-preview.gts` for layout-template previews.\n\n## Import\n\n```js\nimport { sectionSeatPoints } from '../utils/index';\n```\n\n## Usage\n\n```js\nsectionSeatPoints(rows: number, cols: number, order: SeatOrder = 'lr-tb'): SeatPoint[]\n\nsectionSeatPoints(3, 4, 'snake');\n```", "ref": { "module": "../utils/index", "name": "sectionSeatPoints" @@ -42,4 +42,4 @@ } } } -} \ No newline at end of file +} diff --git a/41e20f-wedding-table-seating-planner/Spec/d9e966b0-75b1-464d-b241-2fe0c5f9669f.json b/41e20f-wedding-table-seating-planner/Spec/d9e966b0-75b1-464d-b241-2fe0c5f9669f.json index 716221ea..a229ae37 100644 --- a/41e20f-wedding-table-seating-planner/Spec/d9e966b0-75b1-464d-b241-2fe0c5f9669f.json +++ b/41e20f-wedding-table-seating-planner/Spec/d9e966b0-75b1-464d-b241-2fe0c5f9669f.json @@ -2,7 +2,7 @@ "data": { "type": "card", "attributes": { - "readMe": null, + "readMe": "# categoryColor\n\n## Summary\n\nReturns the hex color assigned to a guest-category value in `GUEST_CATEGORIES`, defaulting to `#c5a35c` when the value is missing or unknown. The planner uses it to color-code guests by category — category swatches in the Guest card views, seat markers and the category legend in `components/tsp.gts`, and per-category dots in the Table card.\n\n## Import\n\n```js\nimport { categoryColor } from '../utils/index';\n```\n\n## Usage\n\n```js\ncategoryColor(value: string | null | undefined): string\n\ncategoryColor('close-friend'); // '#74b18d'\n```", "ref": { "module": "../utils/index", "name": "categoryColor" @@ -42,4 +42,4 @@ } } } -} \ No newline at end of file +} diff --git a/41e20f-wedding-table-seating-planner/Spec/e0c5f966-9f95-45c0-875d-1cec55c21d4f.json b/41e20f-wedding-table-seating-planner/Spec/e0c5f966-9f95-45c0-875d-1cec55c21d4f.json index c4dbe581..63833c36 100644 --- a/41e20f-wedding-table-seating-planner/Spec/e0c5f966-9f95-45c0-875d-1cec55c21d4f.json +++ b/41e20f-wedding-table-seating-planner/Spec/e0c5f966-9f95-45c0-875d-1cec55c21d4f.json @@ -2,7 +2,7 @@ "data": { "type": "card", "attributes": { - "readMe": null, + "readMe": "# TableSeatingPlannerIsolated\n\n## Summary\n\nThe full planner application, assigned as the card's isolated format. It renders a zoomable, pannable canvas of tables and fixtures with drag-and-drop guest seating, marquee multi-select, copy/paste, resize/rotate, and undo/redo with keyboard shortcuts. Beyond the canvas it offers a floor-plan image underlay (upload plus AI analysis), an \"arrange with AI\" action that opens an AI Assistant room armed with the seat-arranger skill, print output for place cards or the seating chart, an invites view for invitation and poster content, and saving the current layout as a template snapshot. On first run it shows the SetupWizard. A linked theme (`cardInfo.theme`) is applied by the standard card theming pipeline, not by this component — every rule here reads the theme's semantic tokens (plus the planner's `--tsp-*` extras) through `var(--token, fallback)` chains, so an unthemed planner falls back to the package's Parisian look.\n\n## Import\n\n```js\nimport { TableSeatingPlannerIsolated } from '../components/tsp';\n```\n\n## Template Usage\n\nNot typically invoked directly — the card definition assigns it as the isolated format in `table-seating-planner.gts`:\n\n```js\nTableSeatingPlanner.isolated = TableSeatingPlannerIsolated;\n```", "ref": { "module": "../components/tsp", "name": "TableSeatingPlannerIsolated" @@ -42,4 +42,4 @@ } } } -} \ No newline at end of file +} diff --git a/41e20f-wedding-table-seating-planner/Spec/e966b075-b116-4db2-812f-e0c5f9669f95.json b/41e20f-wedding-table-seating-planner/Spec/e966b075-b116-4db2-812f-e0c5f9669f95.json index 43ed8e1e..465fcfc8 100644 --- a/41e20f-wedding-table-seating-planner/Spec/e966b075-b116-4db2-812f-e0c5f9669f95.json +++ b/41e20f-wedding-table-seating-planner/Spec/e966b075-b116-4db2-812f-e0c5f9669f95.json @@ -2,7 +2,7 @@ "data": { "type": "card", "attributes": { - "readMe": null, + "readMe": "# sectionSize\n\n## Summary\n\nComputes the pixel footprint of a section-type table from its rows × cols grid at 34px per seat, clamped to a minimum of 80×60. Used in `components/tsp.gts` to size section blocks on the floor-plan canvas and to enforce the minimum size when resizing them.\n\n## Import\n\n```js\nimport { sectionSize } from '../utils/index';\n```\n\n## Usage\n\n```js\nsectionSize(rows: number, cols: number): { w: number; h: number }\n\nsectionSize(3, 4); // { w: 136, h: 102 }\n```", "ref": { "module": "../utils/index", "name": "sectionSize" @@ -42,4 +42,4 @@ } } } -} \ No newline at end of file +} diff --git a/41e20f-wedding-table-seating-planner/Spec/f05cd9e9-66b0-45b1-964d-b2412fe0c5f9.json b/41e20f-wedding-table-seating-planner/Spec/f05cd9e9-66b0-45b1-964d-b2412fe0c5f9.json index 6cd32917..23e17a6c 100644 --- a/41e20f-wedding-table-seating-planner/Spec/f05cd9e9-66b0-45b1-964d-b2412fe0c5f9.json +++ b/41e20f-wedding-table-seating-planner/Spec/f05cd9e9-66b0-45b1-964d-b2412fe0c5f9.json @@ -2,13 +2,40 @@ "data": { "type": "card", "attributes": { - "readMe": null, + "readMe": "# Fixture\n\n## Summary\n\nFixture models a non-seating floor element placed on the same canvas as the tables — the `kind` enum covers plants, trees, a balloon arch, stage, projector, red carpet, dance floor, ceremony arch, cake table, bar, and abstract curved-wall/rectangle/circle decor shapes. Its schema is a `label` string, the `kind` enum, a `pattern` enum (solid/outline/soft) that controls how the glyph fills, canvas geometry (`x`, `y`, `width`, `height`, `rotation`, `z` — all numbers), a `locked` boolean that pins it against moving or resizing on the canvas, and a `color`. A computed `title` falls back from the label to the kind's display label. The planner holds fixtures in a containsMany field so decor travels with the seating plan.\n\n## Import\n\n```js\nimport { Fixture } from '../fixture';\n```\n\n## Usage as a Field\n\n```ts\n@field fixtures = containsMany(Fixture);\n```\n\n## Template Usage\n\n```handlebars\n{{#each @model.fixtures as |fixture|}}\n {{fixture.title}}\n{{/each}}\n```\n\nEach entry also renders with its own embedded and fitted views via `<@fields.fixtures />`.\n", "ref": { "module": "../fixture", "name": "Fixture" }, "specType": "field", - "containedExamples": [], + "containedExamples": [ + { + "label": "Dance floor", + "kind": "dance-floor", + "pattern": "solid", + "x": -120, + "y": 60, + "width": 240, + "height": 240, + "rotation": 0, + "z": 1, + "locked": false, + "color": "#c5a35c" + }, + { + "label": "Stage", + "kind": "stage", + "pattern": "outline", + "x": -250, + "y": 138, + "width": 260, + "height": 140, + "rotation": 0, + "z": 2, + "locked": true, + "color": "#c5a35c" + } + ], "cardTitle": "Fixture", "cardDescription": null, "cardInfo": { @@ -39,7 +66,23 @@ "adoptsFrom": { "module": "@cardstack/base/spec", "name": "Spec" + }, + "fields": { + "containedExamples": [ + { + "adoptsFrom": { + "module": "../fixture", + "name": "Fixture" + } + }, + { + "adoptsFrom": { + "module": "../fixture", + "name": "Fixture" + } + } + ] } } } -} \ No newline at end of file +} diff --git a/41e20f-wedding-table-seating-planner/Spec/f9669f95-25c0-475d-9cec-55c21d4fa59e.json b/41e20f-wedding-table-seating-planner/Spec/f9669f95-25c0-475d-9cec-55c21d4fa59e.json index 6dac28b9..05c99616 100644 --- a/41e20f-wedding-table-seating-planner/Spec/f9669f95-25c0-475d-9cec-55c21d4fa59e.json +++ b/41e20f-wedding-table-seating-planner/Spec/f9669f95-25c0-475d-9cec-55c21d4fa59e.json @@ -12,7 +12,7 @@ "name": "TableSeatingPlanner", "module": "../table-seating-planner" }, - "readMe": "# Table Seating Planner\n\n## Summary\nThe `TableSeatingPlanner` spec is a card definition that provides a comprehensive solution for managing table seating arrangements for events. It includes various fields to capture event details, host information, guest lists, table layouts, and related media like floor plans and invitation posters.\n\n## Import\n```javascript\nimport { TableSeatingPlanner } from '../table-seating-planner';\n```\n\n## Usage as a Field\nYou can use the `TableSeatingPlanner` card as a field within a consuming card or field. For example:\n\n```javascript\nimport { CardDef, field, contains } from 'https://cardstack.com/base/card-api';\nimport { TableSeatingPlanner } from '../table-seating-planner';\n\nexport class EventCard extends CardDef {\n @field seatingPlanner = contains(TableSeatingPlanner);\n}\n```\n\nIn this example, the `EventCard` contains a `seatingPlanner` field of type `TableSeatingPlanner`, allowing the user to manage the table seating arrangement as part of the event.\n\n## Template Usage\nTo use the `TableSeatingPlanner` card in a template, you can simply include the field in your template:\n\n```handlebars\n\n <@fields.seatingPlanner />\n\n```\n\nThis will render the `TableSeatingPlanner` card within the consuming card's template.", + "readMe": "# Table Seating Planner\n\n## Summary\nTableSeatingPlanner is the main card of the package: a drag-and-drop seating chart for a wedding or event. It carries the event identity (`eventTitle`, `eventDate`, `venue`, `eventLogo`, plus `hosts = linksToMany(Host)` with a computed `hostNames` header string), the guest list (`guests = linksToMany(Guest)`), and the room itself (`tables = containsMany(Table)` and `fixtures = containsMany(Fixture)`), with each Table tracking its own seated guests. Supporting fields handle a floor-plan underlay image (`floorPlanURL` with position, size, opacity, and lock fields), invitation and seating messages, and a generated event poster (`poster`, `posterPrompt`, `posterAspect`).\n\n## Import\n```js\nimport { TableSeatingPlanner } from '../table-seating-planner';\n```\n\n## Usage\nTableSeatingPlanner is a card, so consuming cards link to it rather than containing it:\n\n```js\nimport { CardDef, field, linksTo } from 'https://cardstack.com/base/card-api';\nimport { TableSeatingPlanner } from '../table-seating-planner';\n\nexport class WeddingBinder extends CardDef {\n @field seatingPlan = linksTo(TableSeatingPlanner);\n}\n```\n\n## Template Usage\n```handlebars\n<@fields.seatingPlan />\n```\nRenders the linked plan; open it isolated to work on the full seating canvas.", "cardInfo": { "name": null, "notes": null, @@ -30,9 +30,19 @@ "self": null } }, - "linkedExamples": { + "linkedExamples.0": { "links": { - "self": null + "self": "../TableSeatingPlanner/85c8a56e-d0ee-414d-8d7f-ec94c7aff8c8" + } + }, + "linkedExamples.1": { + "links": { + "self": "../TableSeatingPlanner/8b596412-e2ed-4d38-877b-40d12c1876dc" + } + }, + "linkedExamples.2": { + "links": { + "self": "../TableSeatingPlanner/36305cb1-39a5-4a95-b594-ab4af32fa883" } }, "cardInfo.cardThumbnail": { @@ -42,4 +52,4 @@ } } } -} \ No newline at end of file +} diff --git a/41e20f-wedding-table-seating-planner/TableSeatingPlanner/36305cb1-39a5-4a95-b594-ab4af32fa883.json b/41e20f-wedding-table-seating-planner/TableSeatingPlanner/36305cb1-39a5-4a95-b594-ab4af32fa883.json index 45f5e3e7..456604d3 100644 --- a/41e20f-wedding-table-seating-planner/TableSeatingPlanner/36305cb1-39a5-4a95-b594-ab4af32fa883.json +++ b/41e20f-wedding-table-seating-planner/TableSeatingPlanner/36305cb1-39a5-4a95-b594-ab4af32fa883.json @@ -181,7 +181,7 @@ "cardThumbnailURL": null }, "fixtures": [], - "eventDate": "2026-07-16T16:00:00.000Z", + "eventDate": "2026-08-01T10:00:00.000Z", "eventLogo": { "url": null, "sourceMode": "url" @@ -282,4 +282,4 @@ } } } -} \ No newline at end of file +} diff --git a/41e20f-wedding-table-seating-planner/TableSeatingPlanner/85c8a56e-d0ee-414d-8d7f-ec94c7aff8c8.json b/41e20f-wedding-table-seating-planner/TableSeatingPlanner/85c8a56e-d0ee-414d-8d7f-ec94c7aff8c8.json index 8bdd167e..1e2897e6 100644 --- a/41e20f-wedding-table-seating-planner/TableSeatingPlanner/85c8a56e-d0ee-414d-8d7f-ec94c7aff8c8.json +++ b/41e20f-wedding-table-seating-planner/TableSeatingPlanner/85c8a56e-d0ee-414d-8d7f-ec94c7aff8c8.json @@ -417,7 +417,7 @@ "color": "#c5a35c" } ], - "eventDate": "2026-07-16T16:00:00.000Z", + "eventDate": "2026-08-01T10:00:00.000Z", "eventLogo": { "url": null, "sourceMode": "url" @@ -568,4 +568,4 @@ } } } -} \ No newline at end of file +} diff --git a/41e20f-wedding-table-seating-planner/TableSeatingPlanner/8b596412-e2ed-4d38-877b-40d12c1876dc.json b/41e20f-wedding-table-seating-planner/TableSeatingPlanner/8b596412-e2ed-4d38-877b-40d12c1876dc.json index 79f518d2..cd258923 100644 --- a/41e20f-wedding-table-seating-planner/TableSeatingPlanner/8b596412-e2ed-4d38-877b-40d12c1876dc.json +++ b/41e20f-wedding-table-seating-planner/TableSeatingPlanner/8b596412-e2ed-4d38-877b-40d12c1876dc.json @@ -136,7 +136,7 @@ "color": "#c5a35c" } ], - "eventDate": "2026-07-16T16:00:00.000Z", + "eventDate": "2026-12-07T03:00:00.000Z", "eventLogo": { "url": null, "sourceMode": null @@ -242,4 +242,4 @@ } } } -} \ No newline at end of file +}