handoff-app 2.0.0-12 → 2.0.0-13

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.
Files changed (136) hide show
  1. package/{Changelog.md → CHANGELOG.md} +84 -0
  2. package/{License.md → LICENSE.md} +1 -1
  3. package/README.md +760 -801
  4. package/RECIPES.md +975 -0
  5. package/components.json +20 -20
  6. package/config/docs/foundations/colors/guidelines.md +0 -1
  7. package/config/docs/foundations/colors.md +0 -1
  8. package/config/docs/foundations/effects/guidelines.md +0 -1
  9. package/config/docs/foundations/effects/resources.md +0 -1
  10. package/config/docs/foundations/effects.md +0 -1
  11. package/config/docs/foundations/fonts.md +0 -1
  12. package/config/docs/foundations/grid.md +0 -1
  13. package/config/docs/foundations/icon.md +0 -1
  14. package/config/docs/foundations/icons/guidelines.md +0 -1
  15. package/config/docs/foundations/icons.md +0 -1
  16. package/config/docs/foundations/images/guidelines.md +0 -1
  17. package/config/docs/foundations/images/resources.md +0 -1
  18. package/config/docs/foundations/images.md +0 -1
  19. package/config/docs/foundations/logo/resources.md +0 -1
  20. package/config/docs/foundations/logo.md +0 -1
  21. package/config/docs/foundations/typography/guidelines.md +0 -1
  22. package/config/docs/foundations/typography/resources.md +0 -1
  23. package/config/docs/foundations/typography.md +0 -1
  24. package/config/docs/foundations.md +0 -1
  25. package/config/docs/guidelines.md +0 -1
  26. package/config/docs/index.md +0 -1
  27. package/config/docs/system/tokens/foundations/colors.md +0 -4
  28. package/config/docs/system/tokens/foundations/effects.md +0 -4
  29. package/config/docs/system/tokens/foundations/typography.md +0 -2
  30. package/config/docs/system.md +0 -2
  31. package/config/templates/page.md +2 -1
  32. package/config/templates/page.mdx +2 -1
  33. package/dist/ai/define.d.ts +1 -1
  34. package/dist/ai/define.js +1 -1
  35. package/dist/ai/types.d.ts +1 -1
  36. package/dist/ai/types.js +1 -1
  37. package/dist/app-builder/build.js +14 -14
  38. package/dist/app-builder/client-config.js +1 -1
  39. package/dist/config/loader.js +1 -2
  40. package/dist/index.d.ts +2 -2
  41. package/dist/index.js +2 -2
  42. package/dist/nav/index.d.ts +2 -2
  43. package/dist/nav/registry.d.ts +3 -3
  44. package/dist/nav/registry.js +1 -1
  45. package/dist/nav/workspace.d.ts +3 -3
  46. package/dist/nav/workspace.js +1 -1
  47. package/dist/providers.d.ts +10 -0
  48. package/dist/{define.js → providers.js} +4 -4
  49. package/dist/registry/asset-storage/define.d.ts +5 -5
  50. package/dist/registry/asset-storage/define.js +4 -4
  51. package/dist/registry/asset-storage/resolve.d.ts +8 -11
  52. package/dist/registry/asset-storage/resolve.js +12 -13
  53. package/dist/registry/asset-storage/types.d.ts +12 -12
  54. package/dist/registry/asset-storage/types.js +2 -2
  55. package/dist/registry/db/schema.d.ts +2 -2
  56. package/dist/registry/db/schema.js +2 -2
  57. package/dist/registry/email/define.d.ts +1 -1
  58. package/dist/registry/email/define.js +1 -1
  59. package/dist/registry/email/resolve.d.ts +7 -8
  60. package/dist/registry/email/resolve.js +21 -23
  61. package/dist/registry/email/types.d.ts +1 -1
  62. package/dist/registry/email/types.js +1 -1
  63. package/dist/store/registry.d.ts +3 -3
  64. package/dist/store/registry.js +4 -4
  65. package/dist/types/config.d.ts +4 -13
  66. package/docs/cli.md +275 -54
  67. package/package.json +7 -7
  68. package/src/ai/define.ts +1 -1
  69. package/src/ai/types.ts +1 -1
  70. package/src/app/components/Auth/AccountLayout.tsx +1 -1
  71. package/src/app/components/Auth/AuthShell.tsx +10 -4
  72. package/src/app/components/Foundations/ColorGrid.tsx +13 -14
  73. package/src/app/components/Layout/Main.tsx +2 -2
  74. package/src/app/components/McpIntegration/McpConfigDialog.tsx +12 -51
  75. package/src/app/components/Navigation/MobileNav.tsx +0 -2
  76. package/src/app/components/util/index.ts +0 -11
  77. package/src/app/lib/asset-storage.ts +25 -25
  78. package/src/app/lib/auth/email.ts +4 -5
  79. package/src/app/lib/docs-api/page-rendering.ts +1 -1
  80. package/src/app/lib/docs-api/registry.ts +2 -3
  81. package/src/app/lib/docs-api/runtime-config.ts +8 -8
  82. package/src/app/lib/registry-api/asset-transfer.ts +25 -25
  83. package/src/app/lib/server-module.ts +1 -1
  84. package/src/app/lib/utils.ts +7 -0
  85. package/src/app/next.config.mjs +2 -2
  86. package/src/app/pages/[...slug]/index.tsx +2 -1
  87. package/src/app/pages/account/ai.tsx +1 -1
  88. package/src/app/pages/account/index.tsx +1 -1
  89. package/src/app/pages/account/tokens.tsx +1 -1
  90. package/src/app/pages/account/users.tsx +1 -1
  91. package/src/app/pages/api/docs/nav.json.ts +1 -1
  92. package/src/app/pages/api/mcp.ts +6 -0
  93. package/src/app/pages/api/registry/transfer/assets/blobs/[hash].ts +1 -1
  94. package/src/app/pages/cli/device.tsx +1 -1
  95. package/src/app/pages/foundations/colors.tsx +9 -8
  96. package/src/app/pages/foundations/index.tsx +3 -3
  97. package/src/app/pages/index.tsx +2 -2
  98. package/src/app/pages/login.tsx +10 -5
  99. package/src/app/pages/reset-password.tsx +7 -2
  100. package/src/app/pages/system/component/[component]/index.tsx +2 -5
  101. package/src/app/pages/system/index.tsx +11 -11
  102. package/src/app/pages/system/pattern/[pattern]/index.tsx +2 -2
  103. package/src/app/pages/system/tokens/components/[component]/index.tsx +0 -4
  104. package/src/app-builder/build.ts +10 -10
  105. package/src/app-builder/client-config.ts +1 -1
  106. package/src/config/loader.ts +1 -2
  107. package/src/index.ts +6 -6
  108. package/src/nav/index.ts +2 -2
  109. package/src/nav/registry.ts +3 -3
  110. package/src/nav/workspace.ts +3 -3
  111. package/src/{define.ts → providers.ts} +8 -8
  112. package/src/registry/asset-storage/define.ts +6 -6
  113. package/src/registry/asset-storage/resolve.ts +16 -20
  114. package/src/registry/asset-storage/types.ts +12 -12
  115. package/src/registry/db/schema.ts +3 -3
  116. package/src/registry/email/define.ts +1 -1
  117. package/src/registry/email/resolve.ts +23 -23
  118. package/src/registry/email/types.ts +1 -1
  119. package/src/store/registry.ts +7 -7
  120. package/src/types/config.ts +4 -14
  121. package/.editorconfig +0 -19
  122. package/.eslintrc.json +0 -4
  123. package/.prettierignore +0 -0
  124. package/.prettierrc.json +0 -8
  125. package/.vscode/settings.json +0 -17
  126. package/dist/define.d.ts +0 -10
  127. package/dist/registry/email/adapters/resend.d.ts +0 -7
  128. package/dist/registry/email/adapters/resend.js +0 -35
  129. package/src/app/components/old/Header.tsx +0 -89
  130. package/src/app/pages/assets/fonts.tsx +0 -129
  131. package/src/app/pages/assets/index.tsx +0 -91
  132. package/src/app/pages/assets/logos.tsx +0 -75
  133. package/src/registry/email/adapters/resend.ts +0 -22
  134. /package/dist/registry/email/{adapters → providers}/smtp.d.ts +0 -0
  135. /package/dist/registry/email/{adapters → providers}/smtp.js +0 -0
  136. /package/src/registry/email/{adapters → providers}/smtp.ts +0 -0
