@agent-native/core 0.138.0 → 0.140.0
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/corpus/README.md +1 -1
- package/corpus/templates/analytics/server/lib/dashboard-report.ts +2 -0
- package/corpus/templates/analytics/server/lib/emails.ts +51 -0
- package/corpus/templates/analytics/server/plugins/transactional-emails.ts +5 -0
- package/corpus/templates/calendar/server/lib/booking-emails.ts +154 -70
- package/corpus/templates/calendar/server/lib/emails.ts +167 -0
- package/corpus/templates/calendar/server/lib/event-guest-notifications.ts +64 -28
- package/corpus/templates/calendar/server/plugins/transactional-emails.ts +5 -0
- package/corpus/templates/clips/actions/invite-member.ts +39 -15
- package/corpus/templates/clips/server/lib/emails.ts +256 -0
- package/corpus/templates/clips/server/lib/transactional-email-templates.ts +24 -0
- package/corpus/templates/clips/server/plugins/transactional-email-catalog.ts +5 -0
- package/corpus/templates/content/server/lib/comment-notifications.ts +57 -26
- package/corpus/templates/content/server/lib/emails.ts +70 -0
- package/corpus/templates/content/server/plugins/transactional-emails.ts +5 -0
- package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +44 -1
- package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +265 -1
- package/corpus/templates/design/actions/create-design-system.ts +9 -10
- package/corpus/templates/design/actions/get-design-system.ts +53 -0
- package/corpus/templates/design/actions/import-design-tokens.ts +5 -71
- package/corpus/templates/design/actions/index-design-tokens.ts +61 -80
- package/corpus/templates/design/actions/update-design-system.ts +7 -10
- package/corpus/templates/design/app/components/design/EditPanel.tsx +45 -2
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +12 -0
- package/corpus/templates/design/app/components/design/edit-panel/design-system-swatches.ts +215 -0
- package/corpus/templates/design/app/components/design/edit-panel/document-colors.ts +10 -6
- package/corpus/templates/design/app/components/design/edit-panel/effects-properties.tsx +19 -3
- package/corpus/templates/design/app/components/design/edit-panel/fill-properties.tsx +36 -10
- package/corpus/templates/design/app/components/design/edit-panel/interaction-state-helpers.ts +15 -0
- package/corpus/templates/design/app/components/design/edit-panel/panel-primitives.tsx +19 -1
- package/corpus/templates/design/app/components/design/edit-panel/position-helpers.ts +6 -0
- package/corpus/templates/design/app/components/design/edit-panel/stroke-properties.tsx +53 -10
- package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +142 -4
- package/corpus/templates/design/app/components/design/types.ts +6 -0
- package/corpus/templates/design/app/i18n-data.ts +20 -0
- package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +1 -1
- package/corpus/templates/design/app/pages/DesignSystems.tsx +78 -1
- package/corpus/templates/design/app/pages/design-editor/html-layer-positioning.ts +6 -1
- package/corpus/templates/design/changelog/2026-08-04-design-system-tokens-show-by-name-in-the-editor.md +6 -0
- package/corpus/templates/design/e2e/helpers.ts +17 -0
- package/corpus/templates/design/server/lib/design-system-tokens.ts +35 -0
- package/corpus/templates/design/shared/authored-color-styles.ts +401 -0
- package/corpus/templates/design/shared/resolve-tweaks.ts +10 -18
- package/corpus/templates/dispatch/app/routes/transactional-email.$appId.$id.tsx +4 -0
- package/corpus/templates/dispatch/app/routes/transactional-email.tsx +4 -0
- package/corpus/templates/forms/server/lib/emails.ts +52 -0
- package/corpus/templates/forms/server/lib/response-email.ts +2 -0
- package/corpus/templates/forms/server/plugins/transactional-emails.ts +5 -0
- package/corpus/templates/plan/actions/request-plan-access.ts +33 -12
- package/corpus/templates/plan/server/lib/comment-notifications.ts +53 -27
- package/corpus/templates/plan/server/lib/emails.ts +67 -0
- package/corpus/templates/plan/server/plugins/transactional-emails.ts +5 -0
- package/corpus/templates/slides/server/lib/comment-notifications.ts +44 -21
- package/corpus/templates/slides/server/lib/emails.ts +40 -0
- package/corpus/templates/slides/server/plugins/transactional-emails.ts +5 -0
- package/dist/brand-kit/index.d.ts +1 -0
- package/dist/brand-kit/index.js +1 -0
- package/dist/brand-kit/tokens.d.ts +92 -0
- package/dist/brand-kit/tokens.js +280 -0
- package/dist/brand-kit/types.d.ts +25 -0
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/routes.d.ts +2 -2
- package/dist/email-catalog/actions/list-email-activity.d.ts +6 -0
- package/dist/email-catalog/actions/list-email-activity.js +15 -0
- package/dist/email-catalog/actions/list-email-engagement.d.ts +6 -0
- package/dist/email-catalog/actions/list-email-engagement.js +15 -0
- package/dist/email-catalog/actions/list-email-log.d.ts +8 -0
- package/dist/email-catalog/actions/list-email-log.js +22 -0
- package/dist/email-catalog/actions/list-transactional-emails.d.ts +23 -0
- package/dist/email-catalog/actions/list-transactional-emails.js +61 -0
- package/dist/email-catalog/actions/render-transactional-email-preview.d.ts +10 -0
- package/dist/email-catalog/actions/render-transactional-email-preview.js +24 -0
- package/dist/email-catalog/authorize.d.ts +2 -0
- package/dist/email-catalog/authorize.js +10 -0
- package/dist/email-catalog/log.d.ts +57 -0
- package/dist/email-catalog/log.js +117 -0
- package/dist/email-catalog/provider-metrics.d.ts +34 -0
- package/dist/email-catalog/provider-metrics.js +165 -0
- package/dist/email-catalog/registry.d.ts +79 -0
- package/dist/email-catalog/registry.js +71 -0
- package/dist/email-catalog/schema.d.ts +212 -0
- package/dist/email-catalog/schema.js +49 -0
- package/dist/email-catalog/system-emails.d.ts +14 -0
- package/dist/email-catalog/system-emails.js +68 -0
- package/dist/localization/default-messages.d.ts +45 -0
- package/dist/localization/default-messages.js +45 -0
- package/dist/notifications/routes.d.ts +6 -6
- package/dist/org/handlers.js +8 -1
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +7 -7
- package/dist/provider-api/actions/provider-api.d.ts +7 -7
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/server/action-discovery.js +22 -0
- package/dist/server/better-auth-instance.js +17 -2
- package/dist/server/email.d.ts +9 -0
- package/dist/server/email.js +44 -3
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.js +1 -0
- package/package.json +3 -1
|
@@ -42,6 +42,7 @@ import {
|
|
|
42
42
|
type GlslShaderPanelContext,
|
|
43
43
|
} from "../inspector/GlslShaderPanel";
|
|
44
44
|
import type { ElementInfo } from "../types";
|
|
45
|
+
import type { DesignSystemColorSwatch } from "./design-system-swatches";
|
|
45
46
|
import { isTextElement } from "./element-classification";
|
|
46
47
|
import { elementStableKey } from "./element-identity";
|
|
47
48
|
import { FieldTrailer } from "./field-primitives";
|
|
@@ -51,6 +52,7 @@ import {
|
|
|
51
52
|
SectionIconButton,
|
|
52
53
|
useRowDragReorder,
|
|
53
54
|
} from "./inspector-controls";
|
|
55
|
+
import { authoredColorValue } from "./interaction-state-helpers";
|
|
54
56
|
import { ColorInput, PanelSection } from "./panel-primitives";
|
|
55
57
|
import {
|
|
56
58
|
colorHasVisibleAlpha,
|
|
@@ -295,10 +297,15 @@ function ShadowEffectRow({
|
|
|
295
297
|
rowProps,
|
|
296
298
|
handleProps,
|
|
297
299
|
element,
|
|
300
|
+
designSystemColors,
|
|
301
|
+
resolvedColor,
|
|
298
302
|
motionKeyframeContext,
|
|
299
303
|
}: {
|
|
300
304
|
layer: ShadowLayer;
|
|
301
305
|
index: number;
|
|
306
|
+
designSystemColors?: DesignSystemColorSwatch[];
|
|
307
|
+
/** The layer's painted colour, for the swatch when `layer.color` is a token. */
|
|
308
|
+
resolvedColor?: string;
|
|
302
309
|
onChange: (patch: Partial<ShadowLayer>, meta?: StyleChangeMeta) => void;
|
|
303
310
|
onRemove: () => void;
|
|
304
311
|
onToggleVisibility: () => void;
|
|
@@ -452,6 +459,8 @@ function ShadowEffectRow({
|
|
|
452
459
|
<ColorInput
|
|
453
460
|
label={t("editPanel.labels.color")}
|
|
454
461
|
value={cssColorOrFallback(layer.color, "rgba(0, 0, 0, 0.25)")}
|
|
462
|
+
designSystemColors={designSystemColors}
|
|
463
|
+
resolvedColor={resolvedColor}
|
|
455
464
|
onChange={(value, meta) => onChange({ color: value }, meta)}
|
|
456
465
|
/>
|
|
457
466
|
</div>
|
|
@@ -464,12 +473,15 @@ export function EffectsProperties({
|
|
|
464
473
|
element,
|
|
465
474
|
onStyleChange,
|
|
466
475
|
onStylesChange,
|
|
476
|
+
designSystemColors,
|
|
467
477
|
glslShaderContext,
|
|
468
478
|
motionKeyframeContext,
|
|
469
479
|
}: {
|
|
470
480
|
element: ElementInfo;
|
|
471
481
|
onStyleChange: StyleChangeHandler;
|
|
472
482
|
onStylesChange?: StylesChangeHandler;
|
|
483
|
+
/** Kit tokens offered by name in the drop-shadow colour picker. */
|
|
484
|
+
designSystemColors?: DesignSystemColorSwatch[];
|
|
473
485
|
/**
|
|
474
486
|
* Persistence context for the code-backed Shader effect type (GLSL
|
|
475
487
|
* overlay rendered above the element's content, saved into the screen
|
|
@@ -508,11 +520,13 @@ export function EffectsProperties({
|
|
|
508
520
|
const shadowTargetsText = isTextElement(element);
|
|
509
521
|
// Read from the same property the writer targets, or the rows would show a
|
|
510
522
|
// box-shadow that is no longer what this element uses.
|
|
523
|
+
const shadowProperty = shadowTargetsText ? "textShadow" : "boxShadow";
|
|
524
|
+
const paintedShadowLayers = effectsAreMixed
|
|
525
|
+
? []
|
|
526
|
+
: parseShadowLayers(styles[shadowProperty]);
|
|
511
527
|
const shadowLayers = effectsAreMixed
|
|
512
528
|
? []
|
|
513
|
-
: parseShadowLayers(
|
|
514
|
-
shadowTargetsText ? styles.textShadow : styles.boxShadow,
|
|
515
|
-
);
|
|
529
|
+
: parseShadowLayers(authoredColorValue(element, shadowProperty));
|
|
516
530
|
const setShadowLayers = (layers: ShadowLayer[], meta?: StyleChangeMeta) => {
|
|
517
531
|
setHiddenEffectStash((stash) =>
|
|
518
532
|
remapIndexedShadowStash(stash, effectStashKey, layers),
|
|
@@ -676,6 +690,8 @@ export function EffectsProperties({
|
|
|
676
690
|
}
|
|
677
691
|
rowProps={shadowDrag.getRowProps(index)}
|
|
678
692
|
handleProps={shadowDrag.getHandleProps(index)}
|
|
693
|
+
designSystemColors={designSystemColors}
|
|
694
|
+
resolvedColor={paintedShadowLayers[index]?.color}
|
|
679
695
|
onChange={(patch, meta) => {
|
|
680
696
|
const next = shadowLayers.map((candidate) =>
|
|
681
697
|
candidate.id === layer.id
|
|
@@ -23,6 +23,12 @@ import {
|
|
|
23
23
|
import { DesignColorPicker, imageFillToBackgroundStyles } from "../inspector";
|
|
24
24
|
import type { GlslShaderPanelContext } from "../inspector/GlslShaderPanel";
|
|
25
25
|
import type { ElementInfo } from "../types";
|
|
26
|
+
import {
|
|
27
|
+
hiddenColorWrite,
|
|
28
|
+
hiddenTokenReference,
|
|
29
|
+
parseTokenReference,
|
|
30
|
+
type DesignSystemColorSwatch,
|
|
31
|
+
} from "./design-system-swatches";
|
|
26
32
|
import { selectionColorValues } from "./document-colors";
|
|
27
33
|
import { isTextElement } from "./element-classification";
|
|
28
34
|
import { elementStableKey } from "./element-identity";
|
|
@@ -46,11 +52,13 @@ import {
|
|
|
46
52
|
SectionIconButton,
|
|
47
53
|
useRowDragReorder,
|
|
48
54
|
} from "./inspector-controls";
|
|
55
|
+
import { authoredColorValue } from "./interaction-state-helpers";
|
|
49
56
|
import { ColorInput, PanelSection } from "./panel-primitives";
|
|
50
57
|
import {
|
|
51
58
|
colorHasVisibleAlpha,
|
|
52
59
|
cssColorOrFallback,
|
|
53
60
|
swatchStyle,
|
|
61
|
+
zeroAlphaLiteral,
|
|
54
62
|
} from "./position-helpers";
|
|
55
63
|
import { isMixedValue } from "./selection-helpers";
|
|
56
64
|
import type {
|
|
@@ -109,6 +117,7 @@ export function FillProperties({
|
|
|
109
117
|
onStyleChange,
|
|
110
118
|
onStylesChange,
|
|
111
119
|
documentColorPalette = [],
|
|
120
|
+
designSystemColors,
|
|
112
121
|
glslShaderContext,
|
|
113
122
|
motionKeyframeContext,
|
|
114
123
|
breakpointOverrideContext,
|
|
@@ -121,6 +130,9 @@ export function FillProperties({
|
|
|
121
130
|
* colors below so a real, always-populated "Document colors" row is
|
|
122
131
|
* available even before any file content has been scanned. */
|
|
123
132
|
documentColorPalette?: string[];
|
|
133
|
+
/** Colour tokens from the design's linked Brand Kit, shown by name in the
|
|
134
|
+
* fill picker above the document palette. */
|
|
135
|
+
designSystemColors?: DesignSystemColorSwatch[];
|
|
124
136
|
/**
|
|
125
137
|
* Persistence context for the code-backed Shader paint type (GLSL source
|
|
126
138
|
* saved into the screen HTML). Threaded into the fill picker so its
|
|
@@ -144,9 +156,20 @@ export function FillProperties({
|
|
|
144
156
|
Record<string, string>
|
|
145
157
|
>({});
|
|
146
158
|
const fillStashKey = elementStableKey(element);
|
|
147
|
-
|
|
159
|
+
// getComputedStyle resolves `var()` before we can read it, so a token-backed
|
|
160
|
+
// fill would come back as a bare colour and lose the name it was set from.
|
|
161
|
+
// The authored inline value still holds the reference.
|
|
162
|
+
const authoredFill = authoredColorValue(element, fillProperty);
|
|
163
|
+
const computedFill = isTextFillElement
|
|
148
164
|
? styles.color || ""
|
|
149
165
|
: styles.backgroundColor || "";
|
|
166
|
+
// A hidden token's `color-mix` also has to survive here, or the reference it
|
|
167
|
+
// carries is replaced by the transparent colour it computes to.
|
|
168
|
+
const fillValue =
|
|
169
|
+
authoredFill &&
|
|
170
|
+
(parseTokenReference(authoredFill) || hiddenTokenReference(authoredFill))
|
|
171
|
+
? authoredFill
|
|
172
|
+
: computedFill;
|
|
150
173
|
const backgroundLayers = isTextFillElement
|
|
151
174
|
? []
|
|
152
175
|
: splitCssLayers(styles.backgroundImage || "");
|
|
@@ -183,24 +206,25 @@ export function FillProperties({
|
|
|
183
206
|
// separately: computed style strips comments but keeps rgba() channels).
|
|
184
207
|
// Showing again just restores alpha to 1 using those same channels, so no
|
|
185
208
|
// stash is required and the hide survives reselect/reload.
|
|
186
|
-
const
|
|
209
|
+
const hiddenFillToken = hiddenTokenReference(fillValue);
|
|
210
|
+
const isHidden = Boolean(hiddenFillToken) || !colorHasVisibleAlpha(fillValue);
|
|
211
|
+
|
|
187
212
|
const handleFillVisibilityToggle = () => {
|
|
188
|
-
const parsed = parseCssColor(fillValue);
|
|
189
213
|
if (isHidden) {
|
|
214
|
+
if (hiddenFillToken) {
|
|
215
|
+
onStyleChange(fillProperty, hiddenFillToken);
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
const parsed = parseCssColor(fillValue);
|
|
190
219
|
const restored = parsed
|
|
191
220
|
? rgbaToCss(withColorOpacity(parsed, 100))
|
|
192
221
|
: isTextFillElement
|
|
193
222
|
? "#000000"
|
|
194
223
|
: "#ffffff";
|
|
195
224
|
onStyleChange(fillProperty, restored);
|
|
196
|
-
|
|
197
|
-
onStyleChange(fillProperty, rgbaToCss(withColorOpacity(parsed, 0)));
|
|
198
|
-
} else {
|
|
199
|
-
// Value wasn't a parseable color (e.g. already the literal
|
|
200
|
-
// "transparent") — nothing to preserve, transparent is the best we
|
|
201
|
-
// can do.
|
|
202
|
-
onStyleChange(fillProperty, "transparent");
|
|
225
|
+
return;
|
|
203
226
|
}
|
|
227
|
+
onStyleChange(fillProperty, hiddenColorWrite(fillValue, zeroAlphaLiteral));
|
|
204
228
|
};
|
|
205
229
|
|
|
206
230
|
// Reorder fill layers by dragging: permute all four index-aligned parallel
|
|
@@ -385,6 +409,8 @@ export function FillProperties({
|
|
|
385
409
|
commitStylePatch(patch, onStyleChange, onStylesChange)
|
|
386
410
|
}
|
|
387
411
|
documentColors={documentColors}
|
|
412
|
+
designSystemColors={designSystemColors}
|
|
413
|
+
resolvedColor={computedFill}
|
|
388
414
|
pickerKey={[
|
|
389
415
|
element.sourceId ??
|
|
390
416
|
element.id ??
|
package/corpus/templates/design/app/components/design/edit-panel/interaction-state-helpers.ts
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
import type { ElementInfo } from "../types";
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* The colour a property was authored with, else the computed value. A generated
|
|
5
|
+
* design styles through classes, so `el.style` alone is not enough.
|
|
6
|
+
*/
|
|
7
|
+
export function authoredColorValue(
|
|
8
|
+
element: ElementInfo,
|
|
9
|
+
property: string,
|
|
10
|
+
): string | undefined {
|
|
11
|
+
const inline = element.inlineStyles?.[property];
|
|
12
|
+
if (inline) return inline;
|
|
13
|
+
const authored = element.authoredColorStyles?.[property];
|
|
14
|
+
if (authored) return authored;
|
|
15
|
+
return element.computedStyles[property];
|
|
16
|
+
}
|
|
17
|
+
|
|
3
18
|
export function authoredStyleValue(
|
|
4
19
|
element: ElementInfo,
|
|
5
20
|
property: string,
|
|
@@ -26,6 +26,10 @@ import {
|
|
|
26
26
|
} from "../inspector";
|
|
27
27
|
import type { DesignPaintType } from "../inspector/DesignColorPicker";
|
|
28
28
|
import type { GlslShaderPanelContext } from "../inspector/GlslShaderPanel";
|
|
29
|
+
import {
|
|
30
|
+
parseTokenReference,
|
|
31
|
+
type DesignSystemColorSwatch,
|
|
32
|
+
} from "./design-system-swatches";
|
|
29
33
|
import {
|
|
30
34
|
buildGradientLayer,
|
|
31
35
|
defaultGradientLayer,
|
|
@@ -225,6 +229,8 @@ export function ColorInput({
|
|
|
225
229
|
onBlendModeChange,
|
|
226
230
|
supportsLayeredFills = false,
|
|
227
231
|
documentColors,
|
|
232
|
+
designSystemColors,
|
|
233
|
+
resolvedColor,
|
|
228
234
|
supportedPaintTypes,
|
|
229
235
|
pickerKey,
|
|
230
236
|
glslShaderContext,
|
|
@@ -270,6 +276,8 @@ export function ColorInput({
|
|
|
270
276
|
supportsLayeredFills?: boolean;
|
|
271
277
|
/** Hex strings already in use on the page — forwarded to the color picker swatch grid. */
|
|
272
278
|
documentColors?: string[];
|
|
279
|
+
designSystemColors?: DesignSystemColorSwatch[];
|
|
280
|
+
resolvedColor?: string;
|
|
273
281
|
/**
|
|
274
282
|
* Restricts which paint-type tabs the popover renders. Omit for the full
|
|
275
283
|
* set (solid + gradients + image + …). Pass `["solid"]` for properties
|
|
@@ -369,7 +377,15 @@ export function ColorInput({
|
|
|
369
377
|
// before clobbering the last-known-good value in this component's own
|
|
370
378
|
// state. Reject anything that doesn't parse as a plain solid color in
|
|
371
379
|
// that case instead of forwarding it.
|
|
372
|
-
|
|
380
|
+
// A design system token reference is valid on a plain color property even
|
|
381
|
+
// though it is not itself a color, so it must survive this guard.
|
|
382
|
+
if (
|
|
383
|
+
!supportsLayeredFills &&
|
|
384
|
+
!parseCssColor(next) &&
|
|
385
|
+
!parseTokenReference(next)
|
|
386
|
+
) {
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
373
389
|
pendingGestureRef.current = phase === "preview";
|
|
374
390
|
setDraft(next);
|
|
375
391
|
onChange(next, { phase });
|
|
@@ -574,6 +590,8 @@ export function ColorInput({
|
|
|
574
590
|
gradientType={activeGradient?.type}
|
|
575
591
|
onGradientTypeChange={handleGradientTypeChange}
|
|
576
592
|
documentColors={documentColors}
|
|
593
|
+
designSystemColors={designSystemColors}
|
|
594
|
+
resolvedColor={resolvedColor}
|
|
577
595
|
supportedPaintTypes={supportedPaintTypes}
|
|
578
596
|
glslShaderContext={glslShaderContext}
|
|
579
597
|
/>
|
|
@@ -213,6 +213,12 @@ export function resolveRestoredStrokeStyle(
|
|
|
213
213
|
* (a single undo/history step, matching every other multi-property commit
|
|
214
214
|
* in this file, e.g. TextStrokeProperties' equivalent show handler).
|
|
215
215
|
*/
|
|
216
|
+
/** A literal colour at zero alpha, or null when it does not parse as one. */
|
|
217
|
+
export function zeroAlphaLiteral(literal: string): string | null {
|
|
218
|
+
const parsed = parseCssColor(literal);
|
|
219
|
+
return parsed ? rgbaToCss(withColorOpacity(parsed, 0)) : null;
|
|
220
|
+
}
|
|
221
|
+
|
|
216
222
|
export function strokeShowPatch(
|
|
217
223
|
prefix: "border" | "outline",
|
|
218
224
|
color: string,
|
|
@@ -23,6 +23,12 @@ import {
|
|
|
23
23
|
import { ScrubInput } from "../inspector";
|
|
24
24
|
import type { DesignPaintType } from "../inspector/DesignColorPicker";
|
|
25
25
|
import type { ElementInfo } from "../types";
|
|
26
|
+
import {
|
|
27
|
+
hiddenColorWrite,
|
|
28
|
+
hiddenTokenReference,
|
|
29
|
+
parseTokenReference,
|
|
30
|
+
type DesignSystemColorSwatch,
|
|
31
|
+
} from "./design-system-swatches";
|
|
26
32
|
import { isTextElement } from "./element-classification";
|
|
27
33
|
import { commitStylePatch, FieldTrailer } from "./field-primitives";
|
|
28
34
|
import { SectionIconButton } from "./inspector-controls";
|
|
@@ -41,6 +47,7 @@ import {
|
|
|
41
47
|
strokeShowPatch,
|
|
42
48
|
textStrokeAddPatch,
|
|
43
49
|
textStrokeIsVisible,
|
|
50
|
+
zeroAlphaLiteral,
|
|
44
51
|
} from "./position-helpers";
|
|
45
52
|
import { isMixedValue } from "./selection-helpers";
|
|
46
53
|
import type {
|
|
@@ -60,6 +67,26 @@ import { STROKE_POSITION_OPTIONS } from "./style-options";
|
|
|
60
67
|
*/
|
|
61
68
|
const SOLID_ONLY_PAINT_TYPES: DesignPaintType[] = ["solid"];
|
|
62
69
|
|
|
70
|
+
/**
|
|
71
|
+
* The stroke colour as authored: its token name when it has one, else the
|
|
72
|
+
* resolved colour. Empty when no rule set it at all — the border then paints
|
|
73
|
+
* CSS's `currentColor` default, and echoing the inherited text colour here
|
|
74
|
+
* would claim a stroke colour the design never chose.
|
|
75
|
+
*/
|
|
76
|
+
export function authoredStrokeColor(
|
|
77
|
+
element: ElementInfo,
|
|
78
|
+
property: string,
|
|
79
|
+
computed: string | undefined,
|
|
80
|
+
): string {
|
|
81
|
+
const authored =
|
|
82
|
+
element.inlineStyles?.[property] ?? element.authoredColorStyles?.[property];
|
|
83
|
+
if (!authored) return "";
|
|
84
|
+
if (parseTokenReference(authored) || hiddenTokenReference(authored)) {
|
|
85
|
+
return authored;
|
|
86
|
+
}
|
|
87
|
+
return computed || authored;
|
|
88
|
+
}
|
|
89
|
+
|
|
63
90
|
type StrokeLayerKind = "border" | "outline";
|
|
64
91
|
type StrokePosition = "inside" | "outside" | "center";
|
|
65
92
|
|
|
@@ -67,6 +94,7 @@ function StrokeLayerControl({
|
|
|
67
94
|
kind,
|
|
68
95
|
visible,
|
|
69
96
|
color,
|
|
97
|
+
designSystemColors,
|
|
70
98
|
width,
|
|
71
99
|
styleValue,
|
|
72
100
|
outlineOffset,
|
|
@@ -80,6 +108,7 @@ function StrokeLayerControl({
|
|
|
80
108
|
kind: StrokeLayerKind;
|
|
81
109
|
visible: boolean;
|
|
82
110
|
color: string;
|
|
111
|
+
designSystemColors?: DesignSystemColorSwatch[];
|
|
83
112
|
width: string;
|
|
84
113
|
styleValue: string;
|
|
85
114
|
/** Only meaningful when `kind === "outline"` — distinguishes outside vs
|
|
@@ -104,6 +133,7 @@ function StrokeLayerControl({
|
|
|
104
133
|
label: t(`editPanel.labels.${option.key}`),
|
|
105
134
|
}));
|
|
106
135
|
const prefix = kind === "border" ? "border" : "outline";
|
|
136
|
+
const hiddenToken = hiddenTokenReference(color);
|
|
107
137
|
const position: StrokePosition =
|
|
108
138
|
kind === "border"
|
|
109
139
|
? "inside"
|
|
@@ -162,7 +192,10 @@ function StrokeLayerControl({
|
|
|
162
192
|
<div className="min-w-0 flex-1">
|
|
163
193
|
<ColorInput
|
|
164
194
|
label=""
|
|
165
|
-
|
|
195
|
+
// Not cssColorOrFallback: an unset stroke must stay unset here
|
|
196
|
+
// rather than render as a black the design never chose.
|
|
197
|
+
value={hiddenToken ?? color}
|
|
198
|
+
designSystemColors={designSystemColors}
|
|
166
199
|
onChange={(value, meta) =>
|
|
167
200
|
onStyleChange(`${prefix}Color`, value, meta)
|
|
168
201
|
}
|
|
@@ -183,21 +216,18 @@ function StrokeLayerControl({
|
|
|
183
216
|
// style permanently, since there is no round-trippable "unset"
|
|
184
217
|
// for that keyword once it's overwritten.
|
|
185
218
|
if (visible) {
|
|
186
|
-
const parsed = parseCssColor(color);
|
|
187
219
|
onStyleChange(
|
|
188
220
|
`${prefix}Color`,
|
|
189
|
-
|
|
221
|
+
hiddenColorWrite(color, zeroAlphaLiteral),
|
|
190
222
|
);
|
|
191
223
|
return;
|
|
192
224
|
}
|
|
193
225
|
// Restore color/width/style as ONE commit (single undo step)
|
|
194
226
|
// rather than three sequential onStyleChange calls — see
|
|
195
227
|
// strokeShowPatch's doc comment.
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
onStylesChange,
|
|
200
|
-
);
|
|
228
|
+
const showPatch = strokeShowPatch(prefix, color, width, styleValue);
|
|
229
|
+
if (hiddenToken) showPatch[`${prefix}Color`] = hiddenToken;
|
|
230
|
+
commitStylePatch(showPatch, onStyleChange, onStylesChange);
|
|
201
231
|
}}
|
|
202
232
|
>
|
|
203
233
|
{visible ? (
|
|
@@ -293,12 +323,15 @@ export function StrokeProperties({
|
|
|
293
323
|
element,
|
|
294
324
|
onStyleChange,
|
|
295
325
|
onStylesChange,
|
|
326
|
+
designSystemColors,
|
|
296
327
|
motionKeyframeContext,
|
|
297
328
|
breakpointOverrideContext,
|
|
298
329
|
}: {
|
|
299
330
|
element: ElementInfo;
|
|
300
331
|
onStyleChange: StyleChangeHandler;
|
|
301
332
|
onStylesChange?: StylesChangeHandler;
|
|
333
|
+
/** Linked Brand Kit colour tokens, offered by name in the stroke picker. */
|
|
334
|
+
designSystemColors?: DesignSystemColorSwatch[];
|
|
302
335
|
motionKeyframeContext?: MotionKeyframeFieldContext;
|
|
303
336
|
breakpointOverrideContext?: BreakpointOverrideFieldContext;
|
|
304
337
|
}) {
|
|
@@ -449,7 +482,12 @@ export function StrokeProperties({
|
|
|
449
482
|
<StrokeLayerControl
|
|
450
483
|
kind="border"
|
|
451
484
|
visible={borderVisible}
|
|
452
|
-
color={
|
|
485
|
+
color={authoredStrokeColor(
|
|
486
|
+
element,
|
|
487
|
+
"borderColor",
|
|
488
|
+
styles.borderColor,
|
|
489
|
+
)}
|
|
490
|
+
designSystemColors={designSystemColors}
|
|
453
491
|
width={styles.borderWidth || "0px"}
|
|
454
492
|
styleValue={styles.borderStyle || "none"}
|
|
455
493
|
onStyleChange={onStyleChange}
|
|
@@ -470,7 +508,12 @@ export function StrokeProperties({
|
|
|
470
508
|
<StrokeLayerControl
|
|
471
509
|
kind="outline"
|
|
472
510
|
visible={outlineVisible}
|
|
473
|
-
color={
|
|
511
|
+
color={authoredStrokeColor(
|
|
512
|
+
element,
|
|
513
|
+
"outlineColor",
|
|
514
|
+
styles.outlineColor || styles.borderColor,
|
|
515
|
+
)}
|
|
516
|
+
designSystemColors={designSystemColors}
|
|
474
517
|
width={styles.outlineWidth || "0px"}
|
|
475
518
|
styleValue={styles.outlineStyle || "solid"}
|
|
476
519
|
outlineOffset={styles.outlineOffset || "0px"}
|
|
@@ -43,6 +43,14 @@ import {
|
|
|
43
43
|
} from "@/components/ui/tooltip";
|
|
44
44
|
import { cn } from "@/lib/utils";
|
|
45
45
|
|
|
46
|
+
import {
|
|
47
|
+
groupColorSwatches,
|
|
48
|
+
parseTokenReference,
|
|
49
|
+
resolveTokenNameForColor,
|
|
50
|
+
swatchLabel,
|
|
51
|
+
tokenReferenceValue,
|
|
52
|
+
type DesignSystemColorSwatch,
|
|
53
|
+
} from "../edit-panel/design-system-swatches";
|
|
46
54
|
import {
|
|
47
55
|
GlslShaderPanel,
|
|
48
56
|
type GlslShaderPanelContext,
|
|
@@ -190,6 +198,16 @@ export interface DesignColorPickerProps {
|
|
|
190
198
|
* limited in the caller; the component renders whatever is passed.
|
|
191
199
|
*/
|
|
192
200
|
documentColors?: string[];
|
|
201
|
+
/**
|
|
202
|
+
* Colour tokens from the design's linked Brand Kit, listed by name above
|
|
203
|
+
* "Document colors". Picking one writes `var(--token, #hex)`.
|
|
204
|
+
*/
|
|
205
|
+
designSystemColors?: DesignSystemColorSwatch[];
|
|
206
|
+
/**
|
|
207
|
+
* The colour the canvas resolved `value` to. Only consulted when `value` is a
|
|
208
|
+
* token reference this document cannot resolve on its own.
|
|
209
|
+
*/
|
|
210
|
+
resolvedColor?: string;
|
|
193
211
|
/**
|
|
194
212
|
* Restricts which paint-type tabs are rendered (Solid, Linear, Radial, …).
|
|
195
213
|
* Omit to show every type (default, backward compatible). Callers that
|
|
@@ -703,6 +721,8 @@ export function DesignColorPicker({
|
|
|
703
721
|
gradientType,
|
|
704
722
|
onGradientTypeChange,
|
|
705
723
|
documentColors,
|
|
724
|
+
designSystemColors,
|
|
725
|
+
resolvedColor,
|
|
706
726
|
supportedPaintTypes,
|
|
707
727
|
shaderContext,
|
|
708
728
|
glslShaderContext,
|
|
@@ -712,7 +732,15 @@ export function DesignColorPicker({
|
|
|
712
732
|
className,
|
|
713
733
|
}: DesignColorPickerProps) {
|
|
714
734
|
const copy = { ...DEFAULT_LABELS, ...labels };
|
|
715
|
-
|
|
735
|
+
// A `var(--token, #hex)` fill has no colour of its own outside the preview
|
|
736
|
+
// document; read the literal so the SV field and chip are not blank.
|
|
737
|
+
const tokenReference = parseTokenReference(value);
|
|
738
|
+
// Generated CSS writes bare `var(--token)`, unresolvable here, so the colour
|
|
739
|
+
// has to come from what the canvas painted.
|
|
740
|
+
const color =
|
|
741
|
+
parseCssColorExtended(tokenReference?.fallback ?? value) ??
|
|
742
|
+
(tokenReference ? parseCssColorExtended(resolvedColor ?? "") : null) ??
|
|
743
|
+
FALLBACK_COLOR;
|
|
716
744
|
const hsv = rgbaToHsv(color);
|
|
717
745
|
const hsl = rgbaToHsl(color);
|
|
718
746
|
const effectiveOpacity = opacity ?? alphaToOpacity(color.a);
|
|
@@ -814,6 +842,16 @@ export function DesignColorPicker({
|
|
|
814
842
|
? rawEffectivePaintType
|
|
815
843
|
: "solid";
|
|
816
844
|
|
|
845
|
+
const tokenName = useMemo(() => {
|
|
846
|
+
if (effectivePaintType !== "solid" || !designSystemColors?.length) {
|
|
847
|
+
return null;
|
|
848
|
+
}
|
|
849
|
+
return resolveTokenNameForColor(
|
|
850
|
+
tokenReference ? value : rgbaToCss(color),
|
|
851
|
+
designSystemColors,
|
|
852
|
+
);
|
|
853
|
+
}, [color, designSystemColors, effectivePaintType, tokenReference, value]);
|
|
854
|
+
|
|
817
855
|
// Resolve the active gradient: prefer EditPanel-driven props; otherwise parse
|
|
818
856
|
// the live CSS value, falling back to local edit state.
|
|
819
857
|
const parsedGradient = useMemo(
|
|
@@ -896,6 +934,15 @@ export function DesignColorPicker({
|
|
|
896
934
|
onChange(next);
|
|
897
935
|
};
|
|
898
936
|
|
|
937
|
+
/**
|
|
938
|
+
* Same route as `emitColor`: a solid fill must not detour through the
|
|
939
|
+
* layered-paint merge just because its value is an expression.
|
|
940
|
+
*/
|
|
941
|
+
const emitTokenReference = (nextValue: string) => {
|
|
942
|
+
lastEmittedValueRef.current = nextValue;
|
|
943
|
+
onChange(nextValue);
|
|
944
|
+
};
|
|
945
|
+
|
|
899
946
|
const emitPaintValue = (nextValue: string) => {
|
|
900
947
|
lastEmittedValueRef.current = nextValue;
|
|
901
948
|
if (onPaintValueChange) onPaintValueChange(nextValue);
|
|
@@ -1336,8 +1383,14 @@ export function DesignColorPicker({
|
|
|
1336
1383
|
className="size-4 shrink-0 rounded-[3px] border border-border/60"
|
|
1337
1384
|
style={triggerSwatchStyle(value, color)}
|
|
1338
1385
|
/>
|
|
1339
|
-
<span
|
|
1340
|
-
{
|
|
1386
|
+
<span
|
|
1387
|
+
className={cn(
|
|
1388
|
+
"min-w-0 flex-1 truncate text-left tabular-nums !text-[11px]",
|
|
1389
|
+
// Hex is conventionally upper-case; a token name is not.
|
|
1390
|
+
!tokenName && "uppercase",
|
|
1391
|
+
)}
|
|
1392
|
+
>
|
|
1393
|
+
{tokenName ?? triggerLabel(effectivePaintType, color)}
|
|
1341
1394
|
</span>
|
|
1342
1395
|
<span className="tabular-nums text-muted-foreground !text-[11px]">
|
|
1343
1396
|
{effectiveOpacity}%
|
|
@@ -1350,7 +1403,7 @@ export function DesignColorPicker({
|
|
|
1350
1403
|
side="left"
|
|
1351
1404
|
align="start"
|
|
1352
1405
|
sideOffset={8}
|
|
1353
|
-
className="z-[10000] w-[252px] p-0 shadow-xl"
|
|
1406
|
+
className="z-[10000] max-h-[calc(100vh-5rem)] w-[252px] overflow-y-auto p-0 shadow-xl"
|
|
1354
1407
|
// Keep the picker open when the style change triggered by a paint-type
|
|
1355
1408
|
// switch causes the canvas to re-project the element. Without this,
|
|
1356
1409
|
// Radix treats the resulting focus shift as an "interact outside" event
|
|
@@ -1779,6 +1832,91 @@ export function DesignColorPicker({
|
|
|
1779
1832
|
</div>
|
|
1780
1833
|
)}
|
|
1781
1834
|
|
|
1835
|
+
{/* ── Design system ────────────────────────────────────────── */}
|
|
1836
|
+
{/* The linked Brand Kit's colour tokens, under the names the
|
|
1837
|
+
source system uses. Clicking one applies its value; it does
|
|
1838
|
+
not bind to the CSS variable, so the fill will not follow a
|
|
1839
|
+
later kit edit. */}
|
|
1840
|
+
{designSystemColors && designSystemColors.length > 0 && (
|
|
1841
|
+
<div className="border-t border-border/70 px-3 py-2.5">
|
|
1842
|
+
<div className="mb-2 flex h-6 w-full items-center justify-between gap-2 px-0.5 !text-[11px] text-muted-foreground">
|
|
1843
|
+
<span className="truncate">
|
|
1844
|
+
{"Design system" /* i18n-ignore design picker source */}
|
|
1845
|
+
</span>
|
|
1846
|
+
{designSystemColors[0]?.source ? (
|
|
1847
|
+
<span className="shrink-0 truncate opacity-80">
|
|
1848
|
+
{designSystemColors[0].source}
|
|
1849
|
+
</span>
|
|
1850
|
+
) : null}
|
|
1851
|
+
</div>
|
|
1852
|
+
|
|
1853
|
+
{/* A list, not a swatch grid: the token's name is the whole
|
|
1854
|
+
point of this section, and a grid hides it behind hover. */}
|
|
1855
|
+
<div className="max-h-40 space-y-2 overflow-y-auto pe-0.5">
|
|
1856
|
+
{groupColorSwatches(designSystemColors).map((group) => (
|
|
1857
|
+
<div key={group.label ?? "ungrouped"}>
|
|
1858
|
+
{group.label ? (
|
|
1859
|
+
<div className="mb-1 truncate px-0.5 !text-[10px] text-muted-foreground/80">
|
|
1860
|
+
{group.label}
|
|
1861
|
+
</div>
|
|
1862
|
+
) : null}
|
|
1863
|
+
<div className="space-y-0.5">
|
|
1864
|
+
{group.swatches.map((swatch) => {
|
|
1865
|
+
const isActive =
|
|
1866
|
+
!activeGradient && tokenName === swatch.name;
|
|
1867
|
+
return (
|
|
1868
|
+
<button
|
|
1869
|
+
key={swatch.cssVar}
|
|
1870
|
+
type="button"
|
|
1871
|
+
disabled={disabled}
|
|
1872
|
+
aria-label={swatchLabel(swatch)}
|
|
1873
|
+
aria-pressed={isActive}
|
|
1874
|
+
title={`${swatch.name} · ${swatch.cssVar}`}
|
|
1875
|
+
className={cn(
|
|
1876
|
+
"flex w-full items-center gap-2 rounded-sm border px-1 py-0.5 text-left transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
1877
|
+
isActive
|
|
1878
|
+
? "border-primary bg-accent/40"
|
|
1879
|
+
: "border-transparent hover:bg-accent/40",
|
|
1880
|
+
)}
|
|
1881
|
+
onClick={() => {
|
|
1882
|
+
if (activeGradient) {
|
|
1883
|
+
// The stop parser round-trips colours,
|
|
1884
|
+
// not var() expressions.
|
|
1885
|
+
emitStopColor(
|
|
1886
|
+
parseCssColorExtended(swatch.value) ??
|
|
1887
|
+
color,
|
|
1888
|
+
);
|
|
1889
|
+
} else {
|
|
1890
|
+
emitTokenReference(
|
|
1891
|
+
tokenReferenceValue(swatch),
|
|
1892
|
+
);
|
|
1893
|
+
}
|
|
1894
|
+
notifyChangeComplete();
|
|
1895
|
+
}}
|
|
1896
|
+
>
|
|
1897
|
+
<span
|
|
1898
|
+
className="size-4 shrink-0 rounded-[3px] border border-border/60"
|
|
1899
|
+
style={swatchStyle(swatch.value)}
|
|
1900
|
+
/>
|
|
1901
|
+
<span className="min-w-0 flex-1 truncate !text-[11px] text-foreground">
|
|
1902
|
+
{swatchLabel(swatch)}
|
|
1903
|
+
</span>
|
|
1904
|
+
<span className="shrink-0 font-mono uppercase !text-[10px] text-muted-foreground">
|
|
1905
|
+
{rgbaToHex(
|
|
1906
|
+
parseCssColorExtended(swatch.value) ??
|
|
1907
|
+
color,
|
|
1908
|
+
)}
|
|
1909
|
+
</span>
|
|
1910
|
+
</button>
|
|
1911
|
+
);
|
|
1912
|
+
})}
|
|
1913
|
+
</div>
|
|
1914
|
+
</div>
|
|
1915
|
+
))}
|
|
1916
|
+
</div>
|
|
1917
|
+
</div>
|
|
1918
|
+
)}
|
|
1919
|
+
|
|
1782
1920
|
{/* ── Document colors ──────────────────────────────────────── */}
|
|
1783
1921
|
{/* Renders the palette of colors already used in the design.
|
|
1784
1922
|
When `documentColors` is provided, those swatches are shown;
|
|
@@ -95,6 +95,12 @@ export interface ElementInfo {
|
|
|
95
95
|
* must fall back to computedStyles-based inference when absent.
|
|
96
96
|
*/
|
|
97
97
|
inlineStyles?: Record<string, string>;
|
|
98
|
+
/**
|
|
99
|
+
* Colour declarations resolved through the cascade (inline, else the last
|
|
100
|
+
* matching rule). Generated designs style via classes, so `inlineStyles` is
|
|
101
|
+
* empty and only this preserves a `var(--token, …)` reference.
|
|
102
|
+
*/
|
|
103
|
+
authoredColorStyles?: Record<string, string>;
|
|
98
104
|
/**
|
|
99
105
|
* Value of the element's `data-an-primitive` attribute (e.g. "text",
|
|
100
106
|
* "rectangle", "frame", "ellipse") when present. Canvas-drawn primitives —
|