handoff-app 2.0.0-11 → 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 (141) 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 +16 -16
  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 +8 -9
  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/mcp/components.ts +33 -29
  83. package/src/app/lib/mcp/pages.ts +26 -19
  84. package/src/app/lib/mcp/server.ts +33 -9
  85. package/src/app/lib/mcp/tokens.ts +22 -16
  86. package/src/app/lib/mcp/tool.ts +19 -0
  87. package/src/app/lib/registry-api/asset-transfer.ts +25 -25
  88. package/src/app/lib/server-module.ts +1 -1
  89. package/src/app/lib/utils.ts +7 -0
  90. package/src/app/next.config.mjs +2 -2
  91. package/src/app/pages/[...slug]/index.tsx +2 -1
  92. package/src/app/pages/account/ai.tsx +1 -1
  93. package/src/app/pages/account/index.tsx +1 -1
  94. package/src/app/pages/account/tokens.tsx +1 -1
  95. package/src/app/pages/account/users.tsx +1 -1
  96. package/src/app/pages/api/docs/nav.json.ts +1 -1
  97. package/src/app/pages/api/mcp.ts +6 -0
  98. package/src/app/pages/api/registry/transfer/assets/blobs/[hash].ts +1 -1
  99. package/src/app/pages/cli/device.tsx +1 -1
  100. package/src/app/pages/foundations/colors.tsx +9 -8
  101. package/src/app/pages/foundations/index.tsx +3 -3
  102. package/src/app/pages/index.tsx +2 -2
  103. package/src/app/pages/login.tsx +10 -5
  104. package/src/app/pages/reset-password.tsx +7 -2
  105. package/src/app/pages/system/component/[component]/index.tsx +2 -5
  106. package/src/app/pages/system/index.tsx +11 -11
  107. package/src/app/pages/system/pattern/[pattern]/index.tsx +2 -2
  108. package/src/app/pages/system/tokens/components/[component]/index.tsx +0 -4
  109. package/src/app-builder/build.ts +12 -12
  110. package/src/app-builder/client-config.ts +1 -1
  111. package/src/config/loader.ts +1 -2
  112. package/src/index.ts +6 -6
  113. package/src/nav/index.ts +2 -2
  114. package/src/nav/registry.ts +3 -3
  115. package/src/nav/workspace.ts +3 -3
  116. package/src/{define.ts → providers.ts} +8 -8
  117. package/src/registry/asset-storage/define.ts +6 -6
  118. package/src/registry/asset-storage/resolve.ts +16 -20
  119. package/src/registry/asset-storage/types.ts +12 -12
  120. package/src/registry/db/schema.ts +3 -3
  121. package/src/registry/email/define.ts +1 -1
  122. package/src/registry/email/resolve.ts +23 -23
  123. package/src/registry/email/types.ts +1 -1
  124. package/src/store/registry.ts +7 -7
  125. package/src/types/config.ts +4 -14
  126. package/.editorconfig +0 -19
  127. package/.eslintrc.json +0 -4
  128. package/.prettierignore +0 -0
  129. package/.prettierrc.json +0 -8
  130. package/.vscode/settings.json +0 -17
  131. package/dist/define.d.ts +0 -10
  132. package/dist/registry/email/adapters/resend.d.ts +0 -7
  133. package/dist/registry/email/adapters/resend.js +0 -35
  134. package/src/app/components/old/Header.tsx +0 -89
  135. package/src/app/pages/assets/fonts.tsx +0 -129
  136. package/src/app/pages/assets/index.tsx +0 -91
  137. package/src/app/pages/assets/logos.tsx +0 -75
  138. package/src/registry/email/adapters/resend.ts +0 -22
  139. /package/dist/registry/email/{adapters → providers}/smtp.d.ts +0 -0
  140. /package/dist/registry/email/{adapters → providers}/smtp.js +0 -0
  141. /package/src/registry/email/{adapters → providers}/smtp.ts +0 -0