package/docs/cli.md CHANGED
@@ -1,78 +1,299 @@
1
1
  # Handoff CLI
2
2
 
3
- The Handoff cli allows you to interact with your Figma file and Handoff app
4
- via the command line. It allows you to run handoff in any folder with
5
- no other configuration or setup.
3
+ The `handoff-app` CLI fetches Figma foundations, builds and serves the
4
+ documentation, and transfers content between a workspace and a registry. The
5
+ [README](../README.md) explains the concepts and the configuration. This page
6
+ lists every command and option.
6
7
 
7
- ## Interacting with Figma
8
- The CLI will allow you to fetch all data from Figma, build the documentation
9
- app and run a local dev site. For these use the build, fetch, and serve commands
10
- documented below.
8
+ ## Run the CLI
11
9
 
12
- ## Configuring a handoff project
13
- The CLI will allow you to build the various kinds of configurations that you
14
- will need for interacting with Figma. Handoff has sane configuration defaults
15
- but the various kinds of configurations can be tailored or extended.
10
+ `init` installs `handoff-app` in the project and adds npm scripts for the
11
+ common commands. Use the npm scripts, so that local development and CI use the
12
+ project version:
16
13
 
17
- Handoff has 4 configuration areas -
14
+ ```bash
15
+ npm run build -- --target registry
16
+ ```
18
17
 
