@fluid-app/fluid-cli-portal 0.1.31 → 0.1.33

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.
@@ -1,243 +1,184 @@
1
1
  # {{projectName}}
2
2
 
3
- A custom Fluid portal built with [@fluid-app/portal-sdk](https://github.com/fluidcommerce/portal-sdk).
3
+ A Fluid portal shell built with `@fluid-app/portal-sdk` and the Fluid portal CLI.
4
4
 
5
- This is the **Starter** template - includes a dashboard with multiple widgets plus all core screens.
5
+ This starter is designed for the Fluid OS definition workflow: pull the portal definition into local JSON, edit `portal/`, push the working/draft definition back to Fluid OS, then create and activate a version when it should go live.
6
6
 
7
- ## What's Included
7
+ ## What's included
8
8
 
9
- ### Core Screens (Built-in)
10
- - **Messages** - Conversation management (MessagingScreen)
11
- - **Contacts** - Contact directory (ContactsScreen)
9
+ - **Portal shell** — Vite builds the hosted SDK shell assets.
10
+ - **Portal definition sync** `pnpm pull` and `pnpm push` manage local Fluid OS JSON under `portal/`.
11
+ - **Deployment workflow** GitHub Actions can build and upload the hosted shell assets in `dist/`.
12
+ - **Widget authoring support** — `pnpm widget:create <name>` scaffolds company-owned portal widgets.
13
+ - **AI authoring kit** — generated `AGENTS.md`, `CLAUDE.md`, `.agents/skills/...`, and `.claude/skills/...` files explain the supported portal and widget workflows.
12
14
 
13
- ### Custom Screens
14
- - **Dashboard** - Overview screen demonstrating:
15
- - TextWidget - Welcome message and tips
16
- - ToDoWidget - Task list (uses `useTodos` hook)
17
- - RecentActivityWidget - Activity feed (uses `useActivities` hook)
18
- - ChartWidget - Performance visualization
19
- - CalendarWidget - Event calendar (uses `useCalendarEvents` hook)
20
-
21
- ## Getting Started
22
-
23
- ### Development
24
-
25
- Start the development server:
15
+ ## Quick start
26
16
 
27
17
  ```bash
18
+ pnpm install
19
+ pnpm pull
28
20
  pnpm dev
29
21
  ```
30
22
 
31
- Open [http://localhost:5173](http://localhost:5173) in your browser.
32
-
33
- ### Building for Production
34
-
35
- ```bash
36
- pnpm build
37
- ```
38
-
39
- The output will be in the `dist/` folder.
23
+ Open the local URL printed by Vite, usually [http://localhost:5173](http://localhost:5173).
40
24
 
41
- ### Preview Production Build
25
+ Useful commands:
42
26
 
43
27
  ```bash
44
- pnpm preview
28
+ pnpm dev # Start Vite
29
+ pnpm build # TypeScript build plus production bundle
30
+ pnpm preview # Preview dist locally
31
+ pnpm typecheck # TypeScript checks
32
+ pnpm lint # OxLint
33
+ pnpm pull # fluid portal pull
34
+ pnpm push # fluid portal push
35
+ pnpm widget:create <name> # scaffold a company-owned portal widget
45
36
  ```
46
37
 
47
- ## Project Structure
48
-
49
- ```
50
- src/
51
- ├── main.tsx # App entry with FluidProvider setup
52
- ├── App.tsx # Main app with screen routing
53
- ├── index.css # Global styles (Tailwind CSS)
54
- ├── fluid.config.ts # API and auth configuration
55
- ├── portal.config.ts # Navigation and screen registration
56
- ├── navigation/
57
- │ └── index.tsx # Sidebar navigation component
58
- └── screens/
59
- └── Dashboard.tsx # Dashboard with multiple widgets
38
+ Set `VITE_API_URL` in `.env` if you need to point the SDK at a non-default Fluid API host.
39
+
40
+ ## Project structure
41
+
42
+ ```text
43
+ .
44
+ ├── AGENTS.md # AI-agent guidance
45
+ ├── CLAUDE.md # Bridge to AGENTS.md
46
+ ├── .agents/skills/fluid-portal-authoring/ # Portal sync workflow skill
47
+ ├── .agents/skills/fluid-widget-authoring/ # Widget authoring skill
48
+ ├── .claude/skills/fluid-portal-authoring/ # Claude-compatible copy
49
+ ├── .claude/skills/fluid-widget-authoring/ # Claude-compatible copy
50
+ ├── .github/workflows/deploy.yml # Hosted shell asset deployment
51
+ ├── src/
52
+ │ ├── main.tsx # createPortal bootstrap
53
+ │ ├── index.css # Tailwind and SDK globals
54
+ │ ├── portal.config.ts # SDK/widget package integration
55
+ │ ├── preview-entry.tsx # Builder/widget preview entry
56
+ │ └── widgets/ # Created by pnpm widget:create
57
+ ├── portal/ # Created/refreshed by fluid portal pull
58
+ └── .portal-sync/ # Generated sync metadata
60
59
  ```
61
60
 
62
- ## Deployment
61
+ ## Supported portal authoring workflow
63
62
 
64
- This project includes a GitHub Actions workflow (`.github/workflows/deploy.yml`) that deploys your portal to Google Cloud Storage + Cloud CDN.
63
+ ### 1. Pull remote Fluid OS definitions
65
64
 
66
- ### How it works
65
+ ```bash
66
+ pnpm pull
67
+ ```
67
68
 
68
- 1. **Trigger** pushes to `main` (or manual dispatch)
69
- 2. **Build** — runs `pnpm build`, producing stable filenames (`portal.js`, `portal.css`)
70
- 3. **Upload** — syncs `dist/` to `gs://portals-cdn/{{projectName}}/assets/` via `gcloud storage rsync`
71
- 4. **Invalidate** — clears the CDN cache for your tenant's asset prefix
69
+ This runs `fluid portal pull` and writes `portal/` plus `.portal-sync/`.
72
70
 
73
- ### Setup
71
+ - `portal/` contains local JSON for Fluid OS resources such as screens, themes, navigations, profiles, and definition metadata.
72
+ - `.portal-sync/` contains generated sync state for future diffs. Do not edit it by hand.
74
73
 
75
- 1. Create a GCP service account with Storage Object Admin and Compute Load Balancer Admin roles
76
- 2. Add the service account JSON key as a GitHub Actions secret named `GCP_SA_JSON`
77
- 3. Set `CDN_HOSTNAME` in `.github/workflows/deploy.yml` to your Cloud CDN load balancer's frontend domain
78
- 4. Update `GCP_PROJECT` and `CDN_URL_MAP` if your project differs from the defaults
74
+ Pull before editing unless you intentionally want to work from the current local JSON.
79
75
 
80
- ### Environment variables
76
+ ### 2. Edit `portal/` JSON
81
77
 
82
- | Variable | Where | Purpose |
83
- |----------|-------|---------|
84
- | `GCP_SA_JSON` | GitHub secret | GCP service account credentials |
85
- | `GCP_PROJECT` | Workflow env | Google Cloud project ID |
86
- | `CDN_URL_MAP` | Workflow env | Cloud CDN URL map name for cache invalidation |
87
- | `CDN_HOSTNAME` | Workflow env | CDN frontend domain (load balancer hostname) |
88
- | `VITE_ASSET_BASE` | Build env | Derived from `CDN_HOSTNAME` (set automatically) |
78
+ Make portal definition changes inside `portal/`.
89
79
 
90
- ## Customization
80
+ Guidelines:
91
81
 
92
- ### Modify Navigation
82
+ - Keep JSON valid and deterministic.
83
+ - Preserve stable IDs, slugs, and cross-resource references unless intentionally changing them.
84
+ - Update related files together when a resource reference changes.
85
+ - Do not invent unsupported fields. Match the shapes produced by `pnpm pull`.
86
+ - Keep changes small enough to review.
93
87
 
94
- Edit `src/portal.config.ts` to add, remove, or reorder navigation items:
88
+ ### 3. Validate locally
95
89
 
96
- ```typescript
97
- navigation: [
98
- // Your custom screens
99
- { id: "dashboard", label: "Dashboard", icon: "LayoutDashboard", screen: "custom:dashboard" },
90
+ Run checks that match the change:
100
91
 
101
- // Built-in core screens
102
- { id: "messaging", label: "Messages", icon: "MessageSquare", screen: "core:messaging" },
103
- // ...
104
- ],
92
+ ```bash
93
+ pnpm typecheck
94
+ pnpm lint
95
+ pnpm build
105
96
  ```
106
97
 
107
- ### Add Custom Screens
108
-
109
- 1. Create a screen component in `src/screens/`:
98
+ For visual/content changes, use local preview:
110
99
 
111
- ```tsx
112
- // src/screens/Orders.tsx
113
- import { TableWidget, TextWidget } from "@fluid-app/portal-sdk";
114
-
115
- export function OrdersScreen() {
116
- return (
117
- <div className="space-y-6">
118
- <TextWidget title="Orders" titleEnabled padding={4} borderRadius="lg" />
119
- {/* Add more widgets */}
120
- </div>
121
- );
122
- }
100
+ ```bash
101
+ pnpm dev
123
102
  ```
124
103
 
125
- 2. Register in `portal.config.ts`:
104
+ Expected result: the portal shell starts and can load the local pulled portal definition where supported by the SDK/tooling.
126
105
 
127
- ```typescript
128
- import { OrdersScreen } from "./screens/Orders";
106
+ ### 4. Push local definition changes
129
107
 
130
- customScreens: {
131
- dashboard: DashboardScreen,
132
- orders: OrdersScreen, // Add here
133
- },
108
+ ```bash
109
+ pnpm push
134
110
  ```
135
111
 
136
- 3. Add to navigation:
112
+ This runs `fluid portal push`, compares local `portal/` JSON against sync state, and updates the remote working/draft definition.
137
113
 
138
- ```typescript
139
- { id: "orders", label: "Orders", icon: "ShoppingCart", screen: "custom:orders" },
140
- ```
114
+ Push does **not** publish changes live by itself.
141
115
 
142
- ### Connect to Fluid API
116
+ ### 5. Publish a live Fluid OS version
143
117
 
144
- Edit `src/fluid.config.ts` to configure your API connection:
118
+ When the pushed working/draft definition is ready for users:
145
119
 
146
- ```typescript
147
- export const fluidConfig: FluidSDKConfig = {
148
- baseUrl: import.meta.env.VITE_API_URL ?? "https://api.fluid.app",
149
- getAuthToken: () => getStoredToken() ?? null,
150
- };
120
+ ```bash
121
+ pnpm exec fluid portal version create --activate
151
122
  ```
152
123
 
153
- ## Available Widgets
124
+ Only run this when activation is intended. If publishing needs content, design, or release approval, stop and ask first.
154
125
 
155
- Import from `@fluid-app/portal-sdk`:
126
+ ## Command boundaries
156
127
 
157
- | Widget | Description |
158
- |--------|-------------|
159
- | `TextWidget` | Text content with optional title |
160
- | `ChartWidget` | Bar, line, area, pie charts |
161
- | `TableWidget` | Data tables with columns |
162
- | `ListWidget` | Lists with icons and actions |
163
- | `ToDoWidget` | Task list (uses `useTodos`) |
164
- | `RecentActivityWidget` | Activity feed (uses `useActivities`) |
165
- | `CalendarWidget` | Calendar view (uses `useCalendarEvents`) |
166
- | `CatchUpWidget` | Catch-up items |
167
- | `AlertWidget` | Info, warning, error alerts |
168
- | `ImageWidget` | Images with sizing options |
169
- | `VideoWidget` | Embedded video players |
170
- | `CarouselWidget` | Image/content carousel |
171
- | `ContainerWidget` | Layout containers |
172
- | `SpacerWidget` | Vertical spacing |
128
+ Do not mix these up:
173
129
 
174
- ## Building Forms
130
+ - `fluid portal pull` downloads the remote portal definition into `portal/`.
131
+ - `fluid portal push` syncs local `portal/` JSON to the remote working/draft definition.
132
+ - `fluid portal version create --activate` makes the remote working/draft definition live.
133
+ - `pnpm build` creates hosted shell assets in `dist/`.
134
+ - GitHub Actions deploys hosted shell assets from `dist/`.
135
+ - `fluid portal deploy` publishes company-owned widget runtime artifacts. It does not push portal JSON and does not upload hosted shell assets.
175
136
 
176
- For data-entry screens (order forms, customer intake, settings), we recommend [React Hook Form](https://react-hook-form.com/) + [Zod](https://zod.dev/) for validation.
137
+ ## Widget work
177
138
 
178
- ### Install form dependencies
139
+ Company-owned portal widgets are supported through the portal widget scaffold:
179
140
 
180
141
  ```bash
181
- pnpm add react-hook-form zod @hookform/resolvers
142
+ pnpm widget:create stock-ticker
143
+ # or
144
+ pnpm exec fluid portal widget create stock-ticker
182
145
  ```
183
146
 
184
- ### Example
185
-
186
- ```tsx
187
- import { z } from "zod";
188
- import { useZodForm } from "@fluid-app/ui-primitives";
189
-
190
- const schema = z.object({
191
- name: z.string().min(1, "Name is required"),
192
- email: z.string().email("Invalid email"),
193
- });
194
-
195
- type FormData = z.infer<typeof schema>;
196
-
197
- export function MyFormScreen() {
198
- const {
199
- register,
200
- handleSubmit,
201
- formState: { errors, isSubmitting },
202
- } = useZodForm<FormData>(schema);
203
-
204
- const onSubmit = async (data: FormData) => {
205
- // Call your API here
206
- console.log(data);
207
- };
208
-
209
- return (
210
- <form onSubmit={handleSubmit(onSubmit)}>
211
- <input {...register("name")} placeholder="Name" />
212
- {errors.name && <p>{errors.name.message}</p>}
213
-
214
- <input {...register("email")} placeholder="Email" />
215
- {errors.email && <p>{errors.email.message}</p>}
216
-
217
- <button type="submit" disabled={isSubmitting}>
218
- {isSubmitting ? "Submitting..." : "Submit"}
219
- </button>
220
- </form>
221
- );
222
- }
223
- ```
147
+ The scaffold writes widget source under `src/widgets/<name>/` and wires the widget manifest for portal tooling. Then use the generated widget authoring skill:
148
+
149
+ - `.agents/skills/fluid-widget-authoring/SKILL.md`
150
+ - `.claude/skills/fluid-widget-authoring/SKILL.md`
151
+
152
+ That skill covers widget manifests, property schemas, theme variables, runtime CSS, validation, build, and publish workflows.
153
+
154
+ ## Hosted shell deployment
155
+
156
+ The included GitHub Actions workflow builds the portal and uploads `dist/` to Google Cloud Storage plus Cloud CDN.
224
157
 
225
- ### Starter example
158
+ 1. Push to `main` or run the workflow manually.
159
+ 2. The workflow runs `pnpm build`.
160
+ 3. It syncs `dist/` to `gs://portals-cdn/{{projectName}}/assets/`.
161
+ 4. It invalidates the CDN cache for this portal's asset prefix.
226
162
 
227
- A complete form example is included at `src/screens/ExampleForm.tsx`. To enable it:
163
+ Setup:
228
164
 
229
- 1. Install the form dependencies above
230
- 2. Uncomment the import and nav entry in `src/portal.config.ts`
165
+ 1. Create a GCP service account with Storage Object Admin and Compute Load Balancer Admin roles.
166
+ 2. Add the service account JSON key as a GitHub Actions secret named `GCP_SA_JSON`.
167
+ 3. Set `CDN_HOSTNAME` in `.github/workflows/deploy.yml` to your Cloud CDN load balancer domain.
168
+ 4. Update `GCP_PROJECT` and `CDN_URL_MAP` if your project differs from the defaults.
231
169
 
232
- ## Styling
170
+ ## AI authoring kit
233
171
 
234
- This project uses [Tailwind CSS v4](https://tailwindcss.com/).
172
+ Generated projects include portable guidance for AI coding tools:
235
173
 
236
- Edit `src/index.css` to customize the theme or add global styles.
174
+ - `AGENTS.md` canonical project instructions.
175
+ - `CLAUDE.md` — plain-file bridge to `AGENTS.md`.
176
+ - `.agents/skills/fluid-portal-authoring/SKILL.md` — portal pull/edit/push/version workflow.
177
+ - `.agents/skills/fluid-widget-authoring/SKILL.md` — widget authoring and validation workflow.
178
+ - `.claude/skills/...` — Claude-compatible copies generated from the same template skill files.
237
179
 
238
- ## Learn More
180
+ ## Learn more
239
181
 
240
- - [Fluid Commerce Documentation](https://docs.fluidcommerce.com)
241
- - [portal-sdk GitHub](https://github.com/fluidcommerce/portal-sdk)
242
- - [Vite Documentation](https://vite.dev)
243
- - [React Documentation](https://react.dev)
182
+ - Fluid Commerce Documentation: https://docs.fluidcommerce.com
183
+ - Vite Documentation: https://vite.dev
184
+ - React Documentation: https://react.dev
@@ -1,26 +0,0 @@
1
- /**
2
- * Code-defined navigation items.
3
- *
4
- * Synced to the Fluid OS API on `fluid deploy` with source: "code".
5
- * Position is derived from array order. Items without a slug are
6
- * treated as section headers.
7
- *
8
- * User-created navigation items (from the admin dashboard) are not
9
- * affected — only items with source: "code" are managed.
10
- *
11
- * IMPORTANT: Do not add import statements to this file. The Fluid CLI
12
- * reads this file without resolving dependencies, so it must be
13
- * self-contained static data only.
14
- */
15
- export const navigation = [
16
- {
17
- label: "Dashboard",
18
- slug: "dashboard",
19
- icon: "home",
20
- },
21
- {
22
- label: "MySite",
23
- slug: "my-site",
24
- icon: "globe",
25
- },
26
- ];
@@ -1,133 +0,0 @@
1
- import {
2
- TextWidget,
3
- ChartWidget,
4
- ToDoWidget,
5
- RecentActivityWidget,
6
- CalendarWidget,
7
- } from "@fluid-app/portal-sdk";
8
-
9
- /**
10
- * Sample chart data extracted as a module-level constant.
11
- *
12
- * The data is extracted as a constant to:
13
- * 1. Keep the component cleaner
14
- * 2. Allow reuse/testing if needed
15
- * 3. Make it clear the data is static
16
- */
17
- const MONTHLY_PERFORMANCE_DATA = [
18
- { name: "Jan", value: 4000 },
19
- { name: "Feb", value: 3000 },
20
- { name: "Mar", value: 5000 },
21
- { name: "Apr", value: 4500 },
22
- { name: "May", value: 6000 },
23
- { name: "Jun", value: 5500 },
24
- ];
25
-
26
- /**
27
- * Dashboard Screen
28
- *
29
- * This is the default landing screen for the portal.
30
- * It demonstrates using various widgets from portal-sdk:
31
- *
32
- * - TextWidget: Display text content with optional title
33
- * - ToDoWidget: Task list (uses useTodos hook)
34
- * - RecentActivityWidget: Activity feed (uses useActivities hook)
35
- * - ChartWidget: Bar, line, area, pie charts
36
- * - CalendarWidget: Calendar view (uses useCalendarEvents hook)
37
- *
38
- * These widgets fetch data from hooks that connect to your Fluid API.
39
- * In development, they show demo data. Configure fluid.config.ts
40
- * to connect to your Fluid instance.
41
- */
42
- export function DashboardScreen() {
43
- return (
44
- <div className="space-y-6">
45
- {/* Welcome section */}
46
- <TextWidget
47
- titleEnabled
48
- title="Welcome to Your Portal"
49
- description="This is your portal dashboard. Use widgets from @fluid-app/portal-sdk to build powerful interfaces for your team."
50
- padding={4}
51
- borderRadius="lg"
52
- />
53
-
54
- {/* Stats row - sample static data */}
55
- <div className="grid gap-6 md:grid-cols-3">
56
- <div className="bg-background rounded-lg p-4 shadow-sm">
57
- <p className="text-foreground text-3xl font-bold">$12,450</p>
58
- <p className="text-muted-foreground mt-1 text-sm">Total Sales</p>
59
- </div>
60
-
61
- <div className="bg-background rounded-lg p-4 shadow-sm">
62
- <p className="text-foreground text-3xl font-bold">24</p>
63
- <p className="text-muted-foreground mt-1 text-sm">Active Orders</p>
64
- </div>
65
-
66
- <div className="bg-background rounded-lg p-4 shadow-sm">
67
- <p className="text-foreground text-3xl font-bold">156</p>
68
- <p className="text-muted-foreground mt-1 text-sm">Customers</p>
69
- </div>
70
- </div>
71
-
72
- {/* Main content grid - Tasks and Activity */}
73
- <div className="grid gap-6 lg:grid-cols-2">
74
- {/* To-Do Widget - fetches tasks via useTodos hook */}
75
- <ToDoWidget
76
- titleEnabled
77
- titleText="Tasks"
78
- maxItems={5}
79
- padding={4}
80
- borderRadius="lg"
81
- />
82
-
83
- {/* Recent Activity Widget - fetches via useActivities hook */}
84
- <RecentActivityWidget
85
- titleEnabled
86
- titleText="Recent Activity"
87
- maxItemsToShow={5}
88
- padding={4}
89
- borderRadius="lg"
90
- />
91
- </div>
92
-
93
- {/* Performance chart - uses extracted constant data */}
94
- <ChartWidget
95
- titleEnabled
96
- title="Monthly Performance"
97
- chartType="bar"
98
- data={MONTHLY_PERFORMANCE_DATA}
99
- padding={4}
100
- borderRadius="lg"
101
- />
102
-
103
- {/* Calendar preview - uses useCalendarEvents hook */}
104
- <CalendarWidget
105
- titleEnabled
106
- titleText="Upcoming Events"
107
- padding={4}
108
- borderRadius="lg"
109
- />
110
-
111
- {/* Getting started tips */}
112
- <div className="grid gap-6 md:grid-cols-2">
113
- <TextWidget
114
- titleEnabled
115
- title="Customize Navigation"
116
- description="Edit portal.config.ts to add, remove, or reorder navigation items. Core screens (Messaging, Contacts) are built-in."
117
- padding={4}
118
- borderRadius="lg"
119
- background={{ type: "solid", color: "muted" }}
120
- />
121
-
122
- <TextWidget
123
- titleEnabled
124
- title="Connect Your Data"
125
- description="Configure fluid.config.ts with your Fluid API credentials. Widgets will automatically fetch real data from your account."
126
- padding={4}
127
- borderRadius="lg"
128
- background={{ type: "solid", color: "muted" }}
129
- />
130
- </div>
131
- </div>
132
- );
133
- }