@fluid-app/fluid-cli-widget 0.1.33 → 0.1.35
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/authoring/commands.md +195 -0
- package/dist/{index-JKSTx5pZ.d.mts → index-DW3icMxz.d.mts} +7 -1
- package/dist/index-DW3icMxz.d.mts.map +1 -0
- package/dist/index.d.mts +3 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +4 -4
- package/dist/index.mjs.map +1 -1
- package/dist/{src-_wfDoBLp.mjs → src-CzqGGQnf.mjs} +12 -2
- package/dist/src-CzqGGQnf.mjs.map +1 -0
- package/dist/vite/plugin.d.mts +1 -1
- package/dist/vite/plugin.mjs +1 -1
- package/package.json +4 -3
- package/templates/default/AGENTS.md +45 -256
- package/templates/default/README.md.template +110 -316
- package/templates/default/manifest.ts.template +16 -0
- package/templates/default/styles.css +20 -27
- package/dist/index-JKSTx5pZ.d.mts.map +0 -1
- package/dist/src-_wfDoBLp.mjs.map +0 -1
|
@@ -1,340 +1,134 @@
|
|
|
1
1
|
# {{projectName}}
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A standalone Fluid Remote DOM widget package associated with an existing
|
|
4
|
+
Droplet. This project publishes widget code and metadata. It does not create a
|
|
5
|
+
Droplet, portal application, iframe preview, or backend service.
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
## Start development
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
Requirements: Node.js, pnpm, and a Fluid CLI login for commands that read or
|
|
10
|
+
write Droplet state.
|
|
8
11
|
|
|
9
12
|
```bash
|
|
10
|
-
pnpm
|
|
11
|
-
pnpm
|
|
12
|
-
pnpm
|
|
13
|
-
pnpm build # Validate and build runtime artifacts
|
|
14
|
-
pnpm run widget:link # Link this project to an existing Fluid droplet
|
|
15
|
-
pnpm run widget:publish # Publish to the linked droplet
|
|
16
|
-
pnpm run widget:publish --dry-run # Build upload payload without uploading
|
|
13
|
+
pnpm install
|
|
14
|
+
pnpm run widget:link
|
|
15
|
+
pnpm dev
|
|
17
16
|
```
|
|
18
17
|
|
|
19
|
-
|
|
18
|
+
`pnpm dev` starts Vite, normally on http://localhost:5174. It serves the live
|
|
19
|
+
package descriptor at `/__widget-packages__` and the worker bootstrap at
|
|
20
|
+
`/__runtime-entry__`. A portal or builder host loads those endpoints. The
|
|
21
|
+
command does not create a separate preview application or publication build.
|
|
20
22
|
|
|
21
|
-
|
|
22
|
-
.
|
|
23
|
-
|
|
24
|
-
├── CLAUDE.md # Claude-compatible AI authoring guidance
|
|
25
|
-
├── .agents/skills/ # Agent skill docs for this project
|
|
26
|
-
├── .claude/skills/ # Claude skill docs for this project
|
|
27
|
-
├── fluid.widget.config.ts # Widget CLI ownership + package exports
|
|
28
|
-
├── manifest.ts # defineWidget/defineWidgetPackage metadata
|
|
29
|
-
├── styles.css # Shadow-root runtime styles
|
|
30
|
-
├── src/index.ts # Remote DOM worker entry
|
|
31
|
-
└── src/widgets/review-carousel/ReviewCarousel.tsx
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
## Local development endpoints
|
|
35
|
-
|
|
36
|
-
`pnpm dev` exposes:
|
|
37
|
-
|
|
38
|
-
- `/__widget-packages__`: the local canonical package descriptor consumed by Fluid hosts.
|
|
39
|
-
- `/__runtime-entry__`: the Remote DOM worker entry referenced by that descriptor.
|
|
40
|
-
|
|
41
|
-
The standalone project does not include a second host or iframe preview app.
|
|
42
|
-
Portal and builder hosts render the worker through the shared Remote DOM runtime.
|
|
43
|
-
|
|
44
|
-
The standalone source of truth is `manifest.ts`, with droplet ownership in
|
|
45
|
-
`fluid.widget.config.ts`. Company portal projects instead use
|
|
46
|
-
`src/widgets.config.ts` and `fluid portal deploy`.
|
|
47
|
-
|
|
48
|
-
## Package boundary
|
|
49
|
-
|
|
50
|
-
Stay inside the standalone widget package:
|
|
51
|
-
|
|
52
|
-
- Put reusable widget components under `src/widgets/`.
|
|
53
|
-
- Put package and palette metadata in `manifest.ts`.
|
|
54
|
-
- Keep runtime registration in `src/index.ts`.
|
|
55
|
-
- Keep runtime styles in `styles.css` or CSS files imported by widget modules.
|
|
56
|
-
- Do not add Next.js app folders, Rails code, API servers, portal app shells, or droplet scaffolding.
|
|
57
|
-
|
|
58
|
-
Droplet ownership is managed by the Fluid widget CLI through `fluid.widget.config.ts`. Link the project with `pnpm run widget:link` or pass a droplet option to CLI commands when needed.
|
|
59
|
-
|
|
60
|
-
## Authoring widgets
|
|
61
|
-
|
|
62
|
-
Widgets are authored with `defineWidget()` and grouped with `defineWidgetPackage()` from `@fluid-app/portal-sdk/widgets/worker`.
|
|
63
|
-
|
|
64
|
-
`defineWidget()` describes one widget:
|
|
65
|
-
|
|
66
|
-
- `name`: stable URL-safe widget name. Changing it changes the generated widget type.
|
|
67
|
-
- `component`: React component rendered inside the Remote DOM worker.
|
|
68
|
-
- `displayName`, `description`, `icon`, `category`: builder palette metadata.
|
|
69
|
-
- `defaultProps`: JSON-serializable defaults for new widget instances.
|
|
70
|
-
- `propertySchema`: builder property panel fields.
|
|
71
|
-
- `container`: `block`, `card`, `inline`, or `fullscreen`.
|
|
72
|
-
- `uses`: every typed portal function called by this widget. Pass the function values instead of writing capability names.
|
|
73
|
-
- `resizable`: false/omitted, true, `horizontal`, `vertical`, `both`, or an object with horizontal/vertical flags and optional minimum sizes.
|
|
74
|
-
|
|
75
|
-
`defineWidgetPackage()` describes the package:
|
|
76
|
-
|
|
77
|
-
- Keep `packageType` set to `droplet` for this template.
|
|
78
|
-
- Keep `widgets` as the array of widgets exported by this package.
|
|
79
|
-
- Use a SemVer `version` without build metadata.
|
|
80
|
-
- Do not manually set `packageStableId` in this generated droplet template; the CLI injects the linked droplet key during validation, build, and publish.
|
|
81
|
-
- Do not author runtime artifact URLs. Dev and build inject the served `workerEntryUrl`.
|
|
82
|
-
|
|
83
|
-
## Manifest structure
|
|
84
|
-
|
|
85
|
-
`manifest.ts` should remain the single source of package metadata:
|
|
86
|
-
|
|
87
|
-
```ts
|
|
88
|
-
import {
|
|
89
|
-
defineWidget,
|
|
90
|
-
defineWidgetPackage,
|
|
91
|
-
} from "@fluid-app/portal-sdk/widgets/worker";
|
|
92
|
-
import { ExampleWidget } from "./src/widgets/example/ExampleWidget";
|
|
93
|
-
|
|
94
|
-
export const exampleWidget = defineWidget({
|
|
95
|
-
name: "ExampleWidget",
|
|
96
|
-
component: ExampleWidget,
|
|
97
|
-
displayName: "Example Widget",
|
|
98
|
-
description: "A focused reusable Fluid widget.",
|
|
99
|
-
icon: "box",
|
|
100
|
-
category: "components",
|
|
101
|
-
container: "card",
|
|
102
|
-
defaultProps: {
|
|
103
|
-
title: "Featured content",
|
|
104
|
-
},
|
|
105
|
-
propertySchema: {
|
|
106
|
-
tabsConfig: [{ id: "content", label: "Content" }],
|
|
107
|
-
dataSourceTargetProps: ["title"],
|
|
108
|
-
fields: [
|
|
109
|
-
{
|
|
110
|
-
key: "title",
|
|
111
|
-
label: "Title",
|
|
112
|
-
type: "text",
|
|
113
|
-
defaultValue: "Featured content",
|
|
114
|
-
tab: "content",
|
|
115
|
-
group: "Copy",
|
|
116
|
-
},
|
|
117
|
-
],
|
|
118
|
-
},
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
export const widgetPackage = defineWidgetPackage({
|
|
122
|
-
scope: "droplet",
|
|
123
|
-
version: "0.1.0",
|
|
124
|
-
packageType: "droplet",
|
|
125
|
-
widgets: [exampleWidget],
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
export const widgetPackages = [widgetPackage] as const;
|
|
129
|
-
export default widgetPackage;
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
## Typed portal functions
|
|
133
|
-
|
|
134
|
-
Worker code imports typed portal functions from
|
|
135
|
-
`@fluid-app/portal-sdk/widgets/worker`, calls them directly, and lists the same
|
|
136
|
-
function values in the widget's `uses` array:
|
|
137
|
-
|
|
138
|
-
```ts
|
|
139
|
-
import {
|
|
140
|
-
defineWidget,
|
|
141
|
-
getUserAccount,
|
|
142
|
-
navigateTo,
|
|
143
|
-
} from "@fluid-app/portal-sdk/widgets/worker";
|
|
144
|
-
|
|
145
|
-
async function openProfile(): Promise<void> {
|
|
146
|
-
const account = await getUserAccount();
|
|
147
|
-
await navigateTo(account.slug);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
export const profileButtonWidget = defineWidget({
|
|
151
|
-
name: "ProfileButton",
|
|
152
|
-
component: ProfileButton,
|
|
153
|
-
uses: [getUserAccount, navigateTo],
|
|
154
|
-
});
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
Available built-ins are `getUserAccount`, `getStore`, `getPortalApp`,
|
|
158
|
-
`getPortalProfile`, `getNavigationState`, `buildPortalHref`, `navigateTo`,
|
|
159
|
-
`getFullscreenState`, `requestFullscreen`, and `exitFullscreen`. Getters await
|
|
160
|
-
and return resolved data instead of exposing query-status snapshots. A
|
|
161
|
-
navigation target may be a slug string, `{ slug }`, or `{ href }`.
|
|
162
|
-
|
|
163
|
-
`defineWidget()` derives the existing descriptor declarations from `uses`.
|
|
164
|
-
Both the worker and host enforce those declarations. If a call fails with
|
|
165
|
-
`PortalFunctionError` code `NOT_DECLARED`, add that function to the widget's
|
|
166
|
-
`uses` array. Do not author a raw `capabilities` array.
|
|
167
|
-
|
|
168
|
-
`getUserAccount()` returns an allowlisted account view. It may include email,
|
|
169
|
-
but excludes phone numbers, addresses, payment details, credentials,
|
|
170
|
-
government or tax identifiers, dates of birth, raw metadata, and unknown
|
|
171
|
-
future fields. The host's `tenantClient` is not exposed to widgets.
|
|
172
|
-
|
|
173
|
-
For company-specific operations, define a shared typed function with
|
|
174
|
-
`definePortalFunction<Output, Input>({ capability, version, method })`, call it
|
|
175
|
-
directly, and include it in `uses`. The portal owner installs a typed host
|
|
176
|
-
handler with `implementPortalFunction(function, handler)` in
|
|
177
|
-
`remoteWidgets.functions`. Built-ins cannot be overridden; duplicate custom
|
|
178
|
-
implementations are rejected.
|
|
23
|
+
`widget:link` selects an existing Droplet and writes its ID to
|
|
24
|
+
`fluid.widget.config.ts`. If the ID is already present, validation and local
|
|
25
|
+
builds do not require a login.
|
|
179
26
|
|
|
180
|
-
|
|
181
|
-
strings, arrays, and objects composed from those values. Do not use
|
|
182
|
-
`undefined`, functions, Dates, Maps, Sets, class instances, `NaN`, or
|
|
183
|
-
`Infinity`. The runtime validates the protocol boundary as well as the
|
|
184
|
-
TypeScript contract.
|
|
27
|
+
## Project map
|
|
185
28
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
Property schemas define editable builder controls. They must be JSON-serializable.
|
|
189
|
-
|
|
190
|
-
Top-level schema keys:
|
|
191
|
-
|
|
192
|
-
- `tabsConfig`: optional array of tab objects with `id` and `label`.
|
|
193
|
-
- `fields`: array of controls shown in the builder property panel.
|
|
194
|
-
- `dataSourceTargetProps`: prop keys that can be populated from configured data sources.
|
|
195
|
-
- `itemConfigSchema`: optional per-item configuration schema for custom data-source selections.
|
|
196
|
-
- `validate`: optional custom validator in in-app manifests; avoid it for published standalone metadata because package metadata must be serializable.
|
|
197
|
-
|
|
198
|
-
Base field keys:
|
|
199
|
-
|
|
200
|
-
| Key | Purpose |
|
|
29
|
+
| Path | Owner and lifecycle |
|
|
201
30
|
| --- | --- |
|
|
202
|
-
| `
|
|
203
|
-
| `
|
|
204
|
-
| `
|
|
205
|
-
| `
|
|
206
|
-
| `
|
|
207
|
-
| `
|
|
208
|
-
| `
|
|
209
|
-
| `
|
|
210
|
-
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
|
215
|
-
|
|
|
216
|
-
|
|
|
217
|
-
|
|
|
218
|
-
|
|
|
219
|
-
| `
|
|
220
|
-
| `
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
- Keep prop values JSON-serializable.
|
|
251
|
-
- Avoid fetching inside the component when a prop or data source can provide the data.
|
|
252
|
-
- Use `itemConfigSchema` when each selected item needs widget-specific settings.
|
|
253
|
-
|
|
254
|
-
## Component quality bar
|
|
255
|
-
|
|
256
|
-
- Use TypeScript interfaces for public props.
|
|
257
|
-
- Provide defaults for optional props.
|
|
258
|
-
- Treat incoming props as untrusted and validate before reading nested values.
|
|
259
|
-
- Keep render logic deterministic and side-effect free.
|
|
260
|
-
- Clean up effects and subscriptions.
|
|
261
|
-
- Keep bundles small; avoid heavy libraries unless the widget requires them.
|
|
262
|
-
- Avoid secrets, tenant credentials, local URLs, and environment-only assumptions.
|
|
263
|
-
- Prefer semantic HTML over div-only structures.
|
|
264
|
-
|
|
265
|
-
## Accessibility
|
|
266
|
-
|
|
267
|
-
- Use headings, sections, lists, buttons, labels, and form elements semantically.
|
|
268
|
-
- Make every interactive element keyboard reachable.
|
|
269
|
-
- Provide visible focus states.
|
|
270
|
-
- Add `aria-label` to icon-only controls.
|
|
271
|
-
- Add alt text to meaningful images and empty alt text to decorative images.
|
|
272
|
-
- Preserve logical heading order inside the widget.
|
|
273
|
-
- Use ARIA live regions for important dynamic updates.
|
|
274
|
-
- Respect reduced-motion preferences for animation-heavy widgets.
|
|
275
|
-
- Use semantic foreground/background token pairs for contrast.
|
|
276
|
-
|
|
277
|
-
## Runtime CSS and theme tokens
|
|
278
|
-
|
|
279
|
-
Fluid hosts provide semantic CSS variables. Prefer token-based CSS instead of hard-coded palettes.
|
|
280
|
-
|
|
281
|
-
Common CSS variables:
|
|
282
|
-
|
|
283
|
-
- Surfaces: `--background`, `--card`, `--popover`.
|
|
284
|
-
- Text: `--foreground`, `--card-foreground`, `--popover-foreground`.
|
|
285
|
-
- Brand/actions: `--primary`, `--primary-foreground`.
|
|
286
|
-
- Supporting UI: `--secondary`, `--secondary-foreground`, `--muted`, `--muted-foreground`, `--accent`, `--accent-foreground`.
|
|
287
|
-
- Status/chrome: `--destructive`, `--destructive-foreground`, `--border`, `--input`, `--ring`.
|
|
288
|
-
- Charts: `--chart-1`, `--chart-2`, `--chart-3`, `--chart-4`, `--chart-5`.
|
|
289
|
-
- Radius: `--radius`, `--radius-sm`, `--radius-md`, `--radius-lg`, `--radius-xl`.
|
|
290
|
-
- Theme-engine aliases may include `--font-header`, `--font-body`, `--font-size-extra-small`, `--font-size-small`, `--font-size-regular`, `--font-size-large`, `--font-size-extra-large`, and `--font-size-giant`.
|
|
291
|
-
|
|
292
|
-
Tailwind equivalents, when Tailwind is available, are semantic utilities such as `bg-background`, `text-foreground`, `bg-card`, `text-card-foreground`, `bg-primary`, `text-primary-foreground`, `border-border`, `ring-ring`, `rounded-lg`, `text-sm`, and `text-xl`. This standalone template uses plain CSS for runtime portability.
|
|
293
|
-
|
|
294
|
-
Light/dark behavior:
|
|
295
|
-
|
|
296
|
-
- Fluid themes update variables for light and dark mode.
|
|
297
|
-
- Explicit dark mode is represented by `data-theme-mode="dark"` on a host element.
|
|
298
|
-
- Some hosts also use system dark mode when no explicit mode is selected.
|
|
299
|
-
- Prefer semantic variables so both modes work automatically.
|
|
300
|
-
- If mode-specific styling is unavoidable, scope it with `[data-theme-mode="dark"]` and keep values token-based.
|
|
301
|
-
|
|
302
|
-
Runtime CSS rules:
|
|
303
|
-
|
|
304
|
-
- Import runtime CSS from the guarded build import in `src/index.ts` or another worker module included in the build.
|
|
305
|
-
- Prefix selectors with the widget name to prevent host leakage.
|
|
306
|
-
- Do not rely on `body` styles for published runtime rendering.
|
|
307
|
-
- If published styles are missing, check that the CSS import is reachable from the built entry.
|
|
308
|
-
|
|
309
|
-
## Validation, build, and publish preflight
|
|
310
|
-
|
|
311
|
-
Before shipping changes, run:
|
|
31
|
+
| `manifest.ts` | Author-owned package scope, key, version, and widget registrations. Edit for every release or registration change. |
|
|
32
|
+
| `fluid.widget.config.ts` | Author-owned Droplet link and package export. Change the Droplet only when intentionally relinking the project. |
|
|
33
|
+
| `src/index.ts` | Worker entry that starts the package and imports package CSS. |
|
|
34
|
+
| `src/widgets/` | Author-owned widget components. The starter widget is an example and can be replaced. |
|
|
35
|
+
| `styles.css` | Author-owned, package-scoped runtime CSS. Keep its guarded import in `src/index.ts`. |
|
|
36
|
+
| `src/vite-env.d.ts` | Vite environment type declarations. |
|
|
37
|
+
| `vite.config.ts`, `tsconfig.json` | Build configuration. Change only for a concrete toolchain requirement. |
|
|
38
|
+
| `package.json` | Project scripts and dependencies. Author-owned after scaffold creation. |
|
|
39
|
+
| lockfile | Dependency resolution. Commit when dependency changes are intentional. |
|
|
40
|
+
| `.gitignore` | Source-control exclusions for local and generated files. |
|
|
41
|
+
| `.oxlintrc.json` | Generated lint configuration. Change only when project lint policy changes. |
|
|
42
|
+
| `README.md` | Human setup, lifecycle, and recovery guidance. |
|
|
43
|
+
| `AGENTS.md`, `CLAUDE.md` | Root coding-agent guidance. `CLAUDE.md` points to the canonical `AGENTS.md`. |
|
|
44
|
+
| `.agents/skills/fluid-widget-authoring/` | Widget task procedures and reference guidance. |
|
|
45
|
+
| `.claude/skills/` | Compatibility view of the same skill. Do not maintain different instructions there. |
|
|
46
|
+
| `.fluid/widget-build/` | Temporary compilation workspace. Generated and safe to recreate. |
|
|
47
|
+
| `.fluid/tmp/` | Temporary CLI state. Generated and safe to recreate. |
|
|
48
|
+
| `.fluid/widget-dist/` | Publication output: `widget.js`, `manifest.json`, `publish-manifest.json`, CSS, maps, and referenced assets. Generated; never edit or commit it. |
|
|
49
|
+
| `node_modules/` | Installed dependencies. Generated. |
|
|
50
|
+
|
|
51
|
+
## Package ownership
|
|
52
|
+
|
|
53
|
+
`manifest.ts` is the package contract. Use the installed generated API for its
|
|
54
|
+
complete fields, accepted values, and widget-definition shape; this README does
|
|
55
|
+
not repeat them. Keep package identity, widget registration, defaults,
|
|
56
|
+
property-schema metadata, and declared runtime uses together there.
|
|
57
|
+
|
|
58
|
+
`fluid.widget.config.ts` links that package to a Droplet. It does not define
|
|
59
|
+
package identity, version, or widgets.
|
|
60
|
+
|
|
61
|
+
All metadata and props must satisfy the installed JSON-value types. Do not
|
|
62
|
+
maintain a separate value whitelist in project documentation.
|
|
63
|
+
|
|
64
|
+
## Theme compliance
|
|
65
|
+
|
|
66
|
+
Widgets must work with the active portal theme and its supported color modes.
|
|
67
|
+
Use the host semantic CSS variables for colors, typography, spacing, radii,
|
|
68
|
+
borders, focus rings, and charts. Use a semantic property only when authors
|
|
69
|
+
need to choose among theme roles.
|
|
70
|
+
|
|
71
|
+
Use `type: "colorSelect"` for a color property. It stores a semantic token.
|
|
72
|
+
The legacy `type: "color"` field is deprecated because it stores an arbitrary
|
|
73
|
+
color that can conflict with the theme. Avoid `colorPicker`, literal colors,
|
|
74
|
+
custom font controls, and custom radius or spacing controls unless the product
|
|
75
|
+
requirement cannot be expressed by the theme engine. Document any necessary
|
|
76
|
+
exception in the field description and provide a theme-derived default.
|
|
77
|
+
|
|
78
|
+
## Validate and build
|
|
312
79
|
|
|
313
80
|
```bash
|
|
314
81
|
pnpm typecheck
|
|
315
82
|
pnpm validate
|
|
316
83
|
pnpm build
|
|
317
|
-
pnpm run widget:publish --dry-run
|
|
318
84
|
```
|
|
319
85
|
|
|
320
|
-
|
|
86
|
+
Validation checks package metadata and source configuration. Build validates,
|
|
87
|
+
typechecks, and writes `.fluid/widget-dist/`. Inspect `manifest.json` and the
|
|
88
|
+
widget in a portal host; a successful build cannot prove visual, accessibility,
|
|
89
|
+
or theme compatibility.
|
|
90
|
+
|
|
91
|
+
If a build fails, fix the first reported source or metadata error. Remove only
|
|
92
|
+
generated `.fluid/widget-build/`, `.fluid/tmp/`, or `.fluid/widget-dist/` when
|
|
93
|
+
a clean retry is needed. Do not delete author source or configuration.
|
|
94
|
+
|
|
95
|
+
## Publish and inspect
|
|
321
96
|
|
|
322
97
|
```bash
|
|
98
|
+
pnpm run widget:publish --dry-run
|
|
323
99
|
pnpm run widget:publish
|
|
100
|
+
pnpm exec fluid widget status
|
|
101
|
+
pnpm exec fluid widget logs
|
|
324
102
|
```
|
|
325
103
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
104
|
+
The dry run builds and validates the upload payload without creating a remote
|
|
105
|
+
version. Actual publication requires authentication and creates an immutable
|
|
106
|
+
Droplet widget version. Increase `version` in `manifest.ts` before publishing a
|
|
107
|
+
new release.
|
|
108
|
+
|
|
109
|
+
`fluid widget push` first attempts source synchronization and then publication.
|
|
110
|
+
Repository divergence stops before publication. If source synchronization is
|
|
111
|
+
unavailable, the command can continue with publication only. Publication can
|
|
112
|
+
also fail after source synchronization succeeds. Read the phase result before
|
|
113
|
+
retrying; do not assume either phase rolled back the other.
|
|
114
|
+
|
|
115
|
+
## Network-enabled widgets
|
|
116
|
+
|
|
117
|
+
Declare `networkAccess` in the widget `uses` array only for public HTTP APIs.
|
|
118
|
+
A portal author must approve each exact package ID, package version, and
|
|
119
|
+
capability version. Updating any value invalidates the previous grant.
|
|
120
|
+
|
|
121
|
+
Granted fetch uses the worker's native origin. Fluid does not inject cookies,
|
|
122
|
+
credentials, tokens, or headers. The installed runtime API owns the exact
|
|
123
|
+
destination and ambient-API contract. DNS results and redirect destinations
|
|
124
|
+
are not revalidated, so package review remains required. Any portal data given
|
|
125
|
+
to the widget can be sent to an external service. Worker isolation does not
|
|
126
|
+
protect the reputation of `*.fluid.app` from widget behavior.
|
|
127
|
+
|
|
128
|
+
## Detailed guidance
|
|
129
|
+
|
|
130
|
+
- `.agents/skills/fluid-widget-authoring/SKILL.md`
|
|
131
|
+
- Installed API contract:
|
|
132
|
+
`node_modules/@fluid-app/portal-sdk/authoring/widget-api/api.md`
|
|
133
|
+
- Installed command contract:
|
|
134
|
+
`node_modules/@fluid-app/fluid-cli-widget/authoring/commands.md`
|
|
@@ -45,6 +45,7 @@ export const reviewCarouselWidget = defineWidget({
|
|
|
45
45
|
key: "eyebrow",
|
|
46
46
|
label: "Eyebrow",
|
|
47
47
|
type: "text",
|
|
48
|
+
description: "Short text shown above the review title.",
|
|
48
49
|
defaultValue: "Customer stories",
|
|
49
50
|
tab: "content",
|
|
50
51
|
group: "Copy",
|
|
@@ -53,10 +54,25 @@ export const reviewCarouselWidget = defineWidget({
|
|
|
53
54
|
key: "title",
|
|
54
55
|
label: "Title",
|
|
55
56
|
type: "text",
|
|
57
|
+
description: "Heading displayed above the review carousel.",
|
|
56
58
|
defaultValue: "Reviews that travel with your portal",
|
|
57
59
|
tab: "content",
|
|
58
60
|
group: "Copy",
|
|
59
61
|
},
|
|
62
|
+
{
|
|
63
|
+
key: "dataSource",
|
|
64
|
+
label: "Data Source",
|
|
65
|
+
type: "dataSource",
|
|
66
|
+
description: "Configure the review records supplied to the carousel.",
|
|
67
|
+
targetProps: [
|
|
68
|
+
{
|
|
69
|
+
key: "reviews",
|
|
70
|
+
description: "Review records rendered by the carousel.",
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
tab: "content",
|
|
74
|
+
group: "Data",
|
|
75
|
+
},
|
|
60
76
|
],
|
|
61
77
|
},
|
|
62
78
|
});
|
|
@@ -8,18 +8,13 @@
|
|
|
8
8
|
* root, and the same widget then looks correct on every portal and in
|
|
9
9
|
* both light and dark — without a second, hand-written dark palette.
|
|
10
10
|
*
|
|
11
|
-
* • Every var() carries a fallback. Tokens are absent in some hosts (a bare
|
|
12
|
-
* dev harness, a preview surface that has not adopted the portal theme),
|
|
13
|
-
* and a widget must still be legible there rather than collapsing to
|
|
14
|
-
* unstyled text. The fallback is a floor, never the intended look.
|
|
15
|
-
*
|
|
16
11
|
* • Selectors stay prefixed with the widget name so nothing leaks into the
|
|
17
12
|
* host, and nothing here depends on global body styles.
|
|
18
13
|
*
|
|
19
|
-
* Do not
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* styling
|
|
14
|
+
* Do not add fallback brand colors. A missing host theme contract is an
|
|
15
|
+
* integration error; silently replacing it with widget-owned colors hides the
|
|
16
|
+
* error and breaks theme compliance. See
|
|
17
|
+
* .agents/skills/fluid-widget-authoring/references/runtime-and-styling.md. */
|
|
23
18
|
|
|
24
19
|
.review-carousel {
|
|
25
20
|
font-family: var(
|
|
@@ -32,16 +27,15 @@
|
|
|
32
27
|
"Segoe UI",
|
|
33
28
|
sans-serif
|
|
34
29
|
);
|
|
35
|
-
border: 1px solid var(--border
|
|
36
|
-
border-radius: var(--radius-xl
|
|
30
|
+
border: 1px solid var(--border);
|
|
31
|
+
border-radius: var(--radius-xl);
|
|
37
32
|
padding: 32px;
|
|
38
|
-
background: var(--card
|
|
39
|
-
box-shadow: 0 24px 80px rgb(15 23 42 / 10%);
|
|
33
|
+
background: var(--card);
|
|
40
34
|
}
|
|
41
35
|
|
|
42
36
|
.review-carousel__eyebrow {
|
|
43
37
|
margin: 0 0 8px;
|
|
44
|
-
color: var(--primary
|
|
38
|
+
color: var(--primary);
|
|
45
39
|
font-size: 0.75rem;
|
|
46
40
|
font-weight: 700;
|
|
47
41
|
letter-spacing: 0.08em;
|
|
@@ -50,8 +44,8 @@
|
|
|
50
44
|
|
|
51
45
|
.review-carousel__title {
|
|
52
46
|
margin: 0;
|
|
53
|
-
color: var(--card-foreground
|
|
54
|
-
font-family: var(--font-header
|
|
47
|
+
color: var(--card-foreground);
|
|
48
|
+
font-family: var(--font-header);
|
|
55
49
|
font-size: clamp(2rem, 4vw, 3.5rem);
|
|
56
50
|
line-height: 0.95;
|
|
57
51
|
letter-spacing: -0.05em;
|
|
@@ -59,7 +53,7 @@
|
|
|
59
53
|
|
|
60
54
|
.review-carousel__quote {
|
|
61
55
|
margin: 32px 0 0;
|
|
62
|
-
color: var(--card-foreground
|
|
56
|
+
color: var(--card-foreground);
|
|
63
57
|
font-size: clamp(1.25rem, 2vw, 1.75rem);
|
|
64
58
|
line-height: 1.35;
|
|
65
59
|
}
|
|
@@ -74,13 +68,13 @@
|
|
|
74
68
|
|
|
75
69
|
.review-carousel__author {
|
|
76
70
|
margin: 0;
|
|
77
|
-
color: var(--card-foreground
|
|
71
|
+
color: var(--card-foreground);
|
|
78
72
|
font-weight: 700;
|
|
79
73
|
}
|
|
80
74
|
|
|
81
75
|
.review-carousel__role {
|
|
82
76
|
margin: 4px 0 0;
|
|
83
|
-
color: var(--muted-foreground
|
|
77
|
+
color: var(--muted-foreground);
|
|
84
78
|
}
|
|
85
79
|
|
|
86
80
|
.review-carousel__controls {
|
|
@@ -91,21 +85,20 @@
|
|
|
91
85
|
.review-carousel__button {
|
|
92
86
|
width: 40px;
|
|
93
87
|
height: 40px;
|
|
94
|
-
border: 1px solid var(--border
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
background: var(--card, #ffffff);
|
|
88
|
+
border: 1px solid var(--border);
|
|
89
|
+
border-radius: var(--radius-xl);
|
|
90
|
+
color: var(--card-foreground);
|
|
91
|
+
background: var(--card);
|
|
99
92
|
font: inherit;
|
|
100
93
|
cursor: pointer;
|
|
101
94
|
}
|
|
102
95
|
|
|
103
96
|
.review-carousel__button:hover {
|
|
104
|
-
border-color: var(--primary
|
|
105
|
-
color: var(--primary
|
|
97
|
+
border-color: var(--primary);
|
|
98
|
+
color: var(--primary);
|
|
106
99
|
}
|
|
107
100
|
|
|
108
101
|
.review-carousel__button:focus-visible {
|
|
109
|
-
outline: 2px solid var(--ring
|
|
102
|
+
outline: 2px solid var(--ring);
|
|
110
103
|
outline-offset: 2px;
|
|
111
104
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index-JKSTx5pZ.d.mts","names":[],"sources":["../../../portal/core/src/remote-dom-widget-package.ts","../../widget-tooling/src/internal/widget-package-validation.ts","../../widget-tooling/src/dev-routes.ts"],"mappings":";;;cAAa,kCAAA;AAAA,cACA,yBAAA;AAAA,KAED,aAAA;AAAA,KAEA,SAAA,GAAY,aAAA,YAAyB,SAAA,KAAc,UAAA;AAAA,UAE9C,UAAA;EAAA,UACS,GAAA,WAAA,SAAA;AAAA;AAAA,UAGT,oCAAA;EAAA,SACN,IAAA;EAAA,SACA,OAAA;AAAA;AAAA,UAGM,yBAAA;EAAA,SACN,IAAA;EAAA,SACA,WAAA;EAAA,SACA,WAAA;EAAA,SACA,IAAA;EAAA,SACA,QAAA;EAAA,SACA,cAAA,EAAgB,UAAA;EAAA,SAChB,YAAA,EAAc,UAAA;EAAA,SACd,SAAA;EAAA,SACA,SAAA;EAAA,SACA,aAAA;EAAA,SACA,YAAA,WAAuB,oCAAA;AAAA;;;;;;UAQjB,gCAAA;EAAA,SACN,eAAA,SAAwB,kCAAA;EAAA,SACxB,OAAA,SAAgB,yBAAA;EAAA,SAChB,SAAA;EAAA,SACA,MAAA;EAAA,SACA,OAAA;EAAA,SACA,cAAA;EAAA,SACA,OAAA;EAAA,SACA,SAAA;EAAA,SACA,OAAA,WAAkB,yBAAA;AAAA;;;KCpBjB,sBAAA;AAAA,UAKK,wBAAA;EAAA,SACN,IAAA;EAAA,SACA,OAAA;AAAA;AAAA,UAGM,oBAAA;EAAA,SACN,IAAA;EAAA,SACA,WAAA;EAAA,SACA,WAAA;EAAA,SACA,IAAA;EAAA,SACA,QAAA;EAAA,SACA,cAAA,GAAiB,UAAA;EAAA,SACjB,YAAA,GAAe,UAAA;EAAA,SACf,SAAA;EAAA,SACA,aAAA;EAAA,SACA,SAAA;EAAA,SACA,YAAA,YAAwB,wBAAA;AAAA;AAAA,UAGlB,2BAAA;EAAA,SACN,eAAA;EAAA,SACA,KAAA;EAAA,SACA,eAAA;EAAA,SACA,SAAA;EAAA,SACA,WAAA;EAAA,SACA,OAAA;EAAA,SACA,OAAA,WAAkB,oBAAA;EAAA,SAClB,OAAA;AAAA;AAAA,UAGM,wBAAA,SAAiC,yBAAA;EAAA,SACvC,IAAA;AAAA;AAAA,UA4BM,2BAAA;EAAA,SACN,aAAA,EAAe,2BAAA;EAAA,SACf,UAAA;EAAA,SACA,SAAA;EAAA,SACA,KAAA,EAAO,sBAAA;EAAA,SACP,OAAA;EAAA,SACA,OAAA,WAAkB,wBAAA;AAAA;AAAA,KAGjB,gCAAA;AAAA,UAaK,4BAAA;EAAA,SACN,IAAA,EAAM,gCAAA;EAAA,SACN,OAAA;EAAA,SACA,IAAA;AAAA;;;ADjHX;;;;;AACA;;;;AADA,UEgBiB,2BAAA;EFbjB;EAAA,SEeW,eAAA;;WAEA,gBAAA;EFjBC;EAAA,SEmBD,KAAA,GAAQ,sBAAA;EFjBP;;;;;;;EAAA,SEyBD,eAAA;AAAA;AAAA,iBAGK,oBAAA,CACd,OAAA,GAAS,2BAAA,GACR,MAAA;;;AF5BH;;;;;AAIA;;;;;iBEmFgB,2BAAA,CAAA,GAA+B,UAAA;;;;;;;;;;;;;;;;;;;;;;;;AF3D/C;;;;;;;;;;;;;;;;;;;;;;;;;ACXA;;;;;AAKA;;;;;iBC+JgB,gCAAA,CACd,SAAA,UACA,MAAA;;;;;;iBAsCc,iCAAA,CACd,YAAA,EAAc,QAAA,CAAS,MAAA,oBACvB,MAAA,UACA,KAAA,GAAO,sBAAA,YACG,gCAAA"}
|