19
- `handoff.config.*` - Defines the general handoff configuration. Supported files are `handoff.config.ts`, `handoff.config.js`, `handoff.config.cjs`, and `handoff.config.json` (resolved in that precedence order). Pass `-c, --config <file>` to load one specific file instead, resolved from the working directory (`HANDOFF_WORKING_PATH`, or the directory the command runs in).
20
- `pages` - Markdown files that will create or customize pages in the documentation
21
- app
22
- `exportables` - JSON schemas for each component in your figma file that you
23
- want to pull into handoff.
24
- `integration` - scss mappings and html templates for making it easy to map
25
- handoff tokens to your frontend framework.
18
+ npm forwards the arguments after `--` to the CLI. For a command without an npm
19
+ script, use `npx`:
26
20
 
27
- The CLI exposes two ways to manage the config - `make` and `eject`.
21
+ ```bash
22
+ npx handoff-app validate:components
23
+ ```
28
24
 
29
- __Eject__ commands will take the default configuration and eject them into
30
- the current working directory. If you customize these configurations, then run
31
- handoff commands in that directory, these configs will be executed. `eject:config` now prompts for TypeScript or JavaScript config format.
32
- __Make__ commands will generate a boilerplate configuration in the current
33
- working directory. This is useful for extending handoff for different components
34
- or integrations.
25
+ Show the help of the CLI or of one command:
35
26
 
36
- ## Requirements
37
- Node.js 22 or newer and npm 10 or newer
27
+ ```bash
28
+ npx handoff-app --help
29
+ npx handoff-app publish --help
30
+ ```
38
31
 
39
- ## Install the CLI
32
+ Each command runs against the working directory. The working directory is the
33
+ directory that holds `handoff.config.ts`. It is the current directory, or the
34
+ directory that `HANDOFF_WORKING_PATH` sets.
40
35
 
41
- `npm install -g handoff-app`
36
+ ## Shared options
42
37
 
