create-coline-app 2.5.0 → 2.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-coline-app",
3
- "version": "2.5.0",
3
+ "version": "2.5.1",
4
4
  "description": "Scaffold, push, and iterate on apps for the Coline App Store.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/cli.test.ts CHANGED
@@ -53,10 +53,10 @@ describe("runCreate", () => {
53
53
  };
54
54
  expect(packageJson.name).toBe("team-crm");
55
55
  expect(packageJson.dependencies["@colineapp/sdk"]).toBe("^0.4.0");
56
- expect(packageJson.dependencies["@colineapp/ui"]).toBe("^0.4.0");
56
+ expect(packageJson.dependencies["@colineapp/ui"]).toBe("^0.4.1");
57
57
  expect(packageJson.dependencies.react).toBe("^19.0.0");
58
58
  expect(packageJson.dependencies["react-dom"]).toBe("^19.0.0");
59
- expect(packageJson.devDependencies["create-coline-app"]).toBe("^2.5.0");
59
+ expect(packageJson.devDependencies["create-coline-app"]).toBe("^2.5.1");
60
60
  const main = await readFile(join(dir, "main.tsx"), "utf8");
61
61
  expect(main).toContain("ColineAppProvider");
62
62
  expect(main).toContain("AppPage");
@@ -65,3 +65,25 @@ states. No pretend search, dead menus, invented metrics, or static success toast
65
65
 
66
66
  Run the adjacent `coline-ui-review` skill before finishing. If your model cannot
67
67
  inspect images, say so; mechanical checks alone do not establish visual quality.
68
+
69
+ ## Use the shared controls
70
+
71
+ - Views: `Tabs`, `TabsList`, `TabsTrigger`, `TabsContent`. The raised selection
72
+ and content motion are built in. Do not handwrite tab buttons or underline tabs.
73
+ - Filters and value pickers: `FilterMenu` with `value`, `onValueChange`,
74
+ `aria-label`, and `options: [{ value, label }]`. This uses Coline's dropdown,
75
+ including its checked items, surface and animated highlight. Avoid native
76
+ selects for these product controls. Use `DropdownMenu` directly for actions.
77
+ - Dialogs: `Dialog` / `DialogContent` include motion and a dismiss button. Use
78
+ `DialogTitle` and `DialogDescription`; keep content inside the viewport.
79
+ - Buttons: use variants without custom background overrides. Set `type="submit"`
80
+ explicitly inside forms. Other buttons do not submit forms by default.
81
+
82
+ The default `PageToolbar` has no top/bottom border. Keep the header simple and
83
+ use whitespace to separate controls from content. Don't draw another border
84
+ around the table or add a background strip to every row.
85
+
86
+ App-local CSS variables scoped to a page are not available in portalled dialogs
87
+ and menus. Use host tokens for shared controls; put decorative app variables at
88
+ `:root` if both the page and portals need them. Upgrading the UI dependency and
89
+ rebuilding updates shared defaults; copied app CSS continues to override them.
@@ -37,3 +37,11 @@ Keep preview cleanup scoped. The screenshot command closes its own server. For
37
37
  interactive QA, retain the terminal session or exact child-process PID you started
38
38
  and stop only that process. Never use broad `pkill -f` or `killall` commands: other
39
39
  apps may be running previews at the same time.
40
+
41
+ Check dialog buttons in both themes: page-scoped CSS variables do not inherit
42
+ into portals. Exercise form submission, Escape/close, tab keyboard navigation,
43
+ and filter menu selection. Capture every actual app view, not only the default
44
+ preview tab. Inspect motion and reduced-motion behavior as well as still images.
45
+ Keep the number of attached images within the model/provider's request limit;
46
+ review a small representative set, compact context before accumulating more,
47
+ and record provider failures separately from app failures.
@@ -1,5 +1,5 @@
1
1
  import * as React from "react";
2
- import {
2
+ import { FilterMenu,
3
3
  AppPage,
4
4
  PageToolbar,
5
5
  Input,
@@ -65,10 +65,10 @@ export function Example({ state = "ready" }: { state?: string }) {
65
65
  const [items, setItems] = React.useState(state === "empty" ? [] : initial);
66
66
  const [search, setSearch] = React.useState("");
67
67
  const [failure, setFailure] = React.useState(state === "error" || state === "denied");
68
- const moveControls = React.useRef(new Map<string, HTMLSelectElement>());
68
+ const moveControls = React.useRef(new Map<string, HTMLButtonElement>());
69
69
  function move(id: string, stage: string) {
70
70
  setItems((current) => current.map((item) => (item.id === id ? { ...item, stage } : item)));
71
- // Moving between columns remounts the select. Keep keyboard users on the story.
71
+ // Moving between columns remounts the trigger. Keep keyboard users on the story.
72
72
  requestAnimationFrame(() => moveControls.current.get(id)?.focus());
73
73
  }
74
74
  function add() {
@@ -147,20 +147,15 @@ export function Example({ state = "ready" }: { state?: string }) {
147
147
  <p className="example-description">{item.description}</p>
148
148
  <label className="example-field">
149
149
  Move to
150
- <select
151
- className="example-select"
150
+ <FilterMenu
151
+ className="example-filter"
152
152
  aria-label={`Move ${item.title} to`}
153
153
  value={item.stage}
154
154
  ref={(element) => {
155
155
  if (element) moveControls.current.set(item.id, element);
156
156
  else moveControls.current.delete(item.id);
157
157
  }}
158
- onChange={(event) => move(item.id, event.currentTarget.value)}
159
- >
160
- {stages.map((value) => (
161
- <option key={value}>{value}</option>
162
- ))}
163
- </select>
158
+ onValueChange={(value) => move(item.id, value)} options={stages.map((value) => ({ value, label: value }))} />
164
159
  </label>
165
160
  </BoardCard>
166
161
  ))}
@@ -1,5 +1,5 @@
1
1
  import * as React from "react";
2
- import {
2
+ import { FilterMenu,
3
3
  AppPage,
4
4
  SettingsSection,
5
5
  Input,
@@ -80,15 +80,11 @@ export function Example({ state = "ready" }: { state?: string }) {
80
80
  </label>
81
81
  <label className="example-field">
82
82
  Frequency
83
- <select
84
- className="example-select"
83
+ <FilterMenu
84
+ className="example-filter"
85
85
  disabled={!form.digest}
86
86
  value={form.cadence}
87
- onChange={(event) => setForm({ ...form, cadence: event.target.value })}
88
- >
89
- <option>Weekly</option>
90
- <option>Monthly</option>
91
- </select>
87
+ onValueChange={(value) => setForm({ ...form, cadence: value })} aria-label="Frequency" options={["Weekly", "Monthly"].map((value) => ({ value, label: value }))} />
92
88
  </label>
93
89
  </SettingsSection>
94
90
  <div className="flex items-center justify-end gap-3 py-6">
@@ -1,5 +1,5 @@
1
1
  import * as React from "react";
2
- import {
2
+ import { FilterMenu,
3
3
  AppPage,
4
4
  PageToolbar,
5
5
  Input,
@@ -123,16 +123,11 @@ export function Example({ state = "ready" }: { state?: string }) {
123
123
  value={search}
124
124
  onChange={(event) => setSearch(event.target.value)}
125
125
  />
126
- <select
126
+ <FilterMenu
127
127
  aria-label="Filter by status"
128
- className="example-select"
128
+ className="example-filter"
129
129
  value={status}
130
- onChange={(event) => setStatus(event.target.value)}
131
- >
132
- {["All requests", "Needs review", "In progress", "Planned"].map((value) => (
133
- <option key={value}>{value}</option>
134
- ))}
135
- </select>
130
+ onValueChange={(value) => setStatus(value)} options={["All requests", "Needs review", "In progress", "Planned"].map((value) => ({ value, label: value }))} />
136
131
  <span className="example-meta">{visible.length} requests</span>
137
132
  </PageToolbar>
138
133
  {state === "loading" ? (
@@ -223,23 +218,18 @@ export function Example({ state = "ready" }: { state?: string }) {
223
218
  <p className="example-description">{detail.detail}</p>
224
219
  <label className="example-field">
225
220
  Status
226
- <select
227
- className="example-select"
221
+ <FilterMenu
222
+ className="example-filter"
228
223
  value={detail.status}
229
- onChange={(event) =>
224
+ onValueChange={(value) =>
230
225
  setRecords((current) =>
231
226
  current.map((record) =>
232
227
  record.id === detail.id
233
- ? { ...record, status: event.target.value }
228
+ ? { ...record, status: value }
234
229
  : record,
235
230
  ),
236
231
  )
237
- }
238
- >
239
- {["Needs review", "In progress", "Planned"].map((value) => (
240
- <option key={value}>{value}</option>
241
- ))}
242
- </select>
232
+ } aria-label="Status" options={["Needs review", "In progress", "Planned"].map((value) => ({ value, label: value }))} />
243
233
  </label>
244
234
  <div className="example-meta">{detail.category}</div>
245
235
  </DetailPanel>
@@ -14,7 +14,7 @@
14
14
  },
15
15
  "dependencies": {
16
16
  "@colineapp/sdk": "^0.4.0",
17
- "@colineapp/ui": "^0.4.0",
17
+ "@colineapp/ui": "^0.4.1",
18
18
  "react": "^19.0.0",
19
19
  "react-dom": "^19.0.0",
20
20
  "zod": "^4.3.6"
@@ -22,7 +22,7 @@
22
22
  "devDependencies": {
23
23
  "@types/react": "^19.0.0",
24
24
  "@types/react-dom": "^19.0.0",
25
- "create-coline-app": "^2.5.0",
25
+ "create-coline-app": "^2.5.1",
26
26
  "typescript": "^5.8.2",
27
27
  "vitest": "^3.2.4",
28
28
  "tailwindcss": "^4.3.0",