fix(studio): refine preset surfaces and repair gallery interactions
This commit is contained in:
1 parent
f642a84c58
commit
bb40dffee8
20 files changed
+345
-72
No files matched your search
@@ -391,7 +391,7 @@
|
|||||||
},
|
},
|
||||||
"packages/design-studio": {
|
"packages/design-studio": {
|
||||||
"name": "@wrnexus/design-studio",
|
"name": "@wrnexus/design-studio",
|
||||||
"version": "0.1.5",
|
"version": "0.1.6",
|
||||||
"bin": {
|
"bin": {
|
||||||
"wrnexus-design-studio": "src/cli.ts",
|
"wrnexus-design-studio": "src/cli.ts",
|
||||||
},
|
},
|
||||||
@@ -409,7 +409,7 @@
|
|||||||
},
|
},
|
||||||
"packages/design-system": {
|
"packages/design-system": {
|
||||||
"name": "@wrnexus/design-system",
|
"name": "@wrnexus/design-system",
|
||||||
"version": "0.1.1",
|
"version": "0.1.2",
|
||||||
},
|
},
|
||||||
"packages/dev-server": {
|
"packages/dev-server": {
|
||||||
"name": "@wrnexus/dev-server",
|
"name": "@wrnexus/dev-server",
|
||||||
@@ -782,7 +782,7 @@
|
|||||||
},
|
},
|
||||||
"packages/ui": {
|
"packages/ui": {
|
||||||
"name": "@wrnexus/ui",
|
"name": "@wrnexus/ui",
|
||||||
"version": "0.8.121",
|
"version": "0.8.122",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@wrnexus/core": "workspace:*",
|
"@wrnexus/core": "workspace:*",
|
||||||
"@wrnexus/design-system": "workspace:*",
|
"@wrnexus/design-system": "workspace:*",
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
"packages/ui/components/Carousel.wrn": "5d8bd031007e7522c81f6acb16538534230cb8ea5fad4eef12b10db5f109c67a",
|
"packages/ui/components/Carousel.wrn": "5d8bd031007e7522c81f6acb16538534230cb8ea5fad4eef12b10db5f109c67a",
|
||||||
"packages/ui/components/Chart.wrn": "d87254752284ba10eb6f36b8654872b0defe84222c01c739c0086809e05dba12",
|
"packages/ui/components/Chart.wrn": "d87254752284ba10eb6f36b8654872b0defe84222c01c739c0086809e05dba12",
|
||||||
"packages/ui/components/ChatBubble.wrn": "f81c2630542b7c8a171acf2c9102a91b64cc06f925a014bc7f96d3778adbafdc",
|
"packages/ui/components/ChatBubble.wrn": "f81c2630542b7c8a171acf2c9102a91b64cc06f925a014bc7f96d3778adbafdc",
|
||||||
"packages/ui/components/Clipboard.wrn": "fcc4e8a3b3a24b599eb6a52337a31d06c2c4dee3b1a000281d324b8b71678f1b",
|
"packages/ui/components/Clipboard.wrn": "0fb6a19dbcd3b20c28154c9482839ff32b221059c2b1125578d3edeb13cbe6ef",
|
||||||
"packages/ui/components/Collapse.wrn": "c5ecae44b455498e5eae527100abdc27bf9e18f7a361ce563d45087c0cc4a3d3",
|
"packages/ui/components/Collapse.wrn": "c5ecae44b455498e5eae527100abdc27bf9e18f7a361ce563d45087c0cc4a3d3",
|
||||||
"packages/ui/components/ColorPicker.wrn": "561b1cef73ff86f50072fefabade212b0cf10a48e6dd6c6aa93fd6e900fe3f50",
|
"packages/ui/components/ColorPicker.wrn": "561b1cef73ff86f50072fefabade212b0cf10a48e6dd6c6aa93fd6e900fe3f50",
|
||||||
"packages/ui/components/Columns.wrn": "c892798d910745453b617a7f0dcf20ab1fd6506095c150de0f36e9fcf7661783",
|
"packages/ui/components/Columns.wrn": "c892798d910745453b617a7f0dcf20ab1fd6506095c150de0f36e9fcf7661783",
|
||||||
@@ -41,7 +41,7 @@
|
|||||||
"packages/ui/components/FileInput.wrn": "76b0719609ff2204cccbad9692a3b820ce63ee98754f2394be6dbd9b8510e873",
|
"packages/ui/components/FileInput.wrn": "76b0719609ff2204cccbad9692a3b820ce63ee98754f2394be6dbd9b8510e873",
|
||||||
"packages/ui/components/FileUploadProgress.wrn": "4f4dbaf9eb165d9aa48742c03ce51d895891bb234f80b2865bce03d4da0e24ba",
|
"packages/ui/components/FileUploadProgress.wrn": "4f4dbaf9eb165d9aa48742c03ce51d895891bb234f80b2865bce03d4da0e24ba",
|
||||||
"packages/ui/components/FlowCanvas.wrn": "8ca776800047ae4f4e1032352f2e17a295c9b8198d48d7ce3fc9ee26c2033ecd",
|
"packages/ui/components/FlowCanvas.wrn": "8ca776800047ae4f4e1032352f2e17a295c9b8198d48d7ce3fc9ee26c2033ecd",
|
||||||
"packages/ui/components/Footer.wrn": "ea931e624eb820beaef698e93191ed707403ea8b4be4117fa5b83d5a6b0b0f55",
|
"packages/ui/components/Footer.wrn": "989bae53515398e24fbea19a3020dcd802d1e58d82d02a9b40a7752eb08a1833",
|
||||||
"packages/ui/components/Grid.wrn": "b44e041a627cf0f62f676b83ca3c0ea2b671e07876f0649310081edefde4217a",
|
"packages/ui/components/Grid.wrn": "b44e041a627cf0f62f676b83ca3c0ea2b671e07876f0649310081edefde4217a",
|
||||||
"packages/ui/components/Hero.wrn": "dbc4cb3099d422edc2a7bc0500499db5c7c67aa272cbe9329a21e6d665aca047",
|
"packages/ui/components/Hero.wrn": "dbc4cb3099d422edc2a7bc0500499db5c7c67aa272cbe9329a21e6d665aca047",
|
||||||
"packages/ui/components/HeroActions.wrn": "f3e633751e1bdfca11d5fe3d4eb96d4aaba11cba298a62794ed3e2201d3ca038",
|
"packages/ui/components/HeroActions.wrn": "f3e633751e1bdfca11d5fe3d4eb96d4aaba11cba298a62794ed3e2201d3ca038",
|
||||||
@@ -52,7 +52,7 @@
|
|||||||
"packages/ui/components/LayoutSplitter.wrn": "394a765f26c6634d4d75cf91b47b2874bee2250f15f399c325b740fc671cb1ad",
|
"packages/ui/components/LayoutSplitter.wrn": "394a765f26c6634d4d75cf91b47b2874bee2250f15f399c325b740fc671cb1ad",
|
||||||
"packages/ui/components/LegendIndicator.wrn": "6d72694259ad877ae987f260887a1d60301f692bf05622d9a36b3a7f02b57dd3",
|
"packages/ui/components/LegendIndicator.wrn": "6d72694259ad877ae987f260887a1d60301f692bf05622d9a36b3a7f02b57dd3",
|
||||||
"packages/ui/components/Link.wrn": "59b7590d68ddbc8c70dd1926ebeed22dbe7b3d0d60613eb74d156633808d9db9",
|
"packages/ui/components/Link.wrn": "59b7590d68ddbc8c70dd1926ebeed22dbe7b3d0d60613eb74d156633808d9db9",
|
||||||
"packages/ui/components/List.wrn": "79ba3e76767a8ffccfc6a25c1d4d13e765cfaf2ef02c961d0490b385fe199036",
|
"packages/ui/components/List.wrn": "14c4690cd600de9035f5a979bd29749eea31059de2f6bef70cd38543fb1130dc",
|
||||||
"packages/ui/components/ListGroup.wrn": "40c1e8f3030fdc41101f65a06ef8752e52a9e88d85f2c85773fa1544bb6527b9",
|
"packages/ui/components/ListGroup.wrn": "40c1e8f3030fdc41101f65a06ef8752e52a9e88d85f2c85773fa1544bb6527b9",
|
||||||
"packages/ui/components/Map.wrn": "aba6b73a89fe69f84a245d6685771cd7f34e813ca4c54d45a46c2e96135ecb70",
|
"packages/ui/components/Map.wrn": "aba6b73a89fe69f84a245d6685771cd7f34e813ca4c54d45a46c2e96135ecb70",
|
||||||
"packages/ui/components/MarketingSectionHeader.wrn": "b05e7fa1341fbd633145a702ab07fe04b163194bd2513f14b572eb63e09348ea",
|
"packages/ui/components/MarketingSectionHeader.wrn": "b05e7fa1341fbd633145a702ab07fe04b163194bd2513f14b572eb63e09348ea",
|
||||||
@@ -67,8 +67,8 @@
|
|||||||
"packages/ui/components/Pagination.wrn": "df6faf0dbafd1249092b45e586159b575c3d37c73d81847376a28c3ea8fdb5cd",
|
"packages/ui/components/Pagination.wrn": "df6faf0dbafd1249092b45e586159b575c3d37c73d81847376a28c3ea8fdb5cd",
|
||||||
"packages/ui/components/PinInput.wrn": "c17b2a1bb38ba0420de7c9179e64fd104cbd8d3b84127a308a4333d46232b3fb",
|
"packages/ui/components/PinInput.wrn": "c17b2a1bb38ba0420de7c9179e64fd104cbd8d3b84127a308a4333d46232b3fb",
|
||||||
"packages/ui/components/Popover.wrn": "aa73c63bde88d6f52061a98fdc8c84f1147e784abfeb802f094a89c061b9a893",
|
"packages/ui/components/Popover.wrn": "aa73c63bde88d6f52061a98fdc8c84f1147e784abfeb802f094a89c061b9a893",
|
||||||
"packages/ui/components/PortalDashboard.wrn": "ef67fdf9faac850ef34b1d2564386d41a0cfa4c754ed0988e85f2e7ea2f50cdb",
|
"packages/ui/components/PortalDashboard.wrn": "342d001689a386533bb4f864e93557460d051466eca010b8aa46ee7586c85cb0",
|
||||||
"packages/ui/components/PreferenceSwitcher.wrn": "33637d758e63a4a6ce5ca51362f34387aacc1f64134434912c7ecd119b7cb5a8",
|
"packages/ui/components/PreferenceSwitcher.wrn": "09a70986d444d5985277e3cbff5d90804ea3136c0b23ee8426daa0c0d6b41351",
|
||||||
"packages/ui/components/PublicPageShell.wrn": "1e28f4f387f235198a723ed13b0128fe484894e4962b73a482a5a28c8acf003f",
|
"packages/ui/components/PublicPageShell.wrn": "1e28f4f387f235198a723ed13b0128fe484894e4962b73a482a5a28c8acf003f",
|
||||||
"packages/ui/components/RangeSlider.wrn": "a4005dcca80e3e23b6488e865b92486b368f46e00d3f05e7b0663d421652be06",
|
"packages/ui/components/RangeSlider.wrn": "a4005dcca80e3e23b6488e865b92486b368f46e00d3f05e7b0663d421652be06",
|
||||||
"packages/ui/components/Rating.wrn": "8456d577da017eac6e54db78b4beced73930da6fe8ef7a5b74c65c2902571ea9",
|
"packages/ui/components/Rating.wrn": "8456d577da017eac6e54db78b4beced73930da6fe8ef7a5b74c65c2902571ea9",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/design-studio",
|
"name": "@wrnexus/design-studio",
|
||||||
"version": "0.1.5",
|
"version": "0.1.6",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { handlePreviewClipboard } from "./preview-clipboard.ts";
|
||||||
import { STUDIO_PRESETS } from "./presets.ts";
|
import { STUDIO_PRESETS } from "./presets.ts";
|
||||||
import { setupGuidance } from "./guidance.ts";
|
import { setupGuidance } from "./guidance.ts";
|
||||||
import { StudioSession, importDesign, exportDesign, reviewDesign } from "./session.ts";
|
import { StudioSession, importDesign, exportDesign, reviewDesign } from "./session.ts";
|
||||||
@@ -307,6 +308,13 @@ async function previews() {
|
|||||||
$("show-draft").hidden = !reviewed;
|
$("show-draft").hidden = !reviewed;
|
||||||
}
|
}
|
||||||
window.addEventListener("message", (event) => {
|
window.addEventListener("message", (event) => {
|
||||||
|
void handlePreviewClipboard(
|
||||||
|
event,
|
||||||
|
[$<HTMLIFrameElement>("preview-frame"), $<HTMLIFrameElement>("compare-frame")],
|
||||||
|
(text) => navigator.clipboard.writeText(text),
|
||||||
|
navigator.userActivation?.isActive ?? false,
|
||||||
|
);
|
||||||
|
|
||||||
const frame = $<HTMLIFrameElement>("preview-frame");
|
const frame = $<HTMLIFrameElement>("preview-frame");
|
||||||
if (
|
if (
|
||||||
event.source === frame.contentWindow &&
|
event.source === frame.contentWindow &&
|
||||||
|
|||||||
@@ -81,8 +81,44 @@ export function gallerySample(name: string) {
|
|||||||
],
|
],
|
||||||
palette: [{ kind: "task", label: "Add task" }],
|
palette: [{ kind: "task", label: "Add task" }],
|
||||||
});
|
});
|
||||||
if (name === "Toaster")
|
if (name === "Toaster") {
|
||||||
content = '<button type="button" data-preview-toast>Show sample notification</button>';
|
props.max = 1;
|
||||||
|
content =
|
||||||
|
'<button type="button" class="preview-action" data-preview-toast>Show sample notification</button>';
|
||||||
|
}
|
||||||
|
if (name === "PreferenceSwitcher")
|
||||||
|
Object.assign(props, {
|
||||||
|
themeLabel: "Theme",
|
||||||
|
colorLabel: "Accent color",
|
||||||
|
languageLabel: "Language",
|
||||||
|
});
|
||||||
|
if (name === "Tooltip")
|
||||||
|
Object.assign(props, {
|
||||||
|
variant: "soft",
|
||||||
|
title: "Helpful information",
|
||||||
|
description: "Hover or focus a control to see a short explanation.",
|
||||||
|
});
|
||||||
|
if (name === "PortalDashboard")
|
||||||
|
Object.assign(props, {
|
||||||
|
title: "Sample service dashboard",
|
||||||
|
description: "Sample tasks, shortcuts and recent activity.",
|
||||||
|
tasks: [
|
||||||
|
{
|
||||||
|
label: "Review submitted documents",
|
||||||
|
detail: "Confirm the required attachments before approval.",
|
||||||
|
status: "urgent",
|
||||||
|
meta: "Due today",
|
||||||
|
href: "#review",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Update contact information",
|
||||||
|
detail: "Check the applicant’s preferred email address.",
|
||||||
|
status: "warning",
|
||||||
|
meta: "In progress",
|
||||||
|
href: "#contact",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
if (name === "BackToTop") {
|
if (name === "BackToTop") {
|
||||||
props.threshold = 0;
|
props.threshold = 0;
|
||||||
props.alwaysVisible = true;
|
props.alwaysVisible = true;
|
||||||
|
|||||||
@@ -24,9 +24,20 @@ function palette(tokens: TokenMap, values: Palette) {
|
|||||||
"text-subtle": subtle,
|
"text-subtle": subtle,
|
||||||
"border-strong": border,
|
"border-strong": border,
|
||||||
"primary-muted": muted,
|
"primary-muted": muted,
|
||||||
|
muted: subtle,
|
||||||
|
focus: primary,
|
||||||
|
"input-background": surface,
|
||||||
|
"input-border": border,
|
||||||
|
"code-background": muted,
|
||||||
|
"code-text": text,
|
||||||
|
"code-border": border,
|
||||||
};
|
};
|
||||||
for (const [name, value] of Object.entries(colors))
|
for (const [name, value] of Object.entries(colors))
|
||||||
tokens[`color.${name}`] = { ...tokens[`color.${name}`]!, value };
|
tokens[`color.${name}`] = {
|
||||||
|
type: "color",
|
||||||
|
legacy: tokens[`color.${name}`]?.legacy ?? `color-${name}`,
|
||||||
|
value,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
function direction(
|
function direction(
|
||||||
id: string,
|
id: string,
|
||||||
@@ -70,10 +81,10 @@ export const STUDIO_PRESETS: Record<string, DesignSystem> = {
|
|||||||
forest: direction(
|
forest: direction(
|
||||||
"forest",
|
"forest",
|
||||||
"Forest / Sage",
|
"Forest / Sage",
|
||||||
["#E8DCC7", "#D4B895", "#C7CCB5", "#283320", "#48543E", "#7B866A", "#43552D", "#E8DCC7"],
|
["#E5EBDD", "#F4F6EE", "#D5DFC8", "#233827", "#4C604C", "#9BAE91", "#355E40", "#FFFFFF"],
|
||||||
["#20291C", "#2C3726", "#394530", "#E8DCC7", "#BDC5A5", "#718060", "#B7D092", "#20291C"],
|
["#20291C", "#2C3726", "#394530", "#E8DCC7", "#BDC5A5", "#718060", "#B7D092", "#20291C"],
|
||||||
"24px",
|
"10px",
|
||||||
serif,
|
sans,
|
||||||
),
|
),
|
||||||
ocean: direction(
|
ocean: direction(
|
||||||
"ocean",
|
"ocean",
|
||||||
@@ -103,10 +114,10 @@ export const STUDIO_PRESETS: Record<string, DesignSystem> = {
|
|||||||
ember: direction(
|
ember: direction(
|
||||||
"ember",
|
"ember",
|
||||||
"Ember / Terracotta",
|
"Ember / Terracotta",
|
||||||
["#E8DCC7", "#D4B895", "#CEB094", "#3D241C", "#694837", "#A7795A", "#8E3C21", "#E8DCC7"],
|
["#F5E3D5", "#FFF9F3", "#EBCDB7", "#48271D", "#775342", "#C59C81", "#A34525", "#FFFFFF"],
|
||||||
["#2B1B15", "#3B2820", "#50362B", "#E8DCC7", "#CEB094", "#9C7057", "#F4AE80", "#2B1B15"],
|
["#2B1B15", "#3B2820", "#50362B", "#E8DCC7", "#CEB094", "#9C7057", "#F4AE80", "#2B1B15"],
|
||||||
"16px",
|
"6px",
|
||||||
serif,
|
sans,
|
||||||
),
|
),
|
||||||
editorial: direction(
|
editorial: direction(
|
||||||
"editorial",
|
"editorial",
|
||||||
@@ -129,18 +140,44 @@ export const STUDIO_PRESETS: Record<string, DesignSystem> = {
|
|||||||
brutalist: direction(
|
brutalist: direction(
|
||||||
"brutalist",
|
"brutalist",
|
||||||
"Brutalist / Primary",
|
"Brutalist / Primary",
|
||||||
["#FFFFFF", "#FFFF00", "#FFFFFF", "#000000", "#000000", "#000000", "#0000FF", "#FFFFFF"],
|
["#FFFFFF", "#FFFFFF", "#F0F0F0", "#000000", "#333333", "#000000", "#0000FF", "#FFFFFF"],
|
||||||
["#000000", "#000000", "#202020", "#FFFFFF", "#FFFFFF", "#FFFFFF", "#FFFF00", "#000000"],
|
["#000000", "#000000", "#202020", "#FFFFFF", "#FFFFFF", "#FFFFFF", "#FFFF00", "#000000"],
|
||||||
"0",
|
"0",
|
||||||
mono,
|
sans,
|
||||||
"4px 4px 0 #000000",
|
"2px 2px 0 #000000",
|
||||||
),
|
),
|
||||||
contrast: direction(
|
contrast: direction(
|
||||||
"contrast",
|
"contrast",
|
||||||
"Contrast / Clear",
|
"Contrast / Clear",
|
||||||
["#FFFFFF", "#FFFFFF", "#E6E6E6", "#000000", "#333333", "#000000", "#002FA7", "#FFFFFF"],
|
["#FFFFFF", "#FFFFFF", "#EDF1F6", "#101828", "#344054", "#667085", "#173EA5", "#FFFFFF"],
|
||||||
["#000000", "#000000", "#1B1B1B", "#FFFFFF", "#E0E0E0", "#FFFFFF", "#FFFF00", "#000000"],
|
["#0D1117", "#161D29", "#253044", "#FFFFFF", "#D0D8E8", "#8999B2", "#A5C8FF", "#0D1117"],
|
||||||
"4px",
|
"8px",
|
||||||
sans,
|
sans,
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Neutral editorial status colors retain textual and icon cues without stray hues.
|
||||||
|
for (const tokens of [
|
||||||
|
STUDIO_PRESETS.editorial!.tokens,
|
||||||
|
STUDIO_PRESETS.editorial!.modes!.light!,
|
||||||
|
STUDIO_PRESETS.editorial!.modes!.dark!,
|
||||||
|
]) {
|
||||||
|
for (const name of ["secondary", "success", "warning", "danger", "info"]) {
|
||||||
|
for (const suffix of ["", "-hover", "-active", "-text"])
|
||||||
|
tokens[`color.${name}${suffix}`] = {
|
||||||
|
type: "color",
|
||||||
|
value: tokens["color.primary"]!.value,
|
||||||
|
legacy: `color-${name}${suffix}`,
|
||||||
|
};
|
||||||
|
tokens[`color.${name}-soft`] = {
|
||||||
|
type: "color",
|
||||||
|
value: tokens["color.surface-2"]!.value,
|
||||||
|
legacy: `color-${name}-soft`,
|
||||||
|
};
|
||||||
|
tokens[`color.${name}-contrast`] = {
|
||||||
|
type: "color",
|
||||||
|
value: tokens["color.primary-contrast"]!.value,
|
||||||
|
legacy: `color-${name}-contrast`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
/** The sandbox can request a bounded text copy, never read clipboard contents. */
|
||||||
|
export async function handlePreviewClipboard(
|
||||||
|
event: Pick<MessageEvent, "source" | "data">,
|
||||||
|
frames: Pick<HTMLIFrameElement, "contentWindow" | "src">[],
|
||||||
|
writeText: (text: string) => Promise<void>,
|
||||||
|
activated = true,
|
||||||
|
) {
|
||||||
|
const data = event.data;
|
||||||
|
const frame = frames.find((frame) => frame.contentWindow === event.source);
|
||||||
|
if (
|
||||||
|
!frame ||
|
||||||
|
!data ||
|
||||||
|
data.type !== "wrn-studio-copy" ||
|
||||||
|
typeof data.id !== "string" ||
|
||||||
|
!/^[a-z0-9-]{1,80}$/.test(data.id) ||
|
||||||
|
data.path !== new URL(frame.src).pathname ||
|
||||||
|
typeof data.text !== "string" ||
|
||||||
|
data.text.length > 16384
|
||||||
|
)
|
||||||
|
return false;
|
||||||
|
let ok = false;
|
||||||
|
try {
|
||||||
|
if (!activated) throw Error("Copy requires a user gesture.");
|
||||||
|
await writeText(data.text);
|
||||||
|
ok = true;
|
||||||
|
} catch {
|
||||||
|
/* The control reports failure and keeps the value selectable. */
|
||||||
|
}
|
||||||
|
frame.contentWindow?.postMessage({ type: "wrn-studio-copy-result", id: data.id, ok }, "*");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Runs only in the generated trusted preview, with an opaque sandbox origin. */
|
||||||
|
export const previewClipboardRuntime = `
|
||||||
|
if (parent !== window) {
|
||||||
|
const copies = new Map();
|
||||||
|
window.addEventListener('message', event => {
|
||||||
|
if (event.source !== parent || event.data?.type !== 'wrn-studio-copy-result') return;
|
||||||
|
const pending = copies.get(event.data.id);
|
||||||
|
if (!pending) return;
|
||||||
|
clearTimeout(pending.timer); copies.delete(event.data.id);
|
||||||
|
event.data.ok ? pending.resolve() : pending.reject(new Error('Clipboard copy was denied'));
|
||||||
|
});
|
||||||
|
window.wrnClipboard = {writeText(text) {
|
||||||
|
return new Promise((resolve,reject) => {
|
||||||
|
const id = crypto.randomUUID();
|
||||||
|
const timer = setTimeout(() => { copies.delete(id); reject(new Error('Clipboard copy timed out')); }, 4000);
|
||||||
|
copies.set(id, {resolve,reject,timer});
|
||||||
|
parent.postMessage({type:'wrn-studio-copy',id,path:location.pathname,text:String(text)}, '*');
|
||||||
|
});
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
`;
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { previewClipboardRuntime } from "./preview-clipboard.ts";
|
||||||
import { validateAgentPage } from "./agent-pages.ts";
|
import { validateAgentPage } from "./agent-pages.ts";
|
||||||
import { readFileSync, mkdirSync, writeFileSync } from "node:fs";
|
import { readFileSync, mkdirSync, writeFileSync } from "node:fs";
|
||||||
import { tmpdir } from "node:os";
|
import { tmpdir } from "node:os";
|
||||||
@@ -174,6 +175,7 @@ export async function previewDocument(
|
|||||||
}
|
}
|
||||||
const runtime =
|
const runtime =
|
||||||
bindings +
|
bindings +
|
||||||
|
previewClipboardRuntime +
|
||||||
REACTIVE_RUNTIME +
|
REACTIVE_RUNTIME +
|
||||||
providers +
|
providers +
|
||||||
"\ndocument.addEventListener('DOMContentLoaded',()=>parent.postMessage({type:'wrn-studio-preview-ready',path:location.pathname},'*'),{once:true});";
|
"\ndocument.addEventListener('DOMContentLoaded',()=>parent.postMessage({type:'wrn-studio-preview-ready',path:location.pathname},'*'),{once:true});";
|
||||||
@@ -181,9 +183,9 @@ export async function previewDocument(
|
|||||||
meta: { title: system.name + " preview" },
|
meta: { title: system.name + " preview" },
|
||||||
body,
|
body,
|
||||||
htmlAttrs: ` dir="${options.direction === "rtl" ? "rtl" : "ltr"}" data-theme="${escape(mode)}"`,
|
htmlAttrs: ` dir="${options.direction === "rtl" ? "rtl" : "ltr"}" data-theme="${escape(mode)}"`,
|
||||||
extraHead: `<style>${uiCss()}${renderThemeCss(theme)}${previewIconCss(body + galleryIcons + [...names].map((name) => iconSources.get(name) ?? "").join(""))}${providerCss}*{box-sizing:border-box}body{padding:24px;margin:0;font-family:var(--wrn-font-sans);color:var(--wrn-color-text);background:var(--wrn-color-bg);line-height:1.5}h1{font-size:28px}h2{font-size:18px}.layout{display:grid;grid-template-columns:minmax(0,1.2fr) minmax(0,1fr);gap:24px}.layout>*,form{min-width:0;display:grid;gap:16px;align-content:start}.actions{display:flex;gap:12px;flex-wrap:wrap;align-items:center}.gallery{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,300px),1fr));gap:20px}.sample{min-width:0;border:1px solid var(--wrn-color-border);padding:20px;align-self:start}.sample-stage{min-width:0;position:relative}.sample-hint{font-size:12px;color:var(--wrn-color-text-muted);margin:0 0 16px}.sample-event{display:block;font-size:12px;min-height:1.5em;margin-top:12px}.sample canvas{max-width:100%}.sample [data-ui-component="BackToTop"]{position:static;inset:auto}.sample .showcase-slot,.sample .showcase-visual-panel{padding:20px;border:1px dashed var(--wrn-color-border);background:var(--wrn-color-surface-2)}.sample [data-gallery-component="DeviceFrame"]{min-width:0}.sample[data-gallery-component="Confetti"] .sample-stage{overflow:clip;padding:60px 16px;min-height:180px}.sample:is([data-gallery-component="SplitHero"],[data-gallery-component="FeatureGrid"],[data-gallery-component="MetricGrid"],[data-gallery-component="AnnouncementBar"],[data-gallery-component="CTASection"],[data-gallery-component="Chart"],[data-gallery-component="DataTable"],[data-gallery-component="DrawflowCanvas"],[data-gallery-component="FlowCanvas"],[data-gallery-component="Hero"],[data-gallery-component="Navbar"],[data-gallery-component="PortalDashboard"],[data-gallery-component="PublicPageShell"],[data-gallery-component="AuthSplitLayout"]){grid-column:1/-1}@media(max-width:650px){.layout{grid-template-columns:1fr}}@media(prefers-reduced-motion:reduce){*,*::before,*::after{animation-duration:.01ms!important;transition-duration:.01ms!important}}</style>`,
|
extraHead: `<style>${uiCss()}${renderThemeCss(theme)}${previewIconCss(body + galleryIcons + [...names].map((name) => iconSources.get(name) ?? "").join(""))}${providerCss}*{box-sizing:border-box}body{padding:24px;margin:0;font-family:var(--wrn-font-sans);color:var(--wrn-color-text);background:var(--wrn-color-bg);line-height:1.5}h1{font-size:28px}h2{font-size:18px}.layout{display:grid;grid-template-columns:minmax(0,1.2fr) minmax(0,1fr);gap:24px}.layout>*,form{min-width:0;display:grid;gap:16px;align-content:start}.actions{display:flex;gap:12px;flex-wrap:wrap;align-items:center}.gallery{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,300px),1fr));gap:20px}.sample{min-width:0;border:1px solid var(--wrn-color-border);padding:20px;align-self:start}.sample-stage{min-width:0;position:relative}.preview-action,.showcase-action{display:inline-flex;align-items:center;justify-content:center;min-height:40px;padding:8px 16px;border:1px solid var(--wrn-color-primary);border-radius:var(--wrn-radius-sm);background:var(--wrn-color-primary);color:var(--wrn-color-primary-contrast);font:inherit;font-size:14px;font-weight:600;cursor:pointer}.preview-action:focus-visible,.showcase-action:focus-visible{outline:2px solid var(--wrn-color-focus);outline-offset:3px}.sample-hint{font-size:12px;color:var(--wrn-color-text-muted);margin:0 0 16px}.sample-event{display:block;font-size:12px;min-height:1.5em;margin-top:12px}.sample canvas{max-width:100%}.sample [data-ui-component="BackToTop"]{position:static;inset:auto}.sample .showcase-slot,.sample .showcase-visual-panel{padding:20px;border:1px dashed var(--wrn-color-border);background:var(--wrn-color-surface-2)}.sample [data-gallery-component="DeviceFrame"]{min-width:0}.sample[data-gallery-component="Confetti"] .sample-stage{overflow:clip;padding:60px 16px;min-height:180px}.sample:is([data-gallery-component="Footer"],[data-gallery-component="SplitHero"],[data-gallery-component="FeatureGrid"],[data-gallery-component="MetricGrid"],[data-gallery-component="AnnouncementBar"],[data-gallery-component="CTASection"],[data-gallery-component="Chart"],[data-gallery-component="DataTable"],[data-gallery-component="DrawflowCanvas"],[data-gallery-component="FlowCanvas"],[data-gallery-component="Hero"],[data-gallery-component="Navbar"],[data-gallery-component="PortalDashboard"],[data-gallery-component="PublicPageShell"],[data-gallery-component="AuthSplitLayout"]){grid-column:1/-1}@media(max-width:650px){.layout{grid-template-columns:1fr}}@media(prefers-reduced-motion:reduce){*,*::before,*::after{animation-duration:.01ms!important;transition-duration:.01ms!important}}</style>`,
|
||||||
componentRecipeCss: theme.recipeCss,
|
componentRecipeCss: theme.recipeCss,
|
||||||
extraBody: `<script>${runtime.replace(/<\/script/gi, "<\\/script")}\ndocument.addEventListener('submit',e=>e.preventDefault());document.addEventListener('click',e=>{const link=e.target.closest('a[href]');if(link){e.preventDefault();const card=link.closest('[data-gallery-component]');if(card)card.querySelector('.sample-event').textContent='Preview link: '+link.getAttribute('href');const href=link.getAttribute('href');if(href?.startsWith('#'))document.getElementById(href.slice(1))?.scrollIntoView({block:'nearest'});}if(e.target.closest('[data-preview-toast]'))window.toast.success('This is a sample notification.',{title:'Preview notification'});});document.querySelectorAll('[data-gallery-component]').forEach(card=>{['input','change','select','open','close','toggle','start','complete','add','connect','move'].forEach(name=>card.addEventListener(name,()=>{card.querySelector('.sample-event').textContent='Last event: '+name;}));});</script>`,
|
extraBody: `<script>${runtime.replace(/<\/script/gi, "<\\/script")}\ndocument.addEventListener('submit',e=>e.preventDefault());document.addEventListener('click',e=>{const link=e.target.closest('a[href]');if(link){e.preventDefault();const card=link.closest('[data-gallery-component]');if(card)card.querySelector('.sample-event').textContent='Preview link: '+link.getAttribute('href');const href=link.getAttribute('href');if(href?.startsWith('#'))document.getElementById(href.slice(1))?.scrollIntoView({block:'nearest'});}if(e.target.closest('[data-preview-toast]'))window.toast('This is a sample notification.',{title:'Preview notification',duration:3500});});document.querySelectorAll('[data-gallery-component]').forEach(card=>{['input','change','select','open','close','toggle','start','complete','add','connect','move'].forEach(name=>card.addEventListener(name,()=>{card.querySelector('.sample-event').textContent='Last event: '+name;}));});</script>`,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
import { expect, test } from "bun:test";
|
||||||
|
import { handlePreviewClipboard } from "../src/preview-clipboard.ts";
|
||||||
|
|
||||||
|
test("clipboard broker accepts only current preview frames and bounded user-initiated text", async () => {
|
||||||
|
const results: unknown[] = [];
|
||||||
|
const source = { postMessage: (value: unknown) => results.push(value) } as unknown as Window;
|
||||||
|
const frames = [{ contentWindow: source, src: "https://example.test/preview/current" }];
|
||||||
|
const event = {
|
||||||
|
source,
|
||||||
|
data: {
|
||||||
|
type: "wrn-studio-copy",
|
||||||
|
id: "copy-1",
|
||||||
|
path: "/preview/current",
|
||||||
|
text: "npm install @wrnexus/ui",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const copied: string[] = [];
|
||||||
|
const write = async (text: string) => {
|
||||||
|
copied.push(text);
|
||||||
|
};
|
||||||
|
expect(await handlePreviewClipboard(event, frames, write)).toBe(true);
|
||||||
|
expect(copied).toEqual(["npm install @wrnexus/ui"]);
|
||||||
|
expect(results).toEqual([{ type: "wrn-studio-copy-result", id: "copy-1", ok: true }]);
|
||||||
|
expect(await handlePreviewClipboard({ ...event, source: null }, frames, write)).toBe(false);
|
||||||
|
expect(
|
||||||
|
await handlePreviewClipboard(
|
||||||
|
{ ...event, data: { ...event.data, path: "/preview/old" } },
|
||||||
|
frames,
|
||||||
|
write,
|
||||||
|
),
|
||||||
|
).toBe(false);
|
||||||
|
expect(
|
||||||
|
await handlePreviewClipboard(
|
||||||
|
{ ...event, data: { ...event.data, text: "x".repeat(16385) } },
|
||||||
|
frames,
|
||||||
|
write,
|
||||||
|
),
|
||||||
|
).toBe(false);
|
||||||
|
expect(await handlePreviewClipboard(event, frames, write, false)).toBe(true);
|
||||||
|
expect(copied).toHaveLength(1);
|
||||||
|
expect(results.at(-1)).toEqual({ type: "wrn-studio-copy-result", id: "copy-1", ok: false });
|
||||||
|
});
|
||||||
|
|
||||||
|
test("clipboard denial reports failure rather than claiming a copy succeeded", async () => {
|
||||||
|
let result: any;
|
||||||
|
const source = {
|
||||||
|
postMessage: (value: unknown) => {
|
||||||
|
result = value;
|
||||||
|
},
|
||||||
|
} as unknown as Window;
|
||||||
|
await handlePreviewClipboard(
|
||||||
|
{
|
||||||
|
source,
|
||||||
|
data: { type: "wrn-studio-copy", id: "copy-2", path: "/preview/current", text: "test" },
|
||||||
|
},
|
||||||
|
[{ contentWindow: source, src: "https://example.test/preview/current" }],
|
||||||
|
async () => {
|
||||||
|
throw Error("denied");
|
||||||
|
},
|
||||||
|
);
|
||||||
|
expect(result.ok).toBe(false);
|
||||||
|
});
|
||||||
@@ -33,3 +33,18 @@ test("choosing a new preset stays undoable and never mutates the catalog", () =>
|
|||||||
session.undo();
|
session.undo();
|
||||||
expect(session.valid.id).toBe("square");
|
expect(session.valid.id).toBe("square");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("revised directions stay distinct and Editorial status tokens remain monochrome", () => {
|
||||||
|
const organic = ["soft", "forest", "ember"].map((id) => STUDIO_PRESETS[id]!);
|
||||||
|
expect(new Set(organic.map((s) => s.modes!.light!["color.bg"]!.value)).size).toBe(3);
|
||||||
|
expect(new Set(organic.map((s) => s.tokens["shape.control"]!.value)).size).toBe(3);
|
||||||
|
for (const tokens of [
|
||||||
|
STUDIO_PRESETS.editorial!.tokens,
|
||||||
|
...Object.values(STUDIO_PRESETS.editorial!.modes!),
|
||||||
|
]) {
|
||||||
|
for (const name of ["success", "warning", "danger", "info"]) {
|
||||||
|
const color = String(tokens[`color.${name}`]!.value);
|
||||||
|
expect(color).toMatch(/^#([0-9a-f]{2})\1\1$/i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/design-system",
|
"name": "@wrnexus/design-system",
|
||||||
"version": "0.1.1",
|
"version": "0.1.2",
|
||||||
"description": "Typed, deterministic design tokens and legacy WrNexus theme integration.",
|
"description": "Typed, deterministic design tokens and legacy WrNexus theme integration.",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
|
|||||||
@@ -23,18 +23,35 @@ export function catalogRecipes(): Record<string, ComponentRecipe> {
|
|||||||
const circle = ["circle", "round"].includes(axes.shape?.default ?? "");
|
const circle = ["circle", "round"].includes(axes.shape?.default ?? "");
|
||||||
const semanticGeometry =
|
const semanticGeometry =
|
||||||
/thumb|track|spinner|progress|marker|port$|status|dot|star|srOnly/i.test(part);
|
/thumb|track|spinner|progress|marker|port$|status|dot|star|srOnly/i.test(part);
|
||||||
|
// Joined surfaces share the outer component's corners. Rounding each
|
||||||
|
// child creates seams, scalloped panels and rounded splitter rails.
|
||||||
|
const joined = (
|
||||||
|
{
|
||||||
|
StatsBar: ["item", "content"],
|
||||||
|
AuthSplitLayout: ["content"],
|
||||||
|
Collapse: ["trigger", "panel", "content"],
|
||||||
|
InputNumber: ["button", "content", "input"],
|
||||||
|
LayoutSplitter: ["handle"],
|
||||||
|
} as Record<string, string[]>
|
||||||
|
)[name]?.includes(part);
|
||||||
if (
|
if (
|
||||||
descriptor.properties.includes("borderRadius") &&
|
descriptor.properties.includes("borderRadius") &&
|
||||||
!roundedFalse &&
|
!roundedFalse &&
|
||||||
!circle &&
|
!circle &&
|
||||||
!semanticGeometry
|
!semanticGeometry &&
|
||||||
|
!joined
|
||||||
) {
|
) {
|
||||||
styles.borderRadius = ref(
|
styles.borderRadius = ref(
|
||||||
descriptor.profile === "control" ? "shape.control" : "shape.panel",
|
descriptor.profile === "control" ? "shape.control" : "shape.panel",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// Only existing surfaces receive elevation. Layout wrappers and provider hosts are untouched.
|
// Only existing surfaces receive elevation. Layout wrappers and provider hosts are untouched.
|
||||||
if (descriptor.profile === "surface" && descriptor.properties.includes("boxShadow"))
|
if (
|
||||||
|
descriptor.profile === "surface" &&
|
||||||
|
descriptor.properties.includes("boxShadow") &&
|
||||||
|
!joined &&
|
||||||
|
!(name === "PreferenceSwitcher" && part === "menu")
|
||||||
|
)
|
||||||
styles.boxShadow = ref("elevation.panel");
|
styles.boxShadow = ref("elevation.panel");
|
||||||
if (Object.keys(styles).length) base[part] = styles;
|
if (Object.keys(styles).length) base[part] = styles;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ function recipes(angular: boolean, outlined: boolean): Record<string, ComponentR
|
|||||||
paddingInline: ref("space.control"),
|
paddingInline: ref("space.control"),
|
||||||
boxShadow: ref("elevation.panel"),
|
boxShadow: ref("elevation.panel"),
|
||||||
fontFamily: ref("type.body"),
|
fontFamily: ref("type.body"),
|
||||||
...(angular ? { rotate: "-2deg" } : {}),
|
...(angular ? { rotate: "-0.5deg" } : {}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
variants: [
|
variants: [
|
||||||
@@ -84,7 +84,7 @@ function recipes(angular: boolean, outlined: boolean): Record<string, ComponentR
|
|||||||
Radio: recipe({ control: { borderWidth: "1px" } }),
|
Radio: recipe({ control: { borderWidth: "1px" } }),
|
||||||
Switch: recipe({ control: { borderRadius: "999px" } }),
|
Switch: recipe({ control: { borderRadius: "999px" } }),
|
||||||
Card: recipe({
|
Card: recipe({
|
||||||
panel: { ...surface, ...(angular ? { rotate: "1deg" } : {}) },
|
panel: { ...surface, ...(angular ? { rotate: "0.25deg" } : {}) },
|
||||||
body: { paddingInline: ref("space.panel"), paddingBlock: ref("space.panel") },
|
body: { paddingInline: ref("space.panel"), paddingBlock: ref("space.panel") },
|
||||||
heading: { fontFamily: ref("type.heading") },
|
heading: { fontFamily: ref("type.heading") },
|
||||||
}),
|
}),
|
||||||
@@ -132,20 +132,20 @@ function preset(id: string, name: string, soft: boolean, angular: boolean): Desi
|
|||||||
}
|
}
|
||||||
: angular
|
: angular
|
||||||
? {
|
? {
|
||||||
bg: "#E8E0C0",
|
bg: "#F3ECD7",
|
||||||
background: "#E8E0C0",
|
background: "#F3ECD7",
|
||||||
surface: "#EDE4CF",
|
surface: "#FFFBEF",
|
||||||
"surface-2": "#E8E0C0",
|
"surface-2": "#E8DFC6",
|
||||||
text: "#232320",
|
text: "#232320",
|
||||||
foreground: "#232320",
|
foreground: "#232320",
|
||||||
"text-muted": "#535146",
|
"text-muted": "#535146",
|
||||||
border: "#535146",
|
border: "#B9AC8D",
|
||||||
primary: "#8F2448",
|
primary: "#314B6B",
|
||||||
"primary-hover": "#701C39",
|
"primary-hover": "#243B57",
|
||||||
"primary-active": "#59172D",
|
"primary-active": "#1C2F46",
|
||||||
"primary-contrast": "#FFFFFF",
|
"primary-contrast": "#FFFFFF",
|
||||||
"primary-text": "#701C39",
|
"primary-text": "#243B57",
|
||||||
"primary-soft": "#E8E0C0",
|
"primary-soft": "#E8DFC6",
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
bg: "#FFFFFF",
|
bg: "#FFFFFF",
|
||||||
@@ -190,11 +190,11 @@ function preset(id: string, name: string, soft: boolean, angular: boolean): Desi
|
|||||||
foreground: "#EDE4CF",
|
foreground: "#EDE4CF",
|
||||||
"text-muted": "#D0C7B2",
|
"text-muted": "#D0C7B2",
|
||||||
border: "#B0A78D",
|
border: "#B0A78D",
|
||||||
primary: "#FF9FC6",
|
primary: "#B9CFE8",
|
||||||
"primary-hover": "#FFC0DA",
|
"primary-hover": "#CEE0F5",
|
||||||
"primary-active": "#FFD2E5",
|
"primary-active": "#DDEBFA",
|
||||||
"primary-contrast": "#25221E",
|
"primary-contrast": "#25221E",
|
||||||
"primary-text": "#FF9FC6",
|
"primary-text": "#B9CFE8",
|
||||||
"primary-soft": "#454032",
|
"primary-soft": "#454032",
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
@@ -224,7 +224,7 @@ function preset(id: string, name: string, soft: boolean, angular: boolean): Desi
|
|||||||
"control.height": token("dimension", soft ? "2.75rem" : "2.5rem", "control-height"),
|
"control.height": token("dimension", soft ? "2.75rem" : "2.5rem", "control-height"),
|
||||||
"type.body": token("fontFamily", body, "font-sans"),
|
"type.body": token("fontFamily", body, "font-sans"),
|
||||||
"type.heading": token("fontFamily", heading),
|
"type.heading": token("fontFamily", heading),
|
||||||
"elevation.panel": token("shadow", angular ? "3px 3px 0 #535146" : "none", "shadow-1"),
|
"elevation.panel": token("shadow", angular ? "2px 2px 0 #535146" : "none", "shadow-1"),
|
||||||
...colors(dark),
|
...colors(dark),
|
||||||
};
|
};
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -108,3 +108,20 @@ test("mode-dependent recipe values retain mode rules while identical values are
|
|||||||
expect(css).toContain("9px");
|
expect(css).toContain("9px");
|
||||||
expect(css).not.toContain('[data-theme="light"]');
|
expect(css).not.toContain('[data-theme="light"]');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("joined component parts keep their authored geometry instead of receiving independent rounding", () => {
|
||||||
|
for (const system of Object.values(CORE_DESIGN_SYSTEMS)) {
|
||||||
|
for (const [name, parts] of Object.entries({
|
||||||
|
StatsBar: ["item", "content"],
|
||||||
|
AuthSplitLayout: ["content"],
|
||||||
|
Collapse: ["trigger", "panel", "content"],
|
||||||
|
InputNumber: ["button", "input", "content"],
|
||||||
|
LayoutSplitter: ["handle"],
|
||||||
|
})) {
|
||||||
|
for (const part of parts) {
|
||||||
|
expect(system.recipes![name]!.base?.[part]?.borderRadius).toBeUndefined();
|
||||||
|
expect(system.recipes![name]!.base?.[part]?.boxShadow).toBeUndefined();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -40,6 +40,9 @@ component Clipboard {
|
|||||||
}
|
}
|
||||||
status = "copying"
|
status = "copying"
|
||||||
output.copy({ value: value, sourceEvent: sourceEvent })
|
output.copy({ value: value, sourceEvent: sourceEvent })
|
||||||
|
if (window.wrnClipboard && window.wrnClipboard.writeText) {
|
||||||
|
return window.wrnClipboard.writeText(value).then(copySucceeded).catch(copyFailed)
|
||||||
|
}
|
||||||
if (!window.navigator.clipboard || !window.navigator.clipboard.writeText) {
|
if (!window.navigator.clipboard || !window.navigator.clipboard.writeText) {
|
||||||
copyFailed(new Error("Clipboard API is unavailable"))
|
copyFailed(new Error("Clipboard API is unavailable"))
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -47,10 +47,7 @@ component Footer {
|
|||||||
<div
|
<div
|
||||||
class="wrn-footer__pre"
|
class="wrn-footer__pre"
|
||||||
data-wrn-owner="Footer" data-wrn-part="pre" data-wrn-axis-size="{size}" data-wrn-axis-color="{color}">
|
data-wrn-owner="Footer" data-wrn-part="pre" data-wrn-axis-size="{size}" data-wrn-axis-color="{color}">
|
||||||
<slot
|
<slot name="pre-footer"></slot>
|
||||||
name="pre-footer"
|
|
||||||
>
|
|
||||||
</slot>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if items.length > 0}
|
{#if items.length > 0}
|
||||||
@@ -260,10 +257,7 @@ component Footer {
|
|||||||
<div
|
<div
|
||||||
class="wrn-footer__post"
|
class="wrn-footer__post"
|
||||||
data-wrn-owner="Footer" data-wrn-part="post" data-wrn-axis-size="{size}" data-wrn-axis-color="{color}">
|
data-wrn-owner="Footer" data-wrn-part="post" data-wrn-axis-size="{size}" data-wrn-axis-color="{color}">
|
||||||
<slot
|
<slot name="post-footer"></slot>
|
||||||
name="post-footer"
|
|
||||||
>
|
|
||||||
</slot>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -273,10 +267,7 @@ component Footer {
|
|||||||
<div
|
<div
|
||||||
class="wrn-footer__copyright-left"
|
class="wrn-footer__copyright-left"
|
||||||
data-wrn-owner="Footer" data-wrn-part="copyrightLeft" data-wrn-axis-size="{size}" data-wrn-axis-color="{color}">
|
data-wrn-owner="Footer" data-wrn-part="copyrightLeft" data-wrn-axis-size="{size}" data-wrn-axis-color="{color}">
|
||||||
<slot
|
<slot name="copyright-left"></slot>
|
||||||
name="copyright-left"
|
|
||||||
>
|
|
||||||
</slot>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if copyright}
|
{#if copyright}
|
||||||
@@ -290,10 +281,7 @@ component Footer {
|
|||||||
<div
|
<div
|
||||||
class="wrn-footer__copyright-right"
|
class="wrn-footer__copyright-right"
|
||||||
data-wrn-owner="Footer" data-wrn-part="copyrightRight" data-wrn-axis-size="{size}" data-wrn-axis-color="{color}">
|
data-wrn-owner="Footer" data-wrn-part="copyrightRight" data-wrn-axis-size="{size}" data-wrn-axis-color="{color}">
|
||||||
<slot
|
<slot name="copyright-right"></slot>
|
||||||
name="copyright-right"
|
|
||||||
>
|
|
||||||
</slot>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
@@ -366,9 +354,11 @@ component Footer {
|
|||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.wrn-footer { container: wrn-footer / inline-size; }
|
||||||
|
|
||||||
.wrn-footer__grid {
|
.wrn-footer__grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: minmax(13rem, 1.2fr) minmax(0, 3fr) minmax(13rem, 1fr);
|
grid-template-columns: minmax(0, 1fr);
|
||||||
width: min(100%, 90rem);
|
width: min(100%, 90rem);
|
||||||
margin-inline: auto;
|
margin-inline: auto;
|
||||||
padding: clamp(2rem, 5vw, 4rem) clamp(1rem, 4vw, 3rem);
|
padding: clamp(2rem, 5vw, 4rem) clamp(1rem, 4vw, 3rem);
|
||||||
@@ -380,6 +370,8 @@ component Footer {
|
|||||||
max-width: none;
|
max-width: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.wrn-footer__pre:has(> slot:empty),
|
||||||
|
.wrn-footer__post:has(> slot:empty),
|
||||||
.wrn-footer__pre:empty,
|
.wrn-footer__pre:empty,
|
||||||
.wrn-footer__post:empty,
|
.wrn-footer__post:empty,
|
||||||
.wrn-footer__pre:not(:has(*)),
|
.wrn-footer__pre:not(:has(*)),
|
||||||
@@ -408,6 +400,7 @@ component Footer {
|
|||||||
.wrn-footer__links {
|
.wrn-footer__links {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(var(--wrn-footer-columns, 3), minmax(0, 1fr));
|
grid-template-columns: repeat(var(--wrn-footer-columns, 3), minmax(0, 1fr));
|
||||||
|
min-width: 0;
|
||||||
align-content: start;
|
align-content: start;
|
||||||
gap: 1.5rem;
|
gap: 1.5rem;
|
||||||
}
|
}
|
||||||
@@ -435,6 +428,8 @@ component Footer {
|
|||||||
.wrn-footer__link {
|
.wrn-footer__link {
|
||||||
display: flex;
|
display: flex;
|
||||||
width: fit-content;
|
width: fit-content;
|
||||||
|
max-width: 100%;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
min-height: 2rem;
|
min-height: 2rem;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.4rem;
|
gap: 0.4rem;
|
||||||
@@ -469,7 +464,7 @@ component Footer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.wrn-footer__bottom--slots:not(
|
.wrn-footer__bottom--slots:not(
|
||||||
:has(.wrn-footer__copyright-left > *, .wrn-footer__copyright-right > *)
|
:has(.wrn-footer__copyright-left > slot:not(:empty), .wrn-footer__copyright-right > slot:not(:empty))
|
||||||
) {
|
) {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
@@ -513,6 +508,8 @@ component Footer {
|
|||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.wrn-footer__copyright-left:has(> slot:empty),
|
||||||
|
.wrn-footer__copyright-right:has(> slot:empty),
|
||||||
.wrn-footer__copyright-left:empty,
|
.wrn-footer__copyright-left:empty,
|
||||||
.wrn-footer__copyright-right:empty,
|
.wrn-footer__copyright-right:empty,
|
||||||
.wrn-footer__copyright-left:not(:has(*)),
|
.wrn-footer__copyright-left:not(:has(*)),
|
||||||
@@ -525,7 +522,7 @@ component Footer {
|
|||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
}
|
}
|
||||||
.wrn-footer__links {
|
.wrn-footer__links {
|
||||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
grid-template-columns: repeat(auto-fit, minmax(min(100%, 10rem), 1fr));
|
||||||
}
|
}
|
||||||
.wrn-footer__bottom--slots {
|
.wrn-footer__bottom--slots {
|
||||||
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
|
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
|
||||||
@@ -566,5 +563,13 @@ component Footer {
|
|||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@container wrn-footer (max-width: 700px) {
|
||||||
|
.wrn-footer__links { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||||
|
.wrn-footer__bottom { grid-template-columns: 1fr; gap: 1rem; }
|
||||||
|
.wrn-footer__copyright { grid-column: 1; white-space: normal; }
|
||||||
|
}
|
||||||
|
@container wrn-footer (max-width: 400px) {
|
||||||
|
.wrn-footer__links { grid-template-columns: 1fr; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -107,6 +107,9 @@ component List {
|
|||||||
}
|
}
|
||||||
.wrn-list__description { margin-top: 0.25rem; color: var(--wrn-color-text-muted); font-size: 0.875rem; line-height: 1.5rem; }
|
.wrn-list__description { margin-top: 0.25rem; color: var(--wrn-color-text-muted); font-size: 0.875rem; line-height: 1.5rem; }
|
||||||
.wrn-list__items {
|
.wrn-list__items {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
.wrn-list__items:is([data-variant="default"], [data-variant="divided"]) > .wrn-list__item + .wrn-list__item {
|
.wrn-list__items:is([data-variant="default"], [data-variant="divided"]) > .wrn-list__item + .wrn-list__item {
|
||||||
|
|||||||
@@ -312,9 +312,10 @@ component PortalDashboard {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.wrn-portal-dashboard__list a {
|
.wrn-portal-dashboard__list a {
|
||||||
border: 0;
|
align-items: flex-start;
|
||||||
border-bottom: 1px solid var(--wrn-color-border);
|
border: 1px solid var(--wrn-color-border);
|
||||||
border-radius: 0;
|
border-radius: var(--wrn-radius-sm);
|
||||||
|
background: var(--wrn-color-surface-2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.wrn-portal-dashboard__status {
|
.wrn-portal-dashboard__status {
|
||||||
@@ -326,6 +327,8 @@ component PortalDashboard {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.wrn-portal-dashboard__list em {
|
.wrn-portal-dashboard__list em {
|
||||||
|
flex: none;
|
||||||
|
margin-inline-start: auto;
|
||||||
color: var(--wrn-color-muted);
|
color: var(--wrn-color-muted);
|
||||||
font-size: 0.7rem;
|
font-size: 0.7rem;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
|
|||||||
@@ -117,6 +117,8 @@ size: string = "default"
|
|||||||
color: var(--wrn-color-muted);
|
color: var(--wrn-color-muted);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
|
background: var(--wrn-color-surface-2);
|
||||||
|
font: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wrn-preferences__menu > [data-wrn-menu-toggle]:hover,
|
.wrn-preferences__menu > [data-wrn-menu-toggle]:hover,
|
||||||
@@ -185,21 +187,31 @@ size: string = "default"
|
|||||||
padding: 0.5rem 0.65rem;
|
padding: 0.5rem 0.65rem;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
border-radius: 0.55rem;
|
border: 1px solid transparent;
|
||||||
|
border-radius: var(--wrn-radius-sm, 0.55rem);
|
||||||
|
font-family: inherit;
|
||||||
|
text-align: start;
|
||||||
|
cursor: pointer;
|
||||||
color: var(--wrn-color-text);
|
color: var(--wrn-color-text);
|
||||||
background: var(--wrn-color-surface-2);
|
background: var(--wrn-color-surface-2);
|
||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.wrn-preferences__panel button:focus-visible {
|
||||||
|
outline: 2px solid var(--wrn-color-focus, var(--wrn-color-primary));
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
.wrn-preferences__panel button:hover {
|
.wrn-preferences__panel button:hover {
|
||||||
outline: 2px solid color-mix(in srgb, var(--wrn-color-primary) 40%, transparent);
|
border-color: var(--wrn-color-border);
|
||||||
|
background: var(--wrn-color-surface-2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.wrn-preferences__panel button[aria-pressed="true"] {
|
.wrn-preferences__panel button[aria-pressed="true"] {
|
||||||
color: var(--wrn-color-primary);
|
color: var(--wrn-color-primary);
|
||||||
background: color-mix(in srgb, var(--wrn-color-primary) 12%, var(--wrn-color-surface));
|
background: color-mix(in srgb, var(--wrn-color-primary) 12%, var(--wrn-color-surface));
|
||||||
outline: 1px solid color-mix(in srgb, var(--wrn-color-primary) 55%, transparent);
|
border-color: color-mix(in srgb, var(--wrn-color-primary) 55%, transparent);
|
||||||
}
|
}
|
||||||
|
|
||||||
.wrn-preferences__swatches {
|
.wrn-preferences__swatches {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/ui",
|
"name": "@wrnexus/ui",
|
||||||
"version": "0.8.121",
|
"version": "0.8.122",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
|
|||||||
Reference in new issue
Block a user