@hraness/design-kit 0.31.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARTICLE_COPY.md +208 -0
- package/HERO_FIELDS.md +55 -0
- package/ICONS.md +56 -0
- package/LANTERN_MATERIAL.md +60 -0
- package/LICENSE +21 -0
- package/MARKETING_COPY.md +49 -0
- package/MARKETING_PRESET.md +108 -0
- package/PALETTES.md +90 -0
- package/PAPER_THEME.md +128 -0
- package/README.md +745 -0
- package/STATUS_PAGES.md +67 -0
- package/STYLE.md +245 -0
- package/WRITING.md +72 -0
- package/dist/browser/index.js +2345 -0
- package/dist/chunk-44djxk16.js +6872 -0
- package/dist/chunk-5gtx3pza.js +9 -0
- package/dist/chunk-77391vmq.js +454 -0
- package/dist/chunk-7b1586eb.js +1209 -0
- package/dist/chunk-8834fh4n.js +6 -0
- package/dist/chunk-9t8xyqte.js +870 -0
- package/dist/chunk-cejpzyfh.js +446 -0
- package/dist/chunk-eh71jz57.js +429 -0
- package/dist/chunk-evb02bc1.js +516 -0
- package/dist/chunk-h4k7yv6x.js +368 -0
- package/dist/chunk-he8eznb1.js +760 -0
- package/dist/chunk-mr1vdcjq.js +404 -0
- package/dist/chunk-wzvdn8ey.js +116 -0
- package/dist/fonts/nebula-sans/social-fonts.generated.js +53 -0
- package/dist/icons.js +48 -0
- package/dist/index.js +237 -0
- package/dist/launch.js +41 -0
- package/dist/mockups/client.js +422 -0
- package/dist/mockups/index.js +1850 -0
- package/dist/portfolio.js +1549 -0
- package/dist/provider-marks.js +19 -0
- package/dist/react/charts.js +11 -0
- package/dist/react/hero-backdrop.js +8 -0
- package/dist/react/index.js +7252 -0
- package/dist/react/platform-install.js +10 -0
- package/dist/react/server.js +137 -0
- package/dist/stylex-manifest.json +1 -0
- package/dist/stylex.css +5392 -0
- package/dist/syntax-highlighting.js +15 -0
- package/dist/testing.js +250 -0
- package/package.json +377 -0
- package/portfolio-inventory.json +31 -0
- package/scripts/check-lantern-material-snapshot.d.mts +26 -0
- package/scripts/check-lantern-material-snapshot.mjs +126 -0
- package/scripts/check-marketing-snapshot.d.mts +12 -0
- package/scripts/check-marketing-snapshot.mjs +58 -0
- package/scripts/lantern-material-snapshot.ts +101 -0
- package/scripts/marketing-textures.ts +48 -0
- package/scripts/paper-theme-snapshot.ts +97 -0
- package/scripts/product-marketing-snapshot.ts +149 -0
- package/src/appearance-menu.css +266 -0
- package/src/appearance.ts +32 -0
- package/src/article-html.ts +269 -0
- package/src/article.ts +686 -0
- package/src/browser/appearance-menu.ts +604 -0
- package/src/browser/artifact-share.ts +164 -0
- package/src/browser/design-palette.ts +197 -0
- package/src/browser/foil.ts +124 -0
- package/src/browser/hero-light.ts +11 -0
- package/src/browser/index.ts +29 -0
- package/src/browser/status-field.ts +432 -0
- package/src/browser/status-page.ts +100 -0
- package/src/browser/sticky-offset.ts +82 -0
- package/src/browser/theme-color-sync.ts +213 -0
- package/src/charts.css +7 -0
- package/src/compiler-components.css +34 -0
- package/src/compiler-foundation.css +18 -0
- package/src/compiler-palettes.css +7 -0
- package/src/compiler-tokens.css +342 -0
- package/src/components.css +3 -0
- package/src/design-gallery.css +431 -0
- package/src/effects.css +14 -0
- package/src/fonts/geist-mono/GeistMono[wght].woff2 +0 -0
- package/src/fonts/geist-mono/OFL.txt +93 -0
- package/src/fonts/geist-mono/PROVENANCE.md +12 -0
- package/src/fonts/instrument-serif/OFL.txt +93 -0
- package/src/fonts/instrument-serif/UPSTREAM.md +14 -0
- package/src/fonts/instrument-serif/instrument-serif-latin-400.woff2 +0 -0
- package/src/fonts/nebula-sans/LICENSE.txt +96 -0
- package/src/fonts/nebula-sans/NebulaSans-Black.woff2 +0 -0
- package/src/fonts/nebula-sans/NebulaSans-BlackItalic.woff2 +0 -0
- package/src/fonts/nebula-sans/NebulaSans-Bold.otf +0 -0
- package/src/fonts/nebula-sans/NebulaSans-Bold.woff2 +0 -0
- package/src/fonts/nebula-sans/NebulaSans-BoldItalic.woff2 +0 -0
- package/src/fonts/nebula-sans/NebulaSans-Book.otf +0 -0
- package/src/fonts/nebula-sans/NebulaSans-Book.woff2 +0 -0
- package/src/fonts/nebula-sans/NebulaSans-BookItalic.woff2 +0 -0
- package/src/fonts/nebula-sans/NebulaSans-Light.woff2 +0 -0
- package/src/fonts/nebula-sans/NebulaSans-LightItalic.woff2 +0 -0
- package/src/fonts/nebula-sans/NebulaSans-Medium.woff2 +0 -0
- package/src/fonts/nebula-sans/NebulaSans-MediumItalic.woff2 +0 -0
- package/src/fonts/nebula-sans/NebulaSans-Semibold.woff2 +0 -0
- package/src/fonts/nebula-sans/NebulaSans-SemiboldItalic.woff2 +0 -0
- package/src/fonts/nebula-sans/PROVENANCE.md +14 -0
- package/src/fonts/nebula-sans/social-fonts.generated.ts +3873 -0
- package/src/fonts.css +106 -0
- package/src/icons/act60/act60.svg +5 -0
- package/src/icons/act60/application.svg +9 -0
- package/src/icons/act60/chapter-compare.svg +3 -0
- package/src/icons/act60/decree-residence.svg +6 -0
- package/src/icons/act60/estimate.svg +7 -0
- package/src/icons/act60/export-services.svg +4 -0
- package/src/icons/act60/investor.svg +13 -0
- package/src/icons/act60/presence.svg +7 -0
- package/src/icons/aicharts/aicharts.svg +6 -0
- package/src/icons/aicharts/cost-compare.svg +4 -0
- package/src/icons/aicharts/task-audio.svg +7 -0
- package/src/icons/aicharts/task-coding.svg +6 -0
- package/src/icons/aicharts/task-images.svg +3 -0
- package/src/icons/aicharts/task-reasoning.svg +9 -0
- package/src/icons/aicharts/task-research.svg +5 -0
- package/src/icons/aicharts/task-video.svg +4 -0
- package/src/icons/manifest.json +1371 -0
- package/src/icons/platonik/conversation.svg +3 -0
- package/src/icons/platonik/creature.svg +7 -0
- package/src/icons/platonik/economy.svg +4 -0
- package/src/icons/platonik/keep-light.svg +6 -0
- package/src/icons/platonik/platonik.svg +7 -0
- package/src/icons/platonik/spark-route.svg +15 -0
- package/src/icons/receipts/act60-retry.receipt.json +888 -0
- package/src/icons/receipts/act60.receipt.json +1366 -0
- package/src/icons/receipts/aicharts.receipt.json +1546 -0
- package/src/icons/receipts/platonik.receipt.json +1011 -0
- package/src/icons/receipts/roughday.receipt.json +770 -0
- package/src/icons/receipts/shared.receipt.json +1296 -0
- package/src/icons/receipts/slopcamera.receipt.json +1495 -0
- package/src/icons/receipts/soundfish-retry.receipt.json +460 -0
- package/src/icons/receipts/soundfish.receipt.json +1709 -0
- package/src/icons/receipts/sponge.receipt.json +1743 -0
- package/src/icons/receipts/stripe-history-retry.receipt.json +145 -0
- package/src/icons/receipts/stripe-history.receipt.json +1202 -0
- package/src/icons/receipts/wordcell.receipt.json +1924 -0
- package/src/icons/roughday/finance.svg +7 -0
- package/src/icons/roughday/roughday.svg +6 -0
- package/src/icons/roughday/technology.svg +4 -0
- package/src/icons/roughday/world.svg +4 -0
- package/src/icons/sets/act60.json +63 -0
- package/src/icons/sets/aicharts.json +63 -0
- package/src/icons/sets/platonik.json +39 -0
- package/src/icons/sets/roughday.json +25 -0
- package/src/icons/sets/shared.json +12 -0
- package/src/icons/sets/slopcamera.json +47 -0
- package/src/icons/sets/soundfish.json +43 -0
- package/src/icons/sets/sponge.json +51 -0
- package/src/icons/sets/stripe-history.json +39 -0
- package/src/icons/sets/wordcell.json +51 -0
- package/src/icons/shared/agent-skill.svg +4 -0
- package/src/icons/shared/cli.svg +5 -0
- package/src/icons/shared/privacy.svg +3 -0
- package/src/icons/shared/research.svg +7 -0
- package/src/icons/shared/sdk.svg +3 -0
- package/src/icons/slopcamera/diagram.svg +3 -0
- package/src/icons/slopcamera/direct-scene.svg +5 -0
- package/src/icons/slopcamera/edit-video.svg +17 -0
- package/src/icons/slopcamera/mcp.svg +3 -0
- package/src/icons/slopcamera/native-world.svg +3 -0
- package/src/icons/slopcamera/slopcamera.svg +11 -0
- package/src/icons/soundfish/agent-ui.svg +7 -0
- package/src/icons/soundfish/collaborate.svg +5 -0
- package/src/icons/soundfish/compose.svg +3 -0
- package/src/icons/soundfish/hear.svg +6 -0
- package/src/icons/soundfish/human-ui.svg +6 -0
- package/src/icons/soundfish/share.svg +3 -0
- package/src/icons/soundfish/soundfish.svg +6 -0
- package/src/icons/sponge/api.svg +3 -0
- package/src/icons/sponge/documents.svg +3 -0
- package/src/icons/sponge/library.svg +3 -0
- package/src/icons/sponge/one-question.svg +5 -0
- package/src/icons/sponge/people-direct.svg +5 -0
- package/src/icons/sponge/read.svg +3 -0
- package/src/icons/sponge/save.svg +4 -0
- package/src/icons/sponge/sponge.svg +7 -0
- package/src/icons/stripe-history/company-history.svg +3 -0
- package/src/icons/stripe-history/evidence.svg +4 -0
- package/src/icons/stripe-history/independence.svg +7 -0
- package/src/icons/stripe-history/publications.svg +11 -0
- package/src/icons/stripe-history/sources.svg +6 -0
- package/src/icons/stripe-history/stripe-history.svg +3 -0
- package/src/icons/wordcell/backlinks.svg +6 -0
- package/src/icons/wordcell/capture.svg +6 -0
- package/src/icons/wordcell/git-provenance.svg +4 -0
- package/src/icons/wordcell/kb.svg +4 -0
- package/src/icons/wordcell/markdown.svg +4 -0
- package/src/icons/wordcell/scopes.svg +3 -0
- package/src/icons/wordcell/search.svg +7 -0
- package/src/icons.generated.ts +734 -0
- package/src/icons.ts +51 -0
- package/src/index.ts +275 -0
- package/src/lantern-material.css +213 -0
- package/src/launch.ts +624 -0
- package/src/marketing-assets/UPSTREAM.md +18 -0
- package/src/marketing-assets/cells.svg +1 -0
- package/src/marketing-assets/grain.svg +1 -0
- package/src/marketing-forced-colors.css +12 -0
- package/src/mockups/client.tsx +415 -0
- package/src/mockups/core.tsx +318 -0
- package/src/mockups/frames.tsx +368 -0
- package/src/mockups/index.ts +14 -0
- package/src/mockups/surfaces.tsx +681 -0
- package/src/mockups.css +2607 -0
- package/src/palette-appearance.ts +45 -0
- package/src/palette-bridge.css +124 -0
- package/src/palette-color.ts +41 -0
- package/src/palette-system.css +721 -0
- package/src/palette-themes.ts +23 -0
- package/src/palette-tokens.stylex.ts +519 -0
- package/src/palettes.css +3 -0
- package/src/palettes.ts +161 -0
- package/src/paper-theme.css +244 -0
- package/src/plain-publication.css +1241 -0
- package/src/plain-site.css +397 -0
- package/src/platforms.ts +151 -0
- package/src/portfolio.generated.json +1595 -0
- package/src/portfolio.generated.ts +1596 -0
- package/src/portfolio.ts +226 -0
- package/src/product-marketing-foundation.css +433 -0
- package/src/product-marketing-preset.css +261 -0
- package/src/product-marketing.css +2186 -0
- package/src/provider-marks.generated.ts +112 -0
- package/src/provider-marks.ts +143 -0
- package/src/react/animated-rail-stage.stylex.ts +15 -0
- package/src/react/animated-rail-stage.tsx +51 -0
- package/src/react/app-shell.stylex.ts +104 -0
- package/src/react/app-shell.tsx +121 -0
- package/src/react/article.tsx +602 -0
- package/src/react/aurora-dots-background.tsx +34 -0
- package/src/react/charts.stylex.ts +563 -0
- package/src/react/charts.tsx +610 -0
- package/src/react/chat.stylex.ts +34 -0
- package/src/react/chat.tsx +155 -0
- package/src/react/design-gallery.tsx +943 -0
- package/src/react/design-palette.stylex.ts +124 -0
- package/src/react/design-palette.tsx +196 -0
- package/src/react/design-theme-context.tsx +41 -0
- package/src/react/effects.stylex.ts +325 -0
- package/src/react/fader.stylex.ts +71 -0
- package/src/react/fader.tsx +151 -0
- package/src/react/foil-card-math.ts +100 -0
- package/src/react/foil-card-surface.stylex.ts +326 -0
- package/src/react/foil-card-surface.tsx +742 -0
- package/src/react/foil-mark.tsx +35 -0
- package/src/react/foil.stylex.ts +163 -0
- package/src/react/haptics.ts +228 -0
- package/src/react/hero-backdrop.tsx +22 -0
- package/src/react/index.ts +38 -0
- package/src/react/keyboard-shortcuts.ts +228 -0
- package/src/react/lantern-material-gallery.tsx +166 -0
- package/src/react/lantern-material.stylex.ts +39 -0
- package/src/react/launch-beats.tsx +73 -0
- package/src/react/navigation-rail.stylex.ts +130 -0
- package/src/react/navigation-rail.tsx +228 -0
- package/src/react/particle-halo.tsx +115 -0
- package/src/react/phaser-dots.tsx +501 -0
- package/src/react/platform-icons.tsx +94 -0
- package/src/react/platform-install.stylex.ts +385 -0
- package/src/react/platform-install.tsx +389 -0
- package/src/react/playback-transport.stylex.ts +14 -0
- package/src/react/playback-transport.tsx +112 -0
- package/src/react/procedural-backdrop.tsx +192 -0
- package/src/react/procedural-recipe.ts +323 -0
- package/src/react/product-marketing.stylex.ts +4469 -0
- package/src/react/product-marketing.tsx +1611 -0
- package/src/react/production-data-preview-notice.stylex.ts +40 -0
- package/src/react/production-data-preview-notice.tsx +34 -0
- package/src/react/provider-mark.stylex.ts +132 -0
- package/src/react/provider-mark.tsx +120 -0
- package/src/react/relative-time.tsx +117 -0
- package/src/react/route-state.stylex.ts +39 -0
- package/src/react/route-state.tsx +350 -0
- package/src/react/server.ts +11 -0
- package/src/react/social-kit-panel.tsx +103 -0
- package/src/react/sticky-offset.tsx +33 -0
- package/src/react/surfaces.stylex.ts +179 -0
- package/src/react/surfaces.tsx +330 -0
- package/src/react/syntax-code.tsx +33 -0
- package/src/react/theme-color-sync.ts +1 -0
- package/src/react/theme-resolution.ts +11 -0
- package/src/react/theme.stylex.ts +276 -0
- package/src/react/theme.tsx +402 -0
- package/src/reading.css +72 -0
- package/src/relative-time.ts +185 -0
- package/src/reset.css +2 -0
- package/src/site-shell.css +16 -0
- package/src/status-page-html.ts +63 -0
- package/src/status-page.css +276 -0
- package/src/status-page.ts +323 -0
- package/src/styles.css +21 -0
- package/src/syntax-code-lexers.ts +303 -0
- package/src/syntax-highlighting.css +78 -0
- package/src/syntax-highlighting.ts +477 -0
- package/src/syntax-tokens.ts +33 -0
- package/src/testing.ts +307 -0
- package/src/tokens.css +3 -0
- package/src/typography.css +86 -0
- package/vendor/evilcharts/LICENSE +21 -0
- package/vendor/evilcharts/UPSTREAM.md +14 -0
- package/vendor/platform-marks/LICENSE +30 -0
- package/vendor/platform-marks/UPSTREAM.md +15 -0
- package/vendor/platform-marks/apple.svg +1 -0
- package/vendor/platform-marks/linux.svg +1 -0
- package/vendor/provider-marks/CRUSH-LICENSE.md +134 -0
- package/vendor/provider-marks/LICENSE +21 -0
- package/vendor/provider-marks/UPSTREAM.md +27 -0
- package/vendor/provider-marks/aider.svg +3 -0
- package/vendor/provider-marks/crush-heartbit.svg +8 -0
- package/vendor/provider-marks/lobehub/alibabacloud-color.svg +1 -0
- package/vendor/provider-marks/lobehub/alibabacloud.svg +1 -0
- package/vendor/provider-marks/lobehub/amp-color.svg +1 -0
- package/vendor/provider-marks/lobehub/amp.svg +1 -0
- package/vendor/provider-marks/lobehub/anthropic.svg +1 -0
- package/vendor/provider-marks/lobehub/claude-color.svg +1 -0
- package/vendor/provider-marks/lobehub/claude.svg +1 -0
- package/vendor/provider-marks/lobehub/claudecode-color.svg +1 -0
- package/vendor/provider-marks/lobehub/claudecode.svg +1 -0
- package/vendor/provider-marks/lobehub/codex-color.svg +1 -0
- package/vendor/provider-marks/lobehub/codex.svg +1 -0
- package/vendor/provider-marks/lobehub/cursor.svg +1 -0
- package/vendor/provider-marks/lobehub/deepseek-color.svg +1 -0
- package/vendor/provider-marks/lobehub/deepseek.svg +1 -0
- package/vendor/provider-marks/lobehub/devin-color.svg +1 -0
- package/vendor/provider-marks/lobehub/devin.svg +1 -0
- package/vendor/provider-marks/lobehub/gemini-color.svg +1 -0
- package/vendor/provider-marks/lobehub/gemini.svg +1 -0
- package/vendor/provider-marks/lobehub/geminicli-color.svg +1 -0
- package/vendor/provider-marks/lobehub/geminicli.svg +1 -0
- package/vendor/provider-marks/lobehub/goose.svg +1 -0
- package/vendor/provider-marks/lobehub/meta-color.svg +1 -0
- package/vendor/provider-marks/lobehub/meta.svg +1 -0
- package/vendor/provider-marks/lobehub/mistral-color.svg +1 -0
- package/vendor/provider-marks/lobehub/mistral.svg +1 -0
- package/vendor/provider-marks/lobehub/moonshot.svg +1 -0
- package/vendor/provider-marks/lobehub/nvidia-color.svg +1 -0
- package/vendor/provider-marks/lobehub/nvidia.svg +1 -0
- package/vendor/provider-marks/lobehub/openai.svg +1 -0
- package/vendor/provider-marks/lobehub/opencode.svg +1 -0
- package/vendor/provider-marks/lobehub/perplexity-color.svg +1 -0
- package/vendor/provider-marks/lobehub/perplexity.svg +1 -0
- package/vendor/provider-marks/lobehub/qwen-color.svg +1 -0
- package/vendor/provider-marks/lobehub/qwen.svg +1 -0
- package/vendor/provider-marks/lobehub/xai.svg +1 -0
- package/vendor/provider-marks/lobehub/zai.svg +1 -0
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
import type { CSSProperties, ReactNode } from "react";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
MockupGlyph,
|
|
5
|
+
MockupRoot,
|
|
6
|
+
SampleText,
|
|
7
|
+
WindowLights,
|
|
8
|
+
type MockupOptOut,
|
|
9
|
+
type MockupRootProps,
|
|
10
|
+
} from "./core.js";
|
|
11
|
+
|
|
12
|
+
/** Hosts a mockup may show in an address bar: reserved example names only (RFC 2606 and RFC 6761). */
|
|
13
|
+
export const MOCKUP_EXAMPLE_HOST = /(^|\.)(example(\.(com|net|org))?|test|invalid|localhost)$/u;
|
|
14
|
+
|
|
15
|
+
/** Splits a mockup address into host and path, and rejects hosts that could belong to someone. */
|
|
16
|
+
export function mockupAddress(url: string): Readonly<{ host: string; path: string }> {
|
|
17
|
+
const trimmed = url.trim().replace(/^[a-z]+:\/\//iu, "");
|
|
18
|
+
const slash = trimmed.indexOf("/");
|
|
19
|
+
const host = (slash < 0 ? trimmed : trimmed.slice(0, slash)).toLowerCase();
|
|
20
|
+
const path = slash < 0 ? "" : trimmed.slice(slash);
|
|
21
|
+
if (!MOCKUP_EXAMPLE_HOST.test(host)) {
|
|
22
|
+
throw new RangeError(`Mockup address ${JSON.stringify(url)} must use a reserved example host such as example.com or site.example.`);
|
|
23
|
+
}
|
|
24
|
+
return { host, path: path === "/" ? "" : path };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function pageStyle(height: number | undefined): CSSProperties | undefined {
|
|
28
|
+
if (height === undefined) return undefined;
|
|
29
|
+
if (!(height > 0)) throw new RangeError("A mockup page height must be positive.");
|
|
30
|
+
return { "--hkm-page-height": `${height}px` } as CSSProperties;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function optOutProps(optOut: MockupOptOut | undefined): { optOut?: MockupOptOut } {
|
|
34
|
+
return optOut === undefined ? {} : { optOut };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* A neutral browser window: window buttons, history controls, an address
|
|
39
|
+
* field on a reserved example host, an optional toolbar slot, and a page area.
|
|
40
|
+
* With `height`, the page area is a window onto a longer page; `fade` (on by
|
|
41
|
+
* default then) says it keeps going.
|
|
42
|
+
*/
|
|
43
|
+
export function BrowserFrame({
|
|
44
|
+
children,
|
|
45
|
+
fade,
|
|
46
|
+
height,
|
|
47
|
+
toolbar,
|
|
48
|
+
url,
|
|
49
|
+
...root
|
|
50
|
+
}: MockupRootProps & Readonly<{ url: string; toolbar?: ReactNode; height?: number; fade?: boolean; children: ReactNode }>) {
|
|
51
|
+
return (
|
|
52
|
+
<MockupRoot {...root} kind="browser">
|
|
53
|
+
<BrowserWindow {...(fade === undefined ? {} : { fade })} {...(height === undefined ? {} : { height })} toolbar={toolbar} url={url}>
|
|
54
|
+
{children}
|
|
55
|
+
</BrowserWindow>
|
|
56
|
+
</MockupRoot>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** The browser chrome without a root, for surfaces that compose it inside their own root. */
|
|
61
|
+
export function BrowserWindow({
|
|
62
|
+
children,
|
|
63
|
+
fade,
|
|
64
|
+
height,
|
|
65
|
+
toolbar,
|
|
66
|
+
url,
|
|
67
|
+
}: Readonly<{ url: string; toolbar?: ReactNode; height?: number; fade?: boolean; children: ReactNode }>) {
|
|
68
|
+
const { host, path } = mockupAddress(url);
|
|
69
|
+
const fades = fade ?? height !== undefined;
|
|
70
|
+
return (
|
|
71
|
+
<div className="hkm-window">
|
|
72
|
+
<div aria-hidden="true" className="hkm-browser-bar">
|
|
73
|
+
<WindowLights />
|
|
74
|
+
<span className="hkm-browser-nav">
|
|
75
|
+
<MockupGlyph name="back" size={16} />
|
|
76
|
+
<MockupGlyph name="forward" size={16} />
|
|
77
|
+
<MockupGlyph name="reload" size={15} />
|
|
78
|
+
</span>
|
|
79
|
+
<span className="hkm-address">
|
|
80
|
+
<MockupGlyph name="lock" size={12} />
|
|
81
|
+
<span className="hkm-address-host">{host}</span>
|
|
82
|
+
{path === "" ? null : <span className="hkm-address-path">{path}</span>}
|
|
83
|
+
</span>
|
|
84
|
+
<span className="hkm-browser-tools">{toolbar}</span>
|
|
85
|
+
</div>
|
|
86
|
+
<div className="hkm-page" data-hkm-fade={fades ? "" : undefined} style={pageStyle(height)}>
|
|
87
|
+
{children}
|
|
88
|
+
</div>
|
|
89
|
+
</div>
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** One line in a terminal mockup. */
|
|
94
|
+
export type TerminalLine = Readonly<{
|
|
95
|
+
kind: "input" | "output" | "comment";
|
|
96
|
+
text: string;
|
|
97
|
+
tone?: "ok" | "warn" | "error" | "muted";
|
|
98
|
+
/** A beat id a film or step-through can key on; rendered as `data-hkm-beat`. */
|
|
99
|
+
beat?: string;
|
|
100
|
+
}>;
|
|
101
|
+
|
|
102
|
+
const TERMINAL_KINDS = new Set(["input", "output", "comment"]);
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* A terminal window with a prompt, typed commands, output, and comments.
|
|
106
|
+
* Commands and output are sample text; keep them to commands the product ships.
|
|
107
|
+
*/
|
|
108
|
+
export function TerminalFrame({
|
|
109
|
+
fade,
|
|
110
|
+
height,
|
|
111
|
+
lines,
|
|
112
|
+
prompt = "$",
|
|
113
|
+
title = "Terminal",
|
|
114
|
+
...root
|
|
115
|
+
}: MockupRootProps & Readonly<{ title?: string; prompt?: string; lines: readonly TerminalLine[]; height?: number; fade?: boolean }>) {
|
|
116
|
+
for (const line of lines) {
|
|
117
|
+
if (!TERMINAL_KINDS.has(line.kind)) throw new TypeError(`Unknown terminal line kind ${JSON.stringify(line.kind)}.`);
|
|
118
|
+
}
|
|
119
|
+
const fades = fade ?? height !== undefined;
|
|
120
|
+
return (
|
|
121
|
+
<MockupRoot {...root} kind="terminal">
|
|
122
|
+
<div className="hkm-window">
|
|
123
|
+
<div aria-hidden="true" className="hkm-title-bar">
|
|
124
|
+
<WindowLights />
|
|
125
|
+
<span className="hkm-title">{title}</span>
|
|
126
|
+
<span />
|
|
127
|
+
</div>
|
|
128
|
+
<div className="hkm-page hkm-terminal-body" data-hkm-fade={fades ? "" : undefined} style={pageStyle(height)}>
|
|
129
|
+
<div className="hkm-terminal-lines">
|
|
130
|
+
{lines.map((line, index) => (
|
|
131
|
+
<div className="hkm-terminal-line" data-hkm-beat={line.beat} data-hkm-line={line.kind} data-hkm-tone={line.tone} key={index}>
|
|
132
|
+
{line.kind === "input" ? <span className="hkm-terminal-prompt">{prompt}</span> : null}
|
|
133
|
+
{line.kind === "comment" ? <span className="hkm-terminal-prompt">#</span> : null}
|
|
134
|
+
<SampleText {...optOutProps(root.optOut)}>{line.text}</SampleText>
|
|
135
|
+
</div>
|
|
136
|
+
))}
|
|
137
|
+
<div aria-hidden="true" className="hkm-terminal-line" data-hkm-line="input">
|
|
138
|
+
<span className="hkm-terminal-prompt">{prompt}</span>
|
|
139
|
+
<span className="hkm-caret" />
|
|
140
|
+
</div>
|
|
141
|
+
</div>
|
|
142
|
+
</div>
|
|
143
|
+
</div>
|
|
144
|
+
</MockupRoot>
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* A desktop app window: title bar, optional toolbar, optional translucent
|
|
150
|
+
* sidebar, and content.
|
|
151
|
+
*/
|
|
152
|
+
export function MacWindow({
|
|
153
|
+
children,
|
|
154
|
+
sidebar,
|
|
155
|
+
title,
|
|
156
|
+
toolbar,
|
|
157
|
+
...root
|
|
158
|
+
}: MockupRootProps & Readonly<{ title?: string; sidebar?: ReactNode; toolbar?: ReactNode; children: ReactNode }>) {
|
|
159
|
+
return (
|
|
160
|
+
<MockupRoot {...root} kind="app-window">
|
|
161
|
+
<div className="hkm-window" data-hkm-sidebar={sidebar === undefined ? undefined : ""}>
|
|
162
|
+
{sidebar === undefined ? null : (
|
|
163
|
+
<div className="hkm-app-sidebar">
|
|
164
|
+
<WindowLights />
|
|
165
|
+
<div className="hkm-app-sidebar-body">{sidebar}</div>
|
|
166
|
+
</div>
|
|
167
|
+
)}
|
|
168
|
+
<div className="hkm-app-main">
|
|
169
|
+
<div className="hkm-title-bar hkm-app-bar">
|
|
170
|
+
{sidebar === undefined ? <WindowLights /> : <span />}
|
|
171
|
+
<span className="hkm-title">{title}</span>
|
|
172
|
+
<span className="hkm-app-tools">{toolbar}</span>
|
|
173
|
+
</div>
|
|
174
|
+
<div className="hkm-app-content">{children}</div>
|
|
175
|
+
</div>
|
|
176
|
+
</div>
|
|
177
|
+
</MockupRoot>
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/** One row in a menu bar popover. */
|
|
182
|
+
export type MenuBarItem = Readonly<{ id: string; label: string; detail?: string; tone?: "ok" | "warn" | "error"; selected?: boolean }>;
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* A slice of a desktop menu bar with a status item open. `mark` is the status
|
|
186
|
+
* item's glyph; pass the product's own mark, or leave it for a neutral dot.
|
|
187
|
+
*/
|
|
188
|
+
export function MenuBarPopover({
|
|
189
|
+
footer,
|
|
190
|
+
items,
|
|
191
|
+
mark,
|
|
192
|
+
title,
|
|
193
|
+
...root
|
|
194
|
+
}: MockupRootProps & Readonly<{ items: readonly MenuBarItem[]; title?: string; mark?: ReactNode; footer?: string }>) {
|
|
195
|
+
const ids = new Set<string>();
|
|
196
|
+
for (const item of items) {
|
|
197
|
+
if (ids.has(item.id)) throw new RangeError(`MenuBarPopover item ids must be unique: ${item.id}`);
|
|
198
|
+
ids.add(item.id);
|
|
199
|
+
}
|
|
200
|
+
return (
|
|
201
|
+
<MockupRoot {...root} kind="menubar">
|
|
202
|
+
<div aria-hidden="true" className="hkm-menubar">
|
|
203
|
+
<span className="hkm-menubar-spacer" />
|
|
204
|
+
<MockupGlyph name="search" size={14} />
|
|
205
|
+
<span className="hkm-menubar-item" data-hkm-open="">{mark ?? <span className="hkm-menubar-dot" />}</span>
|
|
206
|
+
<span className="hkm-menubar-clock">9:41</span>
|
|
207
|
+
</div>
|
|
208
|
+
<div className="hkm-popover">
|
|
209
|
+
{title === undefined ? null : <div className="hkm-popover-title">{title}</div>}
|
|
210
|
+
<ul className="hkm-popover-list">
|
|
211
|
+
{items.map((item) => (
|
|
212
|
+
<li className="hkm-popover-item" data-hkm-selected={item.selected === true ? "" : undefined} data-hkm-tone={item.tone} key={item.id}>
|
|
213
|
+
<span className="hkm-popover-dot" />
|
|
214
|
+
<span className="hkm-popover-text">
|
|
215
|
+
<span className="hkm-popover-label"><SampleText {...optOutProps(root.optOut)}>{item.label}</SampleText></span>
|
|
216
|
+
{item.detail === undefined ? null : <span className="hkm-popover-detail"><SampleText {...optOutProps(root.optOut)}>{item.detail}</SampleText></span>}
|
|
217
|
+
</span>
|
|
218
|
+
</li>
|
|
219
|
+
))}
|
|
220
|
+
</ul>
|
|
221
|
+
{footer === undefined ? null : <div className="hkm-popover-footer">{footer}</div>}
|
|
222
|
+
</div>
|
|
223
|
+
</MockupRoot>
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* A generic modern phone: metal edge, bezel, side buttons, a camera island,
|
|
229
|
+
* status bar, and home indicator. No logos, carrier names, or real numbers.
|
|
230
|
+
* The screen is 390 by 844 points and scales with the root's width. Children
|
|
231
|
+
* sit in a scrollable body that clears the status bar and home indicator.
|
|
232
|
+
*/
|
|
233
|
+
export function PhoneFrame({
|
|
234
|
+
children,
|
|
235
|
+
screenHeight,
|
|
236
|
+
statusTime,
|
|
237
|
+
width,
|
|
238
|
+
...root
|
|
239
|
+
}: MockupRootProps & Readonly<{ statusTime?: string; width?: number; screenHeight?: number; children: ReactNode }>) {
|
|
240
|
+
return (
|
|
241
|
+
<MockupRoot {...root} kind="phone" style={phoneStyle(width, screenHeight)}>
|
|
242
|
+
<PhoneShell {...(statusTime === undefined ? {} : { statusTime })}>
|
|
243
|
+
<div className="hkm-phone-body">{children}</div>
|
|
244
|
+
</PhoneShell>
|
|
245
|
+
</MockupRoot>
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/** Size variables for a phone root. */
|
|
250
|
+
export function phoneStyle(width: number | undefined, screenHeight: number | undefined): CSSProperties | undefined {
|
|
251
|
+
const style: Record<string, string> = {};
|
|
252
|
+
if (width !== undefined) {
|
|
253
|
+
if (!(width > 0)) throw new RangeError("A phone mockup width must be positive.");
|
|
254
|
+
style["--hkm-phone-width"] = `${width}px`;
|
|
255
|
+
}
|
|
256
|
+
if (screenHeight !== undefined) {
|
|
257
|
+
if (!(screenHeight >= 200 && screenHeight <= 844)) throw new RangeError("A phone mockup screenHeight must be 200 to 844 points.");
|
|
258
|
+
style["--hkm-phone-height"] = String(screenHeight + 22);
|
|
259
|
+
}
|
|
260
|
+
return Object.keys(style).length === 0 ? undefined : (style as CSSProperties);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/** The phone device without a root, for surfaces that compose it inside their own root. */
|
|
264
|
+
export function PhoneShell({ children, statusTime = "9:41" }: Readonly<{ statusTime?: string; children: ReactNode }>) {
|
|
265
|
+
return (
|
|
266
|
+
<div className="hkm-device">
|
|
267
|
+
<span aria-hidden="true" className="hkm-device-button" data-hkm-button="action" />
|
|
268
|
+
<span aria-hidden="true" className="hkm-device-button" data-hkm-button="volume-up" />
|
|
269
|
+
<span aria-hidden="true" className="hkm-device-button" data-hkm-button="volume-down" />
|
|
270
|
+
<span aria-hidden="true" className="hkm-device-button" data-hkm-button="power" />
|
|
271
|
+
<div className="hkm-bezel">
|
|
272
|
+
<div className="hkm-screen">
|
|
273
|
+
<div className="hkm-screen-content">{children}</div>
|
|
274
|
+
<div aria-hidden="true" className="hkm-status-bar">
|
|
275
|
+
<span className="hkm-status-time">{statusTime}</span>
|
|
276
|
+
<span className="hkm-status-glyphs">
|
|
277
|
+
<svg className="hkm-status-cell" viewBox="0 0 19 12">
|
|
278
|
+
<rect height="4.5" rx="0.9" width="3.2" x="0" y="7.5" />
|
|
279
|
+
<rect height="6.8" rx="0.9" width="3.2" x="5.1" y="5.2" />
|
|
280
|
+
<rect height="9.3" rx="0.9" width="3.2" x="10.2" y="2.7" />
|
|
281
|
+
<rect height="12" rx="0.9" width="3.2" x="15.3" y="0" />
|
|
282
|
+
</svg>
|
|
283
|
+
<svg className="hkm-status-wifi" viewBox="0 0 17 12.3">
|
|
284
|
+
<path d="M8.5 2.6c2.4 0 4.6.9 6.2 2.4.2.2.5.2.7 0l1.2-1.2c.2-.2.2-.5 0-.7C14.5 1.2 11.6 0 8.5 0S2.5 1.2.4 3.1c-.2.2-.2.5 0 .7L1.6 5c.2.2.5.2.7 0C3.9 3.5 6.1 2.6 8.5 2.6Z" />
|
|
285
|
+
<path d="M8.5 6.4c1.3 0 2.5.5 3.5 1.3.2.2.5.2.7 0l1.2-1.2c.2-.2.2-.5 0-.7-1.4-1.3-3.3-2-5.4-2s-4 .7-5.4 2c-.2.2-.2.5 0 .7l1.2 1.2c.2.2.5.2.7 0 1-.8 2.2-1.3 3.5-1.3Z" />
|
|
286
|
+
<path d="M11.1 9.4c.2-.2.2-.5 0-.7-.7-.6-1.6-1-2.6-1s-1.9.4-2.6 1c-.2.2-.2.5 0 .7l2.2 2.2c.2.2.5.2.7 0l2.3-2.2Z" />
|
|
287
|
+
</svg>
|
|
288
|
+
<svg className="hkm-status-battery" viewBox="0 0 27.4 13">
|
|
289
|
+
<rect fill="none" height="12" rx="3.8" stroke="currentColor" strokeOpacity="0.35" width="24" x="0.5" y="0.5" />
|
|
290
|
+
<rect height="9" rx="2.5" width="21" x="2" y="2" />
|
|
291
|
+
<path d="M26 4.4v4.2c.8-.3 1.4-1.1 1.4-2.1S26.8 4.7 26 4.4Z" fillOpacity="0.4" />
|
|
292
|
+
</svg>
|
|
293
|
+
</span>
|
|
294
|
+
</div>
|
|
295
|
+
<span aria-hidden="true" className="hkm-island" />
|
|
296
|
+
<span aria-hidden="true" className="hkm-home-indicator" />
|
|
297
|
+
</div>
|
|
298
|
+
</div>
|
|
299
|
+
</div>
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/** One turn in a coding-agent session. */
|
|
304
|
+
export type AgentTurn = Readonly<{
|
|
305
|
+
role: "user" | "agent" | "tool";
|
|
306
|
+
text: string;
|
|
307
|
+
/** For a tool turn: the tool's neutral name, such as "Run tests" or "Edit file". */
|
|
308
|
+
tool?: string;
|
|
309
|
+
status?: "ok" | "warn" | "error" | "running";
|
|
310
|
+
beat?: string;
|
|
311
|
+
}>;
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* A neutral coding-agent session. `generic-cli` draws a terminal agent with
|
|
315
|
+
* tool-call blocks; `generic-chat` draws a chat panel with a composer. Neither
|
|
316
|
+
* copies a vendor's chrome, name, or mark.
|
|
317
|
+
*/
|
|
318
|
+
export function AgentSession({
|
|
319
|
+
agent,
|
|
320
|
+
fade,
|
|
321
|
+
height,
|
|
322
|
+
title,
|
|
323
|
+
turns,
|
|
324
|
+
...root
|
|
325
|
+
}: MockupRootProps & Readonly<{ agent: "generic-cli" | "generic-chat"; turns: readonly AgentTurn[]; title?: string; height?: number; fade?: boolean }>) {
|
|
326
|
+
if (agent !== "generic-cli" && agent !== "generic-chat") throw new TypeError(`Unknown agent chrome ${JSON.stringify(agent)}.`);
|
|
327
|
+
const fades = fade ?? height !== undefined;
|
|
328
|
+
const sample = optOutProps(root.optOut);
|
|
329
|
+
return (
|
|
330
|
+
<MockupRoot {...root} kind="agent">
|
|
331
|
+
<div className="hkm-window" data-hkm-agent={agent}>
|
|
332
|
+
<div aria-hidden="true" className="hkm-title-bar">
|
|
333
|
+
<WindowLights />
|
|
334
|
+
<span className="hkm-title">{title ?? (agent === "generic-cli" ? "Coding agent" : "Assistant")}</span>
|
|
335
|
+
<span />
|
|
336
|
+
</div>
|
|
337
|
+
<div className="hkm-page hkm-agent-body" data-hkm-fade={fades ? "" : undefined} style={pageStyle(height)}>
|
|
338
|
+
<ol className="hkm-agent-turns">
|
|
339
|
+
{turns.map((turn, index) => (
|
|
340
|
+
<li className="hkm-agent-turn" data-hkm-beat={turn.beat} data-hkm-role={turn.role} data-hkm-tone={turn.status} key={index}>
|
|
341
|
+
{turn.role === "tool" ? (
|
|
342
|
+
<span className="hkm-agent-tool">
|
|
343
|
+
<span className="hkm-agent-tool-name">
|
|
344
|
+
<span className="hkm-agent-status" />
|
|
345
|
+
{turn.tool ?? "Tool"}
|
|
346
|
+
</span>
|
|
347
|
+
<span className="hkm-agent-tool-output"><SampleText {...sample}>{turn.text}</SampleText></span>
|
|
348
|
+
</span>
|
|
349
|
+
) : (
|
|
350
|
+
<>
|
|
351
|
+
<span aria-hidden="true" className="hkm-agent-marker">{turn.role === "user" ? "›" : <MockupGlyph name="sparkle" size={14} />}</span>
|
|
352
|
+
<span className="hkm-agent-text"><SampleText {...sample}>{turn.text}</SampleText></span>
|
|
353
|
+
</>
|
|
354
|
+
)}
|
|
355
|
+
</li>
|
|
356
|
+
))}
|
|
357
|
+
</ol>
|
|
358
|
+
</div>
|
|
359
|
+
{agent === "generic-chat" ? (
|
|
360
|
+
<div aria-hidden="true" className="hkm-agent-composer">
|
|
361
|
+
<span className="hkm-agent-composer-field">Ask for a change</span>
|
|
362
|
+
<span className="hkm-agent-composer-send"><MockupGlyph name="send" size={14} /></span>
|
|
363
|
+
</div>
|
|
364
|
+
) : null}
|
|
365
|
+
</div>
|
|
366
|
+
</MockupRoot>
|
|
367
|
+
);
|
|
368
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code-built mockups: neutral browser, terminal, phone, desktop, chat, feed,
|
|
3
|
+
* inbox, and article frames for product pages, launch posts, and films.
|
|
4
|
+
*
|
|
5
|
+
* Server-safe plain React with one stylesheet, `@hraness/design-kit/mockups.css`.
|
|
6
|
+
* Nothing here uses StyleX, React Aria, hooks, or a client boundary, so a
|
|
7
|
+
* film build can render it with `renderToStaticMarkup` under plain Bun.
|
|
8
|
+
* Interactive shells live in `@hraness/design-kit/mockups/client`.
|
|
9
|
+
*/
|
|
10
|
+
// `export *` rather than named re-exports: Bun 1.3 emitted named re-exports
|
|
11
|
+
// from these side-effect-free modules with no bindings.
|
|
12
|
+
export * from "./core.js";
|
|
13
|
+
export * from "./frames.js";
|
|
14
|
+
export * from "./surfaces.js";
|