@@ -129,16 +129,6 @@ interface HandoffEmailBase {
129
129
  from?: string;
130
130
  }
131
131
 
132
- /** Send through the Resend HTTP API. */
133
- export interface HandoffResendEmail extends HandoffEmailBase {
134
- /** @default "resend" */
135
- provider?: 'resend';
136
- options?: {
137
- /** Environment reference to the Resend API key. @default fromEnv('RESEND_API_KEY') */
138
- apiKey?: RuntimeEnvReference<string>;
139
- };
140
- }
141
-
142
132
  /** Send through an SMTP server, such as Amazon SES, SendGrid, Postmark, Mailgun or Microsoft 365. */
143
133
  export interface HandoffSmtpEmail extends HandoffEmailBase {
144
134
  provider: 'smtp';
@@ -170,7 +160,7 @@ export interface HandoffCustomEmail extends HandoffEmailBase {
170
160
  }
171
161
 
172
162
  /** Registry email settings. `provider` selects the shape of `options`. */
173
- export type HandoffEmailConfig = HandoffResendEmail | HandoffSmtpEmail | HandoffCustomEmail;
163
+ export type HandoffEmailConfig = HandoffSmtpEmail | HandoffCustomEmail;
174
164
 
175
165
  /**
176
166
  * One AI connection the deployment declares. Connections are the whole surface: a reader can add a
@@ -277,13 +267,13 @@ export interface HandoffRuntimeConfig {
277
267
  * `custom` module for object storage.
278
268
  * @default "database"
279
269
  */
280
- adapter?: 'database' | 'custom';
281
- /** For `adapter: "custom"` - server-only module path default-exporting a `defineAssetStorage` adapter. */
270
+ provider?: 'database' | 'custom';
271
+ /** For `provider: "custom"` - server-only module path default-exporting a `defineStorageProvider()` result. */
282
272
  module?: string;
283
273
  /** Max bytes kept inline in the database `bytea` column (larger uploads are rejected). @default 4194304 */
284
274
  maxInlineBytes?: number;
285
275
  /**
286
- * Options passed to the adapter factory. Literals are baked. Environment references are resolved
276
+ * Options passed to the provider factory. Literals are baked. Environment references are resolved
287
277
  * at request time, so secrets must be environment references.
288
278
  */
289
279
  options?: Record<string, unknown>;
package/.editorconfig DELETED
@@ -1,19 +0,0 @@
1
- # EditorConfig is awesome: https://EditorConfig.org
2
-
3
- # top-most EditorConfig file
4
- root = true
5
-
6
- [*.{css,scss,sass}]
7
- charset = utf-8
8
- indent_style = tab
9
- indent_size = unset
10
- end_of_line = lf
11
- trim_trailing_whitespace = true
12
- insert_final_newline = true
13
-
14
- [*.{js,ts,jsx,tsx}]
15
- charset = utf-8
16
- indent_style = space
17
- end_of_line = lf
18
- trim_trailing_whitespace = true
19
- insert_final_newline = true
package/.eslintrc.json DELETED
@@ -1,4 +0,0 @@
1
- {
2
- "extends": ["next", "prettier"],
3
- "ignorePatterns": ["./src/app/out/**/*", "**/*.js"]
4
- }
package/.prettierignore DELETED
File without changes
package/.prettierrc.json DELETED
@@ -1,8 +0,0 @@
1
- {
2
- "trailingComma": "es5",
3
- "semi": true,
4
- "tabWidth": 2,
5
- "singleQuote": true,
6
- "jsxSingleQuote": true,
7
- "plugins": ["prettier-plugin-tailwindcss"]
8
- }
@@ -1,17 +0,0 @@
1
- {
2
- "editor.defaultFormatter": "esbenp.prettier-vscode",
3
- "[javascript]": { "editor.defaultFormatter": "esbenp.prettier-vscode" },
4
- "editor.formatOnSave": true,
5
- "editor.codeActionsOnSave": {
6
- "source.organizeImports": "explicit"
7
- },
8
- "[xml]": {
9
- "editor.defaultFormatter": "redhat.vscode-xml"
10
- },
11
- "[typescriptreact]": {
12
- "editor.defaultFormatter": "vscode.typescript-language-features"
13
- },
14
- "[markdown]": {
15
- "editor.defaultFormatter": "unifiedjs.vscode-remark"
16
- }
17
- }
package/dist/define.d.ts DELETED
@@ -1,10 +0,0 @@
1
- /**
2
- * Define helpers for custom server modules. This entry point has no runtime dependencies, so a module
3
- * that imports it does not pull the CLI into the registry bundle.
4
- */
5
- export { defineAiProvider } from './ai/define';
6
- export type { AiProvider, AiProviderContext, AiProviderFactory } from './ai/types';
7
- export { defineAssetStorage } from './registry/asset-storage/define';
8
- export type { AssetStorage, AssetStorageContext, AssetStorageFactory, AssetStorageInput, AssetStorageReadResult, AssetUpload, AssetUploadInput, } from './registry/asset-storage/types';
9
- export { defineEmailProvider } from './registry/email/define';
10
- export type { EmailMessage, EmailProvider, EmailProviderContext, EmailProviderFactory } from './registry/email/types';
@@ -1,7 +0,0 @@
1
- /**
2
- * Built-in Resend provider, selected with `runtime.registry.email.provider: "resend"` (the default).
3
- * A plain `fetch` to the Resend HTTP API, so it adds no dependency.
4
- */
5
- import type { EmailProvider } from '../types';
6
- /** Build a Resend provider from options whose references are already resolved. */
7
- export declare const createResendProvider: (options: Record<string, unknown>) => EmailProvider;
@@ -1,35 +0,0 @@
1
- "use strict";
2
- /**
3
- * Built-in Resend provider, selected with `runtime.registry.email.provider: "resend"` (the default).
4
- * A plain `fetch` to the Resend HTTP API, so it adds no dependency.
5
- */
6
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
7
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
8
- return new (P || (P = Promise))(function (resolve, reject) {
9
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
10
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
11
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
12
- step((generator = generator.apply(thisArg, _arguments || [])).next());
13
- });
14
- };
15
- Object.defineProperty(exports, "__esModule", { value: true });
16
- exports.createResendProvider = void 0;
17
- /** Build a Resend provider from options whose references are already resolved. */
18
- const createResendProvider = (options) => ({
19
- send(_a) {
20
- return __awaiter(this, arguments, void 0, function* ({ from, to, subject, html, text }) {
21
- const apiKey = typeof options.apiKey === 'string' ? options.apiKey : '';
22
- if (!apiKey)
23
- throw new Error('The Resend API key is not set.');
24
- const response = yield fetch('https://api.resend.com/emails', {
25
- method: 'POST',
26
- headers: { Authorization: `Bearer ${apiKey}`, 'Content-Type': 'application/json' },
27
- body: JSON.stringify({ from, to: [to], subject, html, text }),
28
- });
29
- if (!response.ok) {
30
- throw new Error(`Resend rejected the email (HTTP ${response.status}): ${yield response.text()}`);
31
- }
32
- });
33
- },
34
- });
35
- exports.createResendProvider = createResendProvider;
@@ -1,89 +0,0 @@
1
- import { ClientConfig } from '@handoff/types/config';
2
- import Link from 'next/link';
3
- import React from 'react';
4
- import NavLink from '../NavLink';
5
- import { useNav } from '../context/NavProvider';
6
-
7
- interface HeaderProps {
8
- config: ClientConfig;
9
- }
10
-
11
- function Header({ config }: HeaderProps) {
12
- const { menu } = useNav();
13
- const [mobile, setMobile] = React.useState('');
14
- const toggle = () => {
15
- if (mobile === 'is-active') {
16
- setMobile('');
17
- } else {
18
- setMobile('is-active');
19
- }
20
- };
21
-
22
- return (
23
- <>
24
- <header id="site-header" className="c-site-header">
25
- <div className="o-container-fluid">
26
- <div className="c-site-header__wrapper">
27
- <div>
28
- <div className="c-site-menu">
29
- <button className={`c-hamburger-icon c-hamburger-icon--slider ${mobile}`} onClick={toggle}>
30
- <div className="c-hamburger-icon__box">
31
- <div className="c-hamburger-icon__inner"></div>
32
- </div>
33
- </button>
34
- </div>
35
- <div className="c-site-title">
36
- <Link href="/" className="c-site-logo c-site-logo--basic" title="" rel="home" aria-label="logo">
37
- {/* eslint-disable-next-line @next/next/no-img-element */}
38
- <img src={`${process.env.HANDOFF_APP_BASE_PATH ?? ''}/logo.svg`} alt={config?.app?.title} />
39
- </Link>
40
- </div>
41
-
42
- <nav className="c-site-nav">
43
- <ul>
44
- {menu.map((item) => (
45
- <li key={item.path}>
46
- <NavLink href={`${item.path}`}>{item.title}</NavLink>
47
- </li>
48
- ))}
49
- </ul>
50
- </nav>
51
- </div>
52
- <div>
53
- {
54
- // TODO: Reimpliment cmd k search
55
- false && <small>hit cmd+k to search</small>
56
- }
57
- </div>
58
- </div>
59
- </div>
60
- </header>
61
- <div className={`c-offcanvas c-offcanvas--menu ${mobile}`}>
62
- <div className="c-offcanvas__inner">
63
- <nav className="c-mobilenav">
64
- <ul className="c-mobilenav__menu">
65
- {menu.map((item) => (
66
- <li key={`mobile-${item.path}`}>
67
- <NavLink href={`${item.path}`}>{item.title}</NavLink>
68
- {item.subSections.length > 0 && (
69
- <ul className="c-mobilenav__submenu">
70
- {item.subSections
71
- .filter((sub) => sub.path)
72
- .map((sub) => (
73
- <li key={sub.path}>
74
- <NavLink href={`${sub.path}`}>{sub.title}</NavLink>
75
- </li>
76
- ))}
77
- </ul>
78
- )}
79
- </li>
80
- ))}
81
- </ul>
82
- </nav>
83
- </div>
84
- </div>
85
- </>
86
- );
87
- }
88
-
89
- export default Header;
@@ -1,129 +0,0 @@
1
- import * as fs from 'fs-extra';
2
- import uniq from 'lodash/uniq';
3
- import { FileArchive } from 'lucide-react';
4
- import type { GetStaticProps } from 'next';
5
- import Head from 'next/head';
6
- import path from 'path';
7
- import * as React from 'react';
8
- import ReactMarkdown from 'react-markdown';
9
- import rehypeRaw from 'rehype-raw';
10
- import remarkGfm from 'remark-gfm';
11
- import Footer from '../../components/Footer';
12
- import { MarkdownComponents, remarkCodeMeta } from '../../components/Markdown/MarkdownComponents';
13
- import Header from '../../components/old/Header';
14
- import { buildTimeFoundationDesign, fetchDocPageMarkdown, FontDocumentationProps, getClientRuntimeConfig } from '../../components/util';
15
- import { useFoundationTokens } from '../../components/util/useFoundationTokens';
16
-
17
- const basePath = process.env.HANDOFF_APP_BASE_PATH ?? '';
18
- const isRegistry = process.env.HANDOFF_RUNTIME_MODE === 'registry';
19
-
20
- /**
21
- * This statically renders content from the markdown, creating menu and providing
22
- * metadata
23
- *
24
- * This is all done statically at build time
25
- * @param context GetStaticProps
26
- * @returns
27
- */
28
- export const getStaticProps: GetStaticProps = async () => {
29
- // Workspace/static reads the mirrored font archives; a registry build has none (hydrated at runtime).
30
- const customFonts: string[] = [];
31
- try {
32
- const fontDir = path.resolve(process.env.HANDOFF_MODULE_PATH ?? '', '.handoff', `${process.env.HANDOFF_PROJECT_ID}`, 'public', 'fonts');
33
- for (const font of fs.readdirSync(fontDir)) {
34
- if (font.endsWith('.zip')) customFonts.push(font.replace('.zip', ''));
35
- }
36
- } catch {
37
- // No local font archives (e.g. a registry build), so customFonts stays empty.
38
- }
39
-
40
- return {
41
- props: {
42
- ...(await fetchDocPageMarkdown('docs/assets/', 'fonts', `/assets`)).props,
43
- design: buildTimeFoundationDesign(),
44
- config: getClientRuntimeConfig(),
45
- customFonts,
46
- },
47
- };
48
- };
49
-
50
- const FontsPage = ({ content, menu, metadata, customFonts, design, config }: FontDocumentationProps) => {
51
- // Registry hydrates typography tokens (for the family list) + the published font archives at runtime.
52
- const tokens = useFoundationTokens('typography', { design });
53
- const [hydratedFonts, setHydratedFonts] = React.useState<string[]>(customFonts);
54
- React.useEffect(() => {
55
- if (!isRegistry) return;
56
- let active = true;
57
- fetch(`${basePath}/api/docs/assets/fonts`)
58
- .then((res) => (res.ok ? res.json() : null))
59
- .then((data) => {
60
- if (!active || !data?.assets) return;
61
- const names = (data.assets as { path: string }[])
62
- .filter((a) => typeof a.path === 'string' && /^fonts\/.+\.zip$/i.test(a.path))
63
- .map((a) => a.path.slice('fonts/'.length).replace(/\.zip$/i, ''));
64
- setHydratedFonts(names);
65
- })
66
- .catch(() => {});
67
- return () => {
68
- active = false;
69
- };
70
- }, []);
71
-
72
- const typography = tokens.design?.typography ?? [];
73
- const fontFamilies: string[] = uniq(typography.map((type) => type.values.fontFamily));
74
- const fontLinks: string[] = fontFamilies.map((fontFamily) => {
75
- const machineName = fontFamily.replace(/\s/g, '');
76
- const custom = hydratedFonts.find((font) => font === machineName);
77
- if (custom) {
78
- return isRegistry ? `${basePath}/api/docs/assets/fonts/fonts/${machineName}.zip` : `/fonts/${machineName}.zip`;
79
- }
80
- return `https://fonts.google.com/specimen/${fontFamily}`;
81
- });
82
- return (
83
- <div className="c-page">
84
- <Head>
85
- <title>{metadata.metaTitle}</title>
86
- <meta name="description" content={metadata.metaDescription} />
87
- </Head>
88
- <Header config={config} />
89
- <section className="c-content">
90
- <div className="o-container-fluid">
91
- <div className="c-hero">
92
- <div>
93
- <h1>{metadata.title}</h1>
94
- <p>{metadata.description}</p>
95
- </div>
96
- </div>
97
- {fontFamilies.map((fontFamily, i) => (
98
- <React.Fragment key={fontFamily}>
99
- <div className="o-row u-justify-between">
100
- <div className="o-col-5@md">
101
- <h4>{fontFamily}</h4>
102
- </div>
103
- <div className="o-col-6@md">
104
- <div className="c-card">
105
- <FileArchive />
106
- <h4>{fontFamily}</h4>
107
- <p>Font files for installing on a local machine.</p>
108
- <p>
109
- <a href={fontLinks[i]}>Download Font</a>
110
- </p>
111
- </div>
112
- </div>
113
- </div>
114
- <hr />
115
- </React.Fragment>
116
- ))}
117
-
118
- <div className="prose">
119
- <ReactMarkdown components={MarkdownComponents} remarkPlugins={[remarkGfm, remarkCodeMeta]} rehypePlugins={[rehypeRaw]}>
120
- {content}
121
- </ReactMarkdown>
122
- </div>
123
- </div>
124
- </section>
125
- <Footer config={config} />
126
- </div>
127
- );
128
- };
129
- export default FontsPage;
@@ -1,91 +0,0 @@
1
- import { File, FileArchive } from 'lucide-react';
2
- import type { GetStaticProps } from 'next';
3
- import Head from 'next/head';
4
- import ReactMarkdown from 'react-markdown';
5
- import rehypeRaw from 'rehype-raw';
6
- import remarkGfm from 'remark-gfm';
7
- import Footer from '../../components/Footer';
8
- import { MarkdownComponents, remarkCodeMeta } from '../../components/Markdown/MarkdownComponents';
9
- import NavLink from '../../components/NavLink';
10
- import Header from '../../components/old/Header';
11
- import { DocumentationProps, fetchDocPageMarkdown, getClientRuntimeConfig } from '../../components/util';
12
-
13
- /**
14
- * This statically renders content from the markdown, creating menu and providing
15
- * metadata
16
- *
17
- * This is all done statically at build time
18
- * @param context GetStaticProps
19
- * @returns
20
- */
21
- export const getStaticProps: GetStaticProps = async () => {
22
- return {
23
- props: {
24
- ...(await fetchDocPageMarkdown('docs/', 'assets', `/assets`)).props,
25
- config: getClientRuntimeConfig(),
26
- },
27
- };
28
- };
29
-
30
- const AssetsPage = ({ content, menu, metadata, config }: DocumentationProps) => {
31
- return (
32
- <div className="c-page">
33
- <Head>
34
- <title>{metadata.metaTitle}</title>
35
- <meta name="description" content={metadata.metaDescription} />
36
- </Head>
37
- <Header config={config} />
38
- <section className="c-content">
39
- <div className="o-container-fluid">
40
- <div className="c-hero c-hero--boxed c-hero--bg-red">
41
- <div>
42
- <h1 className="c-title--extra-large">{metadata.title}</h1>
43
- <p>{metadata.description}</p>
44
- </div>
45
- </div>
46
-
47
- <div className="o-row">
48
- <div className="o-col-6@md">
49
- <div className="c-card">
50
- <FileArchive />
51
- <h4>Logos</h4>
52
- <p>Official logo used for all digital and offline materials.</p>
53
- <p>
54
- <NavLink href="/assets/logos">View Logos</NavLink>
55
- </p>
56
- </div>
57
- </div>
58
- <div className="o-col-6@md">
59
- <div className="c-card">
60
- <FileArchive />
61
- <h4>Fonts</h4>
62
- <p>Font family and weights for all {config?.app?.client} visuals.</p>
63
- <p>
64
- <NavLink href="/assets/fonts">View Fonts</NavLink>
65
- </p>
66
- </div>
67
- </div>
68
- <div className="o-col-6@md">
69
- <div className="c-card">
70
- <File />
71
- <h4>Iconography</h4>
72
- <p>Library of approved vector iconography.</p>
73
- <p>
74
- <NavLink href="/assets/icons">View Icons</NavLink>
75
- </p>
76
- </div>
77
- </div>
78
- </div>
79
-
80
- <div className="prose">
81
- <ReactMarkdown components={MarkdownComponents} remarkPlugins={[remarkGfm, remarkCodeMeta]} rehypePlugins={[rehypeRaw]}>
82
- {content}
83
- </ReactMarkdown>
84
- </div>
85
- </div>
86
- </section>
87
- <Footer config={config} />
88
- </div>
89
- );
90
- };
91
- export default AssetsPage;
@@ -1,75 +0,0 @@
1
- import { FileArchive } from 'lucide-react';
2
- import type { GetStaticProps } from 'next';
3
- import Head from 'next/head';
4
- import ReactMarkdown from 'react-markdown';
5
- import rehypeRaw from 'rehype-raw';
6
- import remarkGfm from 'remark-gfm';
7
- import Footer from '../../components/Footer';
8
- import { MarkdownComponents, remarkCodeMeta } from '../../components/Markdown/MarkdownComponents';
9
- import { buildAssetDownloadUrl } from '@handoff/artifacts/url';
10
- import Header from '../../components/old/Header';
11
- import { DocumentationProps, fetchDocPageMarkdown, getClientRuntimeConfig } from '../../components/util';
12
-
13
- /**
14
- * This statically renders content from the markdown, creating menu and providing
15
- * metadata
16
- *
17
- * This is all done statically at build time
18
- * @param context GetStaticProps
19
- * @returns
20
- */
21
- export const getStaticProps: GetStaticProps = async () => {
22
- return {
23
- props: {
24
- ...(await fetchDocPageMarkdown('docs/assets/', 'logos', `/assets`)).props,
25
- config: getClientRuntimeConfig(),
26
- },
27
- };
28
- };
29
-
30
- const AssetsLogosPage = ({ content, menu, metadata, config }: DocumentationProps) => {
31
- return (
32
- <div className="c-page">
33
- <Head>
34
- <title>{metadata.metaTitle}</title>
35
- <meta name="description" content={metadata.metaDescription} />
36
- </Head>
37
- <Header config={config} />
38
- <section className="c-content">
39
- <div className="o-container-fluid">
40
- <div className="c-hero">
41
- <div>
42
- <h1>{metadata.title}</h1>
43
- <p>{metadata.description}</p>
44
- </div>
45
- </div>
46
- <div className="o-row u-justify-between">
47
- <div className="o-col-5@md">
48
- <h4>{config?.app?.client} Logo</h4>
49
- </div>
50
- <div className="o-col-6@md">
51
- <div className="c-card">
52
- <FileArchive />
53
- <h4>{config?.app?.client} Logo</h4>
54
- <p>Vector files of approved {config?.app?.client} logos.</p>
55
- <p>
56
- <a href={config?.assets_zip_links?.logos ?? buildAssetDownloadUrl('logos', process.env.HANDOFF_APP_BASE_PATH)} download>
57
- Download Logos
58
- </a>
59
- </p>
60
- </div>
61
- </div>
62
- </div>
63
- <div className="prose">
64
- <ReactMarkdown components={MarkdownComponents} remarkPlugins={[remarkGfm, remarkCodeMeta]} rehypePlugins={[rehypeRaw]}>
65
- {content}
66
- </ReactMarkdown>
67
- </div>
68
- <hr />
69
- </div>
70
- </section>
71
- <Footer config={config} />
72
- </div>
73
- );
74
- };
75
- export default AssetsLogosPage;
@@ -1,22 +0,0 @@
1
- /**
2
- * Built-in Resend provider, selected with `runtime.registry.email.provider: "resend"` (the default).
3
- * A plain `fetch` to the Resend HTTP API, so it adds no dependency.
4
- */
5
-
6
- import type { EmailProvider } from '../types';
7
-
8
- /** Build a Resend provider from options whose references are already resolved. */
9
- export const createResendProvider = (options: Record<string, unknown>): EmailProvider => ({
10
- async send({ from, to, subject, html, text }) {
11
- const apiKey = typeof options.apiKey === 'string' ? options.apiKey : '';
12
- if (!apiKey) throw new Error('The Resend API key is not set.');
13
- const response = await fetch('https://api.resend.com/emails', {
14
- method: 'POST',
15
- headers: { Authorization: `Bearer ${apiKey}`, 'Content-Type': 'application/json' },
16
- body: JSON.stringify({ from, to: [to], subject, html, text }),
17
- });
18
- if (!response.ok) {
19
- throw new Error(`Resend rejected the email (HTTP ${response.status}): ${await response.text()}`);
20
- }
21
- },
22
- });