43
- ## Run the CLI
38
+ Every command except `init` accepts these options:
39
+
40
+ | Option | Description |
41
+ | --- | --- |
42
+ | `-c, --config <file>` | Load this config file, relative to the working directory, instead of `handoff.config.*` |
43
+ | `--profile <name>` | Merge `handoff.config.<name>.*` onto the base config, and use the registry login of this profile. Has priority over `HANDOFF_PROFILE`. |
44
+ | `-d, --debug` | Show debug logs |
45
+ | `-f, --force` | Force the action. The effect depends on the command. |
46
+ | `--help` | Show the help of the command |
47
+
48
+ `handoff-app --version` shows the CLI version.
49
+
50
+ `HANDOFF_LOG_LEVEL` (`debug`, `info`, `warn`, `error`, or `silent`) and
51
+ `HANDOFF_LOG_SCOPES` (`handoff`, `vite`, `next`) control the log output of
52
+ every command. The README lists all
53
+ [environment variables](../README.md#environment-variables).
54
+
55
+ ## Project
56
+
57
+ ### init
58
+
59
+ ```bash
60
+ npx handoff-app init
61
+ ```
62
+
63
+ Creates a project with an interactive wizard. The wizard asks for the project
64
+ name, sample content, JavaScript or TypeScript, an optional Vercel setup, and
65
+ optional Figma credentials. It installs `handoff-app` and adds these npm
66
+ scripts: `start`, `dev`, `fetch`, `build`, `login`, `logout`, `publish`,
67
+ `checkout`, and `db:migrate`.
68
+
69
+ ### fetch
70
+
71
+ ```bash
72
+ handoff-app fetch
73
+ ```
74
+
75
+ Fetches tokens and assets from the Figma file and writes them to `exported/`.
76
+ It reads `HANDOFF_FIGMA_PROJECT_ID` and `HANDOFF_DEV_ACCESS_TOKEN`. If a value
77
+ is empty, `fetch` asks for it in the terminal. Without a terminal, `fetch`
78
+ stops with an error that names the missing variable.
79
+
80
+ ### start
81
+
82
+ ```bash
83
+ handoff-app start
84
+ ```
85
+
86
+ Starts the workspace documentation server and the Handoff file watchers. While
87
+ the command runs, Handoff rebuilds changed files and updates the
88
+ documentation.
89
+
90
+ ### dev
91
+
92
+ ```bash
93
+ handoff-app dev
94
+ ```
95
+
96
+ Starts the workspace documentation server without the Handoff file watchers.
97
+
98
+ ### build
99
+
100
+ ```bash
101
+ handoff-app build [--target static|registry] [--package standalone|vercel] [--skip-components]
102
+ ```
103
+
104
+ Builds the documentation.
105
+
106
+ | Option | Description |
107
+ | --- | --- |
108
+ | `--target static` | Default. Builds a static site in `out/static`. |
109
+ | `--target registry` | Builds a standalone registry server in `out/registry`. The application is always in registry mode. |
110
+ | `--package vercel` | Writes a Vercel Build Output bundle to `.vercel/output` for the selected target |
111
+ | `--package standalone` | Writes the default output of the target |
112
+ | `--skip-components` | Builds the application without a component build first |
113
+ | `-f, --force` | Builds every component again, and ignores the build cache |
114
+
115
+ `--package` never changes the target.
116
+
117
+ ### build:app
118
+
119
+ ```bash
120
+ handoff-app build:app [--skip-components]
121
+ ```
122
+
123
+ Builds the static site. It is the same as `build --target static`.
124
+
125
+ ### build:components
126
+
127
+ ```bash
128
+ handoff-app build:components [component]
129
+ ```
130
+
131
+ Builds the catalog items without the documentation application. A name builds
132
+ only that item.
133
+
134
+ ### validate:components
135
+
136
+ ```bash
137
+ handoff-app validate:components [--skip-build]
138
+ ```
139
+
140
+ Validates the catalog items. `--skip-build` validates the existing build
141
+ output without a build first.
142
+
143
+ ## Content
144
+
145
+ ### make:component
146
+
147
+ ```bash
148
+ handoff-app make:component <name>
149
+ ```
150
+
151
+ Creates a Handlebars catalog item: a template, a CommonJS declaration, and,
152
+ if you select them, a JavaScript file and a Sass file. The item goes into the
153
+ first directory in `catalog.include`, or into `components/`. If that directory
154
+ is not registered, Handoff adds it to `catalog.include`. The name can contain
155
+ letters, numbers, hyphens, and underscores. `--force` overwrites an existing
156
+ template.
157
+
158
+ ### make:page
159
+
160
+ ```bash
161
+ handoff-app make:page <name> [parent]
162
+ ```
163
+
164
+ Creates a Markdown page in `pages/`, or in `pages/<parent>/`. If a default page
165
+ has the same name, the new page starts with its content. `--force` overwrites
166
+ an existing page.
167
+
168
+ ### scaffold
169
+
170
+ ```bash
171
+ handoff-app scaffold
172
+ ```
173
+
174
+ Creates catalog item stubs for the components in the fetched Figma data. The
175
+ command asks which components to create and how.
176
+
177
+ ### eject:config
178
+
179
+ ```bash
180
+ handoff-app eject:config
181
+ ```
182
+
183
+ Writes the default config to the working directory. The command asks for
184
+ TypeScript or JavaScript. `--force` overwrites an existing config file.
185
+
186
+ ### eject:pages
187
+
188
+ ```bash
189
+ handoff-app eject:pages
190
+ ```
191
+
192
+ Copies the default documentation pages to `pages/`, so that you can change
193
+ them. `--force` adds the default pages that you did not change.
194
+
195
+ ### eject:theme
196
+
197
+ ```bash
198
+ handoff-app eject:theme
199
+ ```
200
+
201
+ Copies the Sass file of the selected theme to `theme/default.scss`, so that
202
+ you can change it.
203
+
204
+ ## Registry
205
+
206
+ ### db:migrate
207
+
208
+ ```bash
209
+ DATABASE_URL="postgresql://…" handoff-app db:migrate
210
+ ```
211
+
212
+ Applies the registry database migrations. The command reads the project config
213
+ and the database variables, and uses the same database driver as the registry
214
+ build. Run it before the first deployment and before each upgrade. The build,
215
+ the server start, and the installer never run migrations.
216
+
217
+ ### login
218
+
219
+ ```bash
220
+ handoff-app login [--url <registry-url>] [--profile <name>] [--no-browser]
221
+ ```
222
+
223
+ Authorizes the CLI through the device flow of the registry. The CLI saves the
224
+ token in `.handoff/cli-auth.json`, under the selected profile.
225
+
226
+ | Option | Description |
227
+ | --- | --- |
228
+ | `--url <registry-url>` | Registry URL, with its base path when the registry has one. Without it, the CLI uses the configured registry URL. |
229
+ | `--profile <name>` | Saves the login under this profile. The profile needs no config file. |
230
+ | `--no-browser` | Shows the approval URL, but does not open the browser. `HANDOFF_LOGIN_NO_BROWSER=true` does the same. |
231
+
232
+ ### logout
233
+
234
+ ```bash
235
+ handoff-app logout [--profile <name>] [--url <registry-url>] [--all]
236
+ ```
237
+
238
+ Revokes the saved token when possible. Then it removes the saved login of the
239
+ selected profile.
240
+
241
+ | Option | Description |
242
+ | --- | --- |
243
+ | `--url <registry-url>` | Revokes the login only if it belongs to this exact registry URL |
244
+ | `--all` | Revokes and removes the logins of every profile |
245
+
246
+ ### publish
247
+
248
+ ```bash
249
+ handoff-app publish <catalog|pages|tokens|assets|all> [id...] [--dry-run] [--no-build]
250
+ ```
251
+
252
+ Builds the content and publishes it to the connected registry. `all` publishes
253
+ every kind in dependency order. One or more ids limit the publish to those
254
+ entities: catalog item ids, page ids, token set ids, or asset collections.
255
+ `tokens` and `assets` run the Figma pipeline first.
44
256
 
45
- `handoff-app --help`
257
+ | Option | Description |
258
+ | --- | --- |
259
+ | `--dry-run` | Lists the content that a real publish uploads. Does not connect to a registry. The build still runs. |
260
+ | `--no-build` | Publishes the existing build output without a build first |
46
261
 
47
- ## Commands and Flags
262
+ ### checkout
48
263
 
49
- Usage: handoff-app <cmd> <opts>
264
+ ```bash
265
+ handoff-app checkout <catalog|pages|tokens|assets|all> [id...] [--dry-run]
266
+ ```
50
267
 
51
- Commands:
52
- fetch [opts] - Fetches the design tokens from the design system
268
+ Writes content from the connected registry into the workspace. Checkout writes
269
+ declarations in the format that `runtime.workspace.declarationFormat` sets.
53
270
 
54
- build [--target static|registry] [opts] - Builds the design system for the target (default: static export)
55
- build:app [opts] - Builds the design system static application (alias of `build --target static`)
271
+ | Option | Description |
272
+ | --- | --- |
273
+ | `--dry-run` | Lists the files that a real checkout creates or overwrites. Writes nothing. |
56
274
 
57
- start [opts] - Starts the design system in development mode
275
+ ### Registry connection
58
276
 
59
- db:migrate [opts] - Runs registry database migrations (Drizzle / PostgreSQL). Reads the project config and database env vars, applies the package-owned migration set, and runs independently of `build`.
277
+ `publish` and `checkout` find the registry URL and the token in this order:
60
278
 
61
- login [--profile <name>] --url <registry-url> - Authorizes this workspace in the browser and stores a revocable token in `.handoff/cli-auth.json`, under the selected profile.
279
+ 1. `HANDOFF_REGISTRY_URL` and `HANDOFF_REGISTRY_ACCESS_TOKEN`, or
280
+ `runtime.registryConnection` in the config.
281
+ 2. The saved login of the selected profile, if it belongs to the same registry
282
+ URL. A profile without a login uses the default login.
283
+ 3. `HANDOFF_SYNC_SECRET`, when no token and no login is available.
62
284
 
63
- logout [--profile <name>] [--url <registry-url>] [--all] - Revokes the saved token when possible, then removes the local credential of the selected profile, or of every profile with `--all`.
285
+ ## Deprecated commands
64
286
 
65
- make
66
- make:component <name> [opts] - Creates a new catalog item with a declaration
67
- make:page <component> <state> [opts] - Creates a new page
287
+ These commands still work. Each shows a deprecation warning.
68
288
 
69
- eject - Ejects the default entire configuration to the current directory
70
- eject:config [opts] - Ejects the default configuration to the current directory
71
- eject:integration [opts] - Ejects the default integration to the current directory
72
- eject:pages [opts] - Ejects the default pages to the current directory
289
+ | Command | Replacement |
290
+ | --- | --- |
291
+ | `push [type] [id...]` | `publish` |
292
+ | `push:all` | `publish all` |
293
+ | `push:tokens [setId...]` | `publish tokens` |
294
+ | `push:assets [collection]` | `publish assets` |
295
+ | `pull [type] [id...]` | `checkout` |
73
296
 
74
- Options:
75
- -c, --config [file] Define the path to the config file
76
- -d, --debug Show debug logs
77
- -h, --help Show this help message
78
- -v, --version Show the handoff version number
297
+ The `push` and `pull` commands also accept `components` and `patterns` as the
298
+ type. They accept the options for features that Handoff 2 does not have, and
299
+ show a warning for each one.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "handoff-app",
3
- "version": "2.0.0-12",
3
+ "version": "2.0.0-13",
4
4
  "description": "Automated documentation toolchain for building client side documentation from figma",
5
5
  "author ": {
6
6
  "name": "Convertiv",
@@ -40,9 +40,9 @@
40
40
  "types": "./dist/pattern.d.ts",
41
41
  "default": "./dist/pattern.js"
42
42
  },
43
- "./define": {
44
- "types": "./dist/define.d.ts",
45
- "default": "./dist/define.js"
43
+ "./providers": {
44
+ "types": "./dist/providers.d.ts",
45
+ "default": "./dist/providers.js"
46
46
  },
47
47
  "./package.json": "./package.json",
48
48
  "./*": "./*"
@@ -58,8 +58,8 @@
58
58
  "pattern": [
59
59
  "./dist/pattern.d.ts"
60
60
  ],
61
- "define": [
62
- "./dist/define.d.ts"
61
+ "providers": [
62
+ "./dist/providers.d.ts"
63
63
  ]
64
64
  }
65
65
  },
@@ -89,7 +89,7 @@
89
89
  "plugins": [
90
90
  "prettier-plugin-tailwindcss"
91
91
  ],
92
- "tailwindConfig": "./tailwind.config.ts"
92
+ "tailwindStylesheet": "./src/app/css/index.css"
93
93
  },
94
94
  "dependencies": {
95
95
  "@ai-sdk/openai-compatible": "^3.0.53",
package/src/ai/define.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Typed identity helper for custom AI providers (mirrors `defineAssetStorage`): a consumer
2
+ * Typed identity helper for custom AI providers (mirrors `defineStorageProvider`): a consumer
3
3
  * default-exports its result from a **server-only** module and points a connection's `module` at
4
4
  * it. It must be its own module (not an inline `defineConfig` function) so the build can trace it
5
5
  * into the deployed registry. The default export may be the provider object, or a factory
package/src/ai/types.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  * Server-only AI provider contract for connections that are not OpenAI-compatible.
3
3
  *
4
4
  * Every built-in connection speaks the OpenAI-compatible `/chat/completions` API and needs no
5
- * adapter — Ollama, LiteLLM, OpenRouter, vLLM, LM Studio and Azure OpenAI all serve it, and each of
5
+ * custom module — Ollama, LiteLLM, OpenRouter, vLLM, LM Studio and Azure OpenAI all serve it, and each of
6
6
  * them fronts Anthropic, Google and xAI models. A provider that fits nothing else names a
7
7
  * {@link defineAiProvider} module instead of a base URL.
8
8
  */
@@ -41,7 +41,7 @@ interface AccountLayoutProps {
41
41
 
42
42
  export function AccountLayout({ children, config, title, description }: AccountLayoutProps) {
43
43
  if (process.env.HANDOFF_RUNTIME_MODE !== 'registry') {
44
- return <AuthShell title={title}>{null}</AuthShell>;
44
+ return <AuthShell config={config} title={title}>{null}</AuthShell>;
45
45
  }
46
46
 
47
47
  return (
@@ -3,6 +3,7 @@ import Head from 'next/head';
3
3
  import Link from 'next/link';
4
4
  import type { ClientConfig } from '@handoff/types/config';
5
5
  import type { ReactNode } from 'react';
6
+ import { pageTitle } from '../../lib/utils';
6
7
  import { ConfigContextProvider } from '../context/ConfigContext';
7
8
  import { Header } from '../Layout/Header';
8
9
  import { Button } from '../ui/button';
@@ -21,9 +22,17 @@ interface AuthShellProps {
21
22
  }
22
23
 
23
24
  export function AuthShell({ children, config, title, description, wide = false, hideNav = false, centered = false }: AuthShellProps) {
25
+ const head = (
26
+ <Head>
27
+ <title>{pageTitle({ title }, config?.app?.title)}</title>
28
+ {description ? <meta name="description" content={description} /> : null}
29
+ </Head>
30
+ );
31
+
24
32
  if (!isRegistryRuntime) {
25
33
  return (
26
34
  <main className="flex min-h-screen items-center justify-center px-6">
35
+ {head}
27
36
  <div className="max-w-md text-center">
28
37
  <h1 className="text-2xl font-semibold">Registry only</h1>
29
38
  <p className="mt-2 text-sm text-muted-foreground">Account management is available only in a deployed registry.</p>
@@ -40,10 +49,7 @@ export function AuthShell({ children, config, title, description, wide = false,
40
49
 
41
50
  return (
42
51
  <ThemeProvider attribute="class" defaultTheme="system" enableSystem disableTransitionOnChange>
43
- <Head>
44
- <title>{title} · Handoff Registry</title>
45
- {description ? <meta name="description" content={description} /> : null}
46
- </Head>
52
+ {head}
47
53
  {centered ? (
48
54
  <main className="flex min-h-[calc(100vh-8rem)] items-center justify-center bg-background px-4 py-12">
49
55
  <div className="w-full max-w-md">{children}</div>
@@ -16,7 +16,7 @@ import {
16
16
  import { Separator } from '../ui/separator';
17
17
  import { Sheet, SheetClose, SheetContent, SheetHeader } from '../ui/sheet';
18
18
  import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../ui/tooltip';
19
- import { hexToRgb } from '../util/colors';
19
+ import { hexToRgb, hexToRgbaCss } from '../util/colors';
20
20
  import ColorContrast from './ColorContrast';
21
21
  import ColorInfo from './ColorInfo';
22
22
  import ColorSpaces from './ColorSpaces';
@@ -25,7 +25,6 @@ import ColorTailwind from './ColorTailwind';
25
25
  type ColorGridProps = {
26
26
  title: string;
27
27
  group: string;
28
- description: string;
29
28
  colors: CoreTypes.IColorObject[];
30
29
  };
31
30
 
@@ -33,7 +32,7 @@ const LargeColorGrid: React.FC<{ colors: CoreTypes.IColorObject[]; setOpen: (col
33
32
  <div className="@container">
34
33
  <div className="mb-6 grid grid-cols-1 gap-6 @md:grid-cols-2">
35
34
  {colors.map((color) => (
36
- <a href="#" className="flex flex-col items-start" key={color.group + '-' + color.name}>
35
+ <div className="flex flex-col items-start" key={color.group + '-' + color.name}>
37
36
  <div
38
37
  className="group relative mb-2 block h-32 w-full rounded-lg"
39
38
  style={{ background: color.value ?? '', backgroundBlendMode: color.blend ?? '' }}
@@ -42,7 +41,7 @@ const LargeColorGrid: React.FC<{ colors: CoreTypes.IColorObject[]; setOpen: (col
42
41
  </div>
43
42
  <p className="mb-1 text-sm font-medium line-clamp-1" title={color.name}>{color.name}</p>
44
43
  <small className="font-mono text-xs font-light text-gray-400 line-clamp-1" title={color.value}>{color.value}</small>
45
- </a>
44
+ </div>
46
45
  ))}
47
46
  </div>
48
47
  </div>
@@ -52,10 +51,11 @@ const SmallColorGrid: React.FC<{ colors: CoreTypes.IColorObject[]; setOpen: (col
52
51
  // group colors by subgroup
53
52
  const groupedColors = colors.reduce(
54
53
  (acc, color) => {
55
- if (!acc[color.subgroup]) {
56
- acc[color.subgroup] = [];
54
+ const subgroup = color.subgroup ?? '';
55
+ if (!acc[subgroup]) {
56
+ acc[subgroup] = [];
57
57
  }
58
- acc[color.subgroup].push(color);
58
+ acc[subgroup].push(color);
59
59
  return acc;
60
60
  },
61
61
  {} as Record<string, CoreTypes.IColorObject[]>
@@ -64,11 +64,11 @@ const SmallColorGrid: React.FC<{ colors: CoreTypes.IColorObject[]; setOpen: (col
64
64
  <>
65
65
  {Object.entries(groupedColors).map(([subgroup]) => (
66
66
  <div className="mb-5" key={`group-${subgroup}`}>
67
- <HeadersType.H4 key={subgroup}>{startCase(subgroup.replaceAll('-', ' '))}</HeadersType.H4>
67
+ {subgroup && <HeadersType.H4>{startCase(subgroup.replaceAll('-', ' '))}</HeadersType.H4>}
68
68
  <div className="grid grid-cols-[repeat(auto-fit,minmax(60px,60px))] gap-6">
69
69
  {groupedColors[subgroup].map((color) => (
70
70
  <React.Fragment key={`group-${subgroup}-${color.name}`}>
71
- <a href="" className="flex flex-col">
71
+ <div className="flex flex-col">
72
72
  <div
73
73
  className="group relative mb-2 block h-14 rounded-lg"
74
74
  style={{ background: color.value ?? '', backgroundBlendMode: color.blend ?? '' }}
@@ -77,7 +77,7 @@ const SmallColorGrid: React.FC<{ colors: CoreTypes.IColorObject[]; setOpen: (col
77
77
  </div>
78
78
  <p className="mb-1 text-xs font-medium line-clamp-1" title={color.name}>{color.name}</p>
79
79
  <small className="font-mono text-xs font-light text-gray-400 line-clamp-1" title={color.value}>{color.value}</small>
80
- </a>
80
+ </div>
81
81
  </React.Fragment>
82
82
  ))}
83
83
  </div>
@@ -87,7 +87,7 @@ const SmallColorGrid: React.FC<{ colors: CoreTypes.IColorObject[]; setOpen: (col
87
87
  );
88
88
  };
89
89
 
90
- const ColorGrid: React.FC<ColorGridProps> = ({ title, description, colors, group }) => {
90
+ const ColorGrid: React.FC<ColorGridProps> = ({ title, colors, group }) => {
91
91
  const [open, setOpen] = React.useState(false);
92
92
  const [selectedColor, setSelectedColor] = React.useState(null);
93
93
  const openSheet = (color) => {
@@ -96,8 +96,7 @@ const ColorGrid: React.FC<ColorGridProps> = ({ title, description, colors, group
96
96
  };
97
97
  return (
98
98
  <div id={`${group}-colors`} className="scroll-mt-24 scroll-smooth pb-10">
99
- <h3 className="text-lg font-medium">{title}</h3>
100
- <p className="mb-8">{description}</p>
99
+ <h3 className="mb-8 text-lg font-medium">{title}</h3>
101
100
  {colors.length < 5 ? <LargeColorGrid colors={colors} setOpen={openSheet} /> : <SmallColorGrid colors={colors} setOpen={openSheet} />}
102
101
  <ColorSheet color={selectedColor} open={open} setOpen={setOpen} />
103
102
  </div>
@@ -122,7 +121,7 @@ const ColorDropdown: React.FC<{ color: CoreTypes.IColorObject; openSheet: (color
122
121
  <Copy className="text-gray-400" /> HEX
123
122
  <DropdownMenuShortcut className="max-w-[100px] truncate">{color.value}</DropdownMenuShortcut>
124
123
  </DropdownMenuItem>
125
- <DropdownMenuItem onClick={() => navigator.clipboard.writeText('rgba(0, 49, 82, 1)')}>
124
+ <DropdownMenuItem onClick={() => navigator.clipboard.writeText(hexToRgbaCss(color.value) ?? color.value)}>
126
125
  <Copy className="text-gray-400" /> RGBA
127
126
  <DropdownMenuShortcut className="max-w-[100px] truncate">{hexToRgb(color.value)}</DropdownMenuShortcut>
128
127
  </DropdownMenuItem>
@@ -5,7 +5,7 @@ import { Header } from '../../components/Layout/Header';
5
5
  import { ThemeProvider } from '../../components/util/theme-provider';
6
6
  import SideNav from '../Navigation/SideNav';
7
7
  import { ConfigContextProvider } from '../context/ConfigContext';
8
- import { hasRenderableNav } from '../../lib/utils';
8
+ import { hasRenderableNav, pageTitle } from '../../lib/utils';
9
9
  import { SidebarInset, SidebarProvider } from '../ui/sidebar';
10
10
  import { SectionLink } from '../util';
11
11
 
@@ -34,7 +34,7 @@ export default function Layout<LayoutComponentProps>({ children, config, menu, m
34
34
  <ConfigContextProvider defaultConfig={config} defaultMenu={menu}>
35
35
  <ThemeProvider attribute="class" defaultTheme="system" enableSystem disableTransitionOnChange>
36
36
  <Head>
37
- <title>{metadata.metaTitle}</title>
37
+ <title>{pageTitle(metadata, config?.app?.title)}</title>
38
38
  <meta name="description" content={metadata.metaDescription} />
39
39
  </Head>
40
40
  <div className="absolute left-[-200px] top-[-200px] z-[-1] h-[400px] w-[600px] bg-[#111111] opacity-[0.05] blur-[350px]">