@shapesos/clay 0.13.0 → 0.14.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/dist/ai-elements.cjs +877 -0
- package/dist/ai-elements.cjs.map +1 -0
- package/dist/ai-elements.d.cts +136 -0
- package/dist/ai-elements.d.ts +136 -0
- package/dist/ai-elements.js +31 -0
- package/dist/ai-elements.js.map +1 -0
- package/dist/artifacts.cjs +56 -11
- package/dist/artifacts.cjs.map +1 -1
- package/dist/artifacts.js +6 -6
- package/dist/blocks.cjs +95 -28
- package/dist/blocks.cjs.map +1 -1
- package/dist/blocks.css +1 -1
- package/dist/blocks.js +7 -7
- package/dist/button.cjs +27 -17
- package/dist/button.cjs.map +1 -1
- package/dist/button.js +3 -2
- package/dist/chart.js +3 -3
- package/dist/chat.cjs +95 -28
- package/dist/chat.cjs.map +1 -1
- package/dist/chat.js +8 -8
- package/dist/{chunk-QH2WW2MW.js → chunk-3THOTQO3.js} +7 -7
- package/dist/{chunk-WPQQVKWY.js → chunk-7OYIDM42.js} +2 -2
- package/dist/{chunk-EDICS62G.js → chunk-B4U37WIH.js} +9 -11
- package/dist/chunk-B4U37WIH.js.map +1 -0
- package/dist/chunk-BR5S37SC.js +171 -0
- package/dist/chunk-BR5S37SC.js.map +1 -0
- package/dist/chunk-CYC2YUKU.js +1 -0
- package/dist/chunk-CYC2YUKU.js.map +1 -0
- package/dist/chunk-L75JPWGS.js +1 -0
- package/dist/chunk-L75JPWGS.js.map +1 -0
- package/dist/{chunk-26XVDY6Z.js → chunk-MDAWYZBI.js} +15 -3
- package/dist/{chunk-26XVDY6Z.js.map → chunk-MDAWYZBI.js.map} +1 -1
- package/dist/{chunk-L6DUGB2E.js → chunk-PGR2MDDU.js} +16 -5
- package/dist/chunk-PGR2MDDU.js.map +1 -0
- package/dist/{chunk-OBOXCBDL.js → chunk-PYDVANWI.js} +58 -13
- package/dist/chunk-PYDVANWI.js.map +1 -0
- package/dist/{chunk-SV24ONND.js → chunk-UXT2H3FM.js} +28 -18
- package/dist/chunk-UXT2H3FM.js.map +1 -0
- package/dist/{chunk-DT44KNCV.js → chunk-XFJ6XMJZ.js} +5 -5
- package/dist/{chunk-QXGYMDIA.js → chunk-YZB6YXKK.js} +4 -4
- package/dist/icon.cjs +56 -11
- package/dist/icon.cjs.map +1 -1
- package/dist/icon.d.cts +11 -2
- package/dist/icon.d.ts +11 -2
- package/dist/icon.js +1 -1
- package/dist/index.cjs +658 -415
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -2
- package/dist/index.d.ts +4 -2
- package/dist/index.js +50 -28
- package/dist/keyboard-shortcut.cjs +15 -4
- package/dist/keyboard-shortcut.cjs.map +1 -1
- package/dist/keyboard-shortcut.d.cts +10 -3
- package/dist/keyboard-shortcut.d.ts +10 -3
- package/dist/keyboard-shortcut.js +1 -1
- package/dist/text-area.cjs +27 -17
- package/dist/text-area.cjs.map +1 -1
- package/dist/text-area.d.cts +2 -24
- package/dist/text-area.d.ts +2 -24
- package/dist/text-area.js +3 -2
- package/dist/tokens.cjs +27 -17
- package/dist/tokens.cjs.map +1 -1
- package/dist/tokens.d.cts +6 -5
- package/dist/tokens.d.ts +6 -5
- package/dist/tokens.js +1 -1
- package/dist/types-CMzZsL_J.d.cts +25 -0
- package/dist/types-CMzZsL_J.d.ts +25 -0
- package/dist/utils.js +3 -3
- package/package.json +6 -1
- package/dist/chunk-EDICS62G.js.map +0 -1
- package/dist/chunk-L6DUGB2E.js.map +0 -1
- package/dist/chunk-OBOXCBDL.js.map +0 -1
- package/dist/chunk-SV24ONND.js.map +0 -1
- /package/dist/{chunk-QH2WW2MW.js.map → chunk-3THOTQO3.js.map} +0 -0
- /package/dist/{chunk-WPQQVKWY.js.map → chunk-7OYIDM42.js.map} +0 -0
- /package/dist/{chunk-DT44KNCV.js.map → chunk-XFJ6XMJZ.js.map} +0 -0
- /package/dist/{chunk-QXGYMDIA.js.map → chunk-YZB6YXKK.js.map} +0 -0
package/dist/chat.js
CHANGED
|
@@ -5,19 +5,19 @@ import {
|
|
|
5
5
|
copyMessageText,
|
|
6
6
|
useChatContext,
|
|
7
7
|
useCopyToClipboard
|
|
8
|
-
} from "./chunk-
|
|
9
|
-
import "./chunk-
|
|
8
|
+
} from "./chunk-3THOTQO3.js";
|
|
9
|
+
import "./chunk-MDAWYZBI.js";
|
|
10
10
|
import "./chunk-NMKKU2UG.js";
|
|
11
|
-
import "./chunk-
|
|
11
|
+
import "./chunk-XFJ6XMJZ.js";
|
|
12
|
+
import "./chunk-PYDVANWI.js";
|
|
12
13
|
import "./chunk-LXYCT4YO.js";
|
|
13
|
-
import "./chunk-
|
|
14
|
+
import "./chunk-OLJIJYB5.js";
|
|
15
|
+
import "./chunk-YZB6YXKK.js";
|
|
14
16
|
import "./chunk-C77QMQNT.js";
|
|
15
|
-
import "./chunk-
|
|
17
|
+
import "./chunk-UXT2H3FM.js";
|
|
18
|
+
import "./chunk-JF3P66JF.js";
|
|
16
19
|
import "./chunk-4MZZH3WX.js";
|
|
17
20
|
import "./chunk-P6GUNIAE.js";
|
|
18
|
-
import "./chunk-OBOXCBDL.js";
|
|
19
|
-
import "./chunk-OLJIJYB5.js";
|
|
20
|
-
import "./chunk-JF3P66JF.js";
|
|
21
21
|
import "./chunk-5WRI5ZAA.js";
|
|
22
22
|
export {
|
|
23
23
|
ChatContext,
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Block,
|
|
3
3
|
BlockServices
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-MDAWYZBI.js";
|
|
5
5
|
import {
|
|
6
6
|
IconCheck,
|
|
7
7
|
IconCopy,
|
|
8
8
|
IconThumbDown,
|
|
9
9
|
IconThumbUp
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-XFJ6XMJZ.js";
|
|
11
|
+
import {
|
|
12
|
+
IconButton
|
|
13
|
+
} from "./chunk-PYDVANWI.js";
|
|
11
14
|
import {
|
|
12
15
|
typographyMixin,
|
|
13
16
|
typographyTypes
|
|
14
|
-
} from "./chunk-
|
|
15
|
-
import {
|
|
16
|
-
IconButton
|
|
17
|
-
} from "./chunk-OBOXCBDL.js";
|
|
17
|
+
} from "./chunk-UXT2H3FM.js";
|
|
18
18
|
import {
|
|
19
19
|
colors
|
|
20
20
|
} from "./chunk-JF3P66JF.js";
|
|
@@ -220,4 +220,4 @@ export {
|
|
|
220
220
|
MESSAGE_ROLE,
|
|
221
221
|
ChatMessage
|
|
222
222
|
};
|
|
223
|
-
//# sourceMappingURL=chunk-
|
|
223
|
+
//# sourceMappingURL=chunk-3THOTQO3.js.map
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
import {
|
|
5
5
|
typographyMixin,
|
|
6
6
|
typographyTypes
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-UXT2H3FM.js";
|
|
8
8
|
import {
|
|
9
9
|
colors
|
|
10
10
|
} from "./chunk-JF3P66JF.js";
|
|
@@ -73,4 +73,4 @@ var TextArea = forwardRef(function TextArea2({ value, onChange, placeholder, max
|
|
|
73
73
|
export {
|
|
74
74
|
TextArea
|
|
75
75
|
};
|
|
76
|
-
//# sourceMappingURL=chunk-
|
|
76
|
+
//# sourceMappingURL=chunk-7OYIDM42.js.map
|
|
@@ -1,20 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
typographyMixin,
|
|
3
|
-
typographyTypes
|
|
4
|
-
} from "./chunk-SV24ONND.js";
|
|
5
1
|
import {
|
|
6
2
|
Icon
|
|
7
3
|
} from "./chunk-OLJIJYB5.js";
|
|
4
|
+
import {
|
|
5
|
+
typographyMixin,
|
|
6
|
+
typographyTypes
|
|
7
|
+
} from "./chunk-UXT2H3FM.js";
|
|
8
8
|
import {
|
|
9
9
|
colors
|
|
10
10
|
} from "./chunk-JF3P66JF.js";
|
|
11
11
|
|
|
12
|
-
// src/components/button/button.tsx
|
|
13
|
-
import { forwardRef } from "react";
|
|
14
|
-
|
|
15
|
-
// src/components/button/button-styles.ts
|
|
16
|
-
import styled, { css } from "styled-components";
|
|
17
|
-
|
|
18
12
|
// src/components/button/constants.ts
|
|
19
13
|
var BUTTON_VARIANT = {
|
|
20
14
|
SOLID: "solid",
|
|
@@ -35,7 +29,11 @@ var ICON_POSITION = {
|
|
|
35
29
|
TRAILING: "trailing"
|
|
36
30
|
};
|
|
37
31
|
|
|
32
|
+
// src/components/button/button.tsx
|
|
33
|
+
import { forwardRef } from "react";
|
|
34
|
+
|
|
38
35
|
// src/components/button/button-styles.ts
|
|
36
|
+
import styled, { css } from "styled-components";
|
|
39
37
|
var intentColors = {
|
|
40
38
|
[BUTTON_INTENT.PRIMARY]: {
|
|
41
39
|
base: colors["brown-100"],
|
|
@@ -174,4 +172,4 @@ export {
|
|
|
174
172
|
ICON_POSITION,
|
|
175
173
|
Button
|
|
176
174
|
};
|
|
177
|
-
//# sourceMappingURL=chunk-
|
|
175
|
+
//# sourceMappingURL=chunk-B4U37WIH.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/button/constants.ts","../src/components/button/button.tsx","../src/components/button/button-styles.ts"],"sourcesContent":["/** Available visual style variants for the Button component. */\nexport const BUTTON_VARIANT = {\n SOLID: \"solid\",\n OUTLINE: \"outline\",\n GHOST: \"ghost\",\n} as const;\n\n/** Available size presets for the Button component. */\nexport const BUTTON_SIZE = {\n XS: \"xs\",\n S: \"s\",\n M: \"m\",\n} as const;\n\n/** Available semantic intents for the Button component. */\nexport const BUTTON_INTENT = {\n PRIMARY: \"primary\",\n ERROR: \"error\",\n} as const;\n\n/** Available icon positions relative to the label text. */\nexport const ICON_POSITION = {\n LEADING: \"leading\",\n TRAILING: \"trailing\",\n} as const;\n","import { forwardRef } from \"react\";\nimport { Icon } from \"../icon/icon\";\nimport { StyledButton } from \"./button-styles\";\nimport { BUTTON_INTENT, BUTTON_SIZE, BUTTON_VARIANT, ICON_POSITION } from \"./constants\";\nimport type { ButtonProps } from \"./types\";\n\n/** A versatile button component with multiple variants and sizes. */\nexport const Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button(\n {\n children,\n variant = BUTTON_VARIANT.SOLID,\n intent = BUTTON_INTENT.PRIMARY,\n size = BUTTON_SIZE.M,\n icon,\n iconPosition = ICON_POSITION.LEADING,\n disabled = false,\n onClick,\n className,\n \"aria-label\": ariaLabel,\n },\n ref\n) {\n const iconElement = icon ? <Icon icon={icon} size={16} /> : null;\n\n return (\n <StyledButton\n ref={ref}\n type=\"button\"\n $variant={variant}\n $intent={intent}\n $size={size}\n disabled={disabled}\n onClick={disabled ? undefined : onClick}\n className={className}\n aria-label={ariaLabel}\n >\n {iconPosition === ICON_POSITION.LEADING && iconElement}\n <span>{children}</span>\n {iconPosition === ICON_POSITION.TRAILING && iconElement}\n </StyledButton>\n );\n});\n","import styled, { css } from \"styled-components\";\nimport { colors } from \"../../tokens/colors\";\nimport { typographyMixin, typographyTypes } from \"../../tokens/typography\";\nimport { BUTTON_INTENT, BUTTON_SIZE, BUTTON_VARIANT } from \"./constants\";\nimport type { ButtonIntent, ButtonSize, ButtonVariant } from \"./types\";\n\ninterface IntentColors {\n /** Primary fill / text color. */\n base: string;\n /** Hover fill for solid variant. */\n hover: string;\n /** Muted fill for disabled solid variant. */\n disabledBg: string;\n /** Muted text for disabled state. */\n disabledText: string;\n /** Subtle hover background for outline/ghost variants. */\n subtleHover: string;\n /** Border color for outline variant. */\n border: string;\n /** Muted border for disabled outline variant. */\n disabledBorder: string;\n}\n\nconst intentColors: Record<ButtonIntent, IntentColors> = {\n [BUTTON_INTENT.PRIMARY]: {\n base: colors[\"brown-100\"],\n hover: colors[\"brown-90\"],\n disabledBg: colors[\"brown-20\"],\n disabledText: colors[\"brown-50\"],\n subtleHover: colors[\"brown-20\"],\n border: colors[\"brown-40\"],\n disabledBorder: colors[\"brown-20\"],\n },\n [BUTTON_INTENT.ERROR]: {\n base: colors[\"red-500\"],\n hover: colors[\"red-600\"],\n disabledBg: colors[\"red-100\"],\n disabledText: colors[\"red-200\"],\n subtleHover: colors[\"red-50\"],\n border: colors[\"red-200\"],\n disabledBorder: colors[\"red-100\"],\n },\n};\n\ninterface SizeValues {\n padding: string;\n borderRadius: string;\n gap: string;\n}\n\nconst sizeValues: Record<ButtonSize, SizeValues> = {\n [BUTTON_SIZE.XS]: { padding: \"6px 10px\", borderRadius: \"8px\", gap: \"4px\" },\n [BUTTON_SIZE.S]: { padding: \"8px 12px\", borderRadius: \"10px\", gap: \"6px\" },\n [BUTTON_SIZE.M]: { padding: \"10px 14px\", borderRadius: \"10px\", gap: \"6px\" },\n};\n\nfunction sizeMixin({ $size }: { $size: ButtonSize }): ReturnType<typeof css> {\n const s = sizeValues[$size];\n return css`\n padding: ${s.padding};\n border-radius: ${s.borderRadius};\n gap: ${s.gap};\n `;\n}\n\ninterface VariantColorMap {\n background: string;\n color: string;\n border: string;\n hoverBg: string;\n disabledBg?: string;\n disabledColor: string;\n disabledBorder?: string;\n}\n\nconst variantColorMap: Record<ButtonVariant, (c: IntentColors) => VariantColorMap> = {\n [BUTTON_VARIANT.SOLID]: (c) => ({\n background: c.base,\n color: colors.white,\n border: \"none\",\n hoverBg: c.hover,\n disabledBg: c.disabledBg,\n disabledColor: c.disabledText,\n }),\n [BUTTON_VARIANT.OUTLINE]: (c) => ({\n background: colors.white,\n color: c.base,\n border: `1px solid ${c.border}`,\n hoverBg: c.subtleHover,\n disabledColor: c.disabledText,\n disabledBorder: c.disabledBorder,\n }),\n [BUTTON_VARIANT.GHOST]: (c) => ({\n background: \"transparent\",\n color: c.base,\n border: \"none\",\n hoverBg: c.subtleHover,\n disabledColor: c.disabledText,\n }),\n};\n\nfunction variantMixin({\n $variant,\n $intent,\n}: {\n $variant: ButtonVariant;\n $intent: ButtonIntent;\n}): ReturnType<typeof css> {\n const v = variantColorMap[$variant](intentColors[$intent]);\n\n return css`\n background: ${v.background};\n color: ${v.color};\n border: ${v.border};\n\n &:hover:not(:disabled) {\n background: ${v.hoverBg};\n }\n\n &:disabled {\n color: ${v.disabledColor};\n ${v.disabledBg ? `background: ${v.disabledBg};` : \"\"}\n ${v.disabledBorder ? `border-color: ${v.disabledBorder};` : \"\"}\n }\n `;\n}\n\n/** Styled button element with variant, intent, and size transient props. */\nexport const StyledButton = styled.button<{\n $variant: ButtonVariant;\n $intent: ButtonIntent;\n $size: ButtonSize;\n}>`\n display: inline-flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n ${typographyMixin(typographyTypes.GEIST_BODY_XS_MEDIUM)};\n transition: background-color 150ms ease, border-color 150ms ease, color 150ms ease;\n\n &:disabled {\n cursor: not-allowed;\n }\n\n ${sizeMixin}\n ${variantMixin}\n`;\n"],"mappings":"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","names":["Button"]}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import {
|
|
2
|
+
TextArea
|
|
3
|
+
} from "./chunk-7OYIDM42.js";
|
|
4
|
+
import {
|
|
5
|
+
BUTTON_INTENT,
|
|
6
|
+
BUTTON_SIZE,
|
|
7
|
+
Button
|
|
8
|
+
} from "./chunk-B4U37WIH.js";
|
|
9
|
+
import {
|
|
10
|
+
cn
|
|
11
|
+
} from "./chunk-P6GUNIAE.js";
|
|
12
|
+
|
|
13
|
+
// src/components/ai-elements/prompt-input/prompt-input.tsx
|
|
14
|
+
import { forwardRef, useCallback } from "react";
|
|
15
|
+
|
|
16
|
+
// src/components/ai-elements/prompt-input/types.ts
|
|
17
|
+
var CHAT_STATUS = {
|
|
18
|
+
/** Idle / ready to submit. */
|
|
19
|
+
READY: "ready",
|
|
20
|
+
/** The last submission errored — the button shows a retry affordance. */
|
|
21
|
+
ERROR: "error"
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// src/components/ai-elements/prompt-input/prompt-input.tsx
|
|
25
|
+
import { jsx } from "react/jsx-runtime";
|
|
26
|
+
var PromptInput = ({ className, onSubmit, disabled, children, ...props }) => {
|
|
27
|
+
const handleSubmit = useCallback(
|
|
28
|
+
(event) => {
|
|
29
|
+
event.preventDefault();
|
|
30
|
+
if (disabled) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const textarea = event.currentTarget.querySelector("textarea");
|
|
34
|
+
const text = textarea?.value ?? "";
|
|
35
|
+
if (!text.trim()) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
onSubmit({ text }, event);
|
|
39
|
+
},
|
|
40
|
+
[disabled, onSubmit]
|
|
41
|
+
);
|
|
42
|
+
return /* @__PURE__ */ jsx(
|
|
43
|
+
"form",
|
|
44
|
+
{
|
|
45
|
+
className: cn(
|
|
46
|
+
"flex w-full flex-col overflow-hidden rounded-xl border bg-background shadow-lg shadow-foreground/5",
|
|
47
|
+
className
|
|
48
|
+
),
|
|
49
|
+
onSubmit: handleSubmit,
|
|
50
|
+
...props,
|
|
51
|
+
children
|
|
52
|
+
}
|
|
53
|
+
);
|
|
54
|
+
};
|
|
55
|
+
var PromptInputTextarea = forwardRef(
|
|
56
|
+
function PromptInputTextarea2({ className, onKeyDown, placeholder = "What would you like to know?", ...props }, ref) {
|
|
57
|
+
const handleKeyDown = useCallback(
|
|
58
|
+
(event) => {
|
|
59
|
+
onKeyDown?.(event);
|
|
60
|
+
if (event.defaultPrevented) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
if (event.key === "Enter" && !event.shiftKey && !event.nativeEvent.isComposing) {
|
|
64
|
+
event.preventDefault();
|
|
65
|
+
event.currentTarget.form?.requestSubmit();
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
[onKeyDown]
|
|
69
|
+
);
|
|
70
|
+
return /* @__PURE__ */ jsx("div", { className: cn("py-3", className), children: /* @__PURE__ */ jsx(TextArea, { ref, className: "px-4", onKeyDown: handleKeyDown, placeholder, ...props }) });
|
|
71
|
+
}
|
|
72
|
+
);
|
|
73
|
+
var PromptInputFooter = ({ className, ...props }) => /* @__PURE__ */ jsx("div", { className: cn("flex items-center justify-between gap-1 px-4 pb-3", className), ...props });
|
|
74
|
+
var PromptInputTools = ({ className, ...props }) => /* @__PURE__ */ jsx("div", { className: cn("flex min-w-0 items-center gap-1", className), ...props });
|
|
75
|
+
var PromptInputSubmit = ({
|
|
76
|
+
label = "Submit",
|
|
77
|
+
status = CHAT_STATUS.READY,
|
|
78
|
+
disabled,
|
|
79
|
+
className
|
|
80
|
+
}) => {
|
|
81
|
+
const handleClick = useCallback((event) => {
|
|
82
|
+
event.currentTarget.form?.requestSubmit();
|
|
83
|
+
}, []);
|
|
84
|
+
return /* @__PURE__ */ jsx(
|
|
85
|
+
Button,
|
|
86
|
+
{
|
|
87
|
+
intent: status === CHAT_STATUS.ERROR ? BUTTON_INTENT.ERROR : BUTTON_INTENT.PRIMARY,
|
|
88
|
+
size: BUTTON_SIZE.XS,
|
|
89
|
+
className,
|
|
90
|
+
disabled,
|
|
91
|
+
onClick: handleClick,
|
|
92
|
+
children: label
|
|
93
|
+
}
|
|
94
|
+
);
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
// src/components/ai-elements/mouse-grid/mouse-grid.tsx
|
|
98
|
+
import { useEffect, useRef } from "react";
|
|
99
|
+
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
100
|
+
function MouseGrid({ glowColor } = {}) {
|
|
101
|
+
const layerRef = useRef(null);
|
|
102
|
+
useEffect(() => {
|
|
103
|
+
let raf = 0;
|
|
104
|
+
let clientX = 0;
|
|
105
|
+
let clientY = 0;
|
|
106
|
+
function flush() {
|
|
107
|
+
raf = 0;
|
|
108
|
+
const layer = layerRef.current;
|
|
109
|
+
if (!layer) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
const rect = layer.getBoundingClientRect();
|
|
113
|
+
layer.style.setProperty("--vibe-mx", `${clientX - rect.left}px`);
|
|
114
|
+
layer.style.setProperty("--vibe-my", `${clientY - rect.top}px`);
|
|
115
|
+
}
|
|
116
|
+
function onMove(event) {
|
|
117
|
+
clientX = event.clientX;
|
|
118
|
+
clientY = event.clientY;
|
|
119
|
+
if (raf) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
raf = requestAnimationFrame(flush);
|
|
123
|
+
}
|
|
124
|
+
window.addEventListener("mousemove", onMove, { passive: true });
|
|
125
|
+
return () => {
|
|
126
|
+
window.removeEventListener("mousemove", onMove);
|
|
127
|
+
if (raf) {
|
|
128
|
+
cancelAnimationFrame(raf);
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
}, []);
|
|
132
|
+
const glowStyle = glowColor ? { "--vibe-glow-color": glowColor } : void 0;
|
|
133
|
+
return /* @__PURE__ */ jsxs("div", { ref: layerRef, "aria-hidden": "true", className: "vibe-mouse-grid-layer", children: [
|
|
134
|
+
/* @__PURE__ */ jsx2("div", { className: "vibe-mouse-grid" }),
|
|
135
|
+
/* @__PURE__ */ jsx2("div", { className: "vibe-mouse-glow", style: glowStyle })
|
|
136
|
+
] });
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// src/components/ai-elements/suggestion/suggestion.tsx
|
|
140
|
+
import { useCallback as useCallback2 } from "react";
|
|
141
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
142
|
+
var Suggestions = ({ className, ...props }) => /* @__PURE__ */ jsx3("div", { className: cn("flex flex-wrap items-center justify-center gap-3", className), ...props });
|
|
143
|
+
var Suggestion = ({ suggestion, onClick, className, children, ...props }) => {
|
|
144
|
+
const handleClick = useCallback2(() => onClick?.(suggestion), [onClick, suggestion]);
|
|
145
|
+
return /* @__PURE__ */ jsx3(
|
|
146
|
+
"button",
|
|
147
|
+
{
|
|
148
|
+
type: "button",
|
|
149
|
+
onClick: handleClick,
|
|
150
|
+
className: cn(
|
|
151
|
+
"cursor-pointer whitespace-nowrap rounded-full border border-brown-30 bg-white px-4 py-2 text-sm text-brown-100 transition-colors hover:bg-brown-10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brown-40",
|
|
152
|
+
className
|
|
153
|
+
),
|
|
154
|
+
...props,
|
|
155
|
+
children: children ?? suggestion
|
|
156
|
+
}
|
|
157
|
+
);
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
export {
|
|
161
|
+
CHAT_STATUS,
|
|
162
|
+
PromptInput,
|
|
163
|
+
PromptInputTextarea,
|
|
164
|
+
PromptInputFooter,
|
|
165
|
+
PromptInputTools,
|
|
166
|
+
PromptInputSubmit,
|
|
167
|
+
MouseGrid,
|
|
168
|
+
Suggestions,
|
|
169
|
+
Suggestion
|
|
170
|
+
};
|
|
171
|
+
//# sourceMappingURL=chunk-BR5S37SC.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/ai-elements/prompt-input/prompt-input.tsx","../src/components/ai-elements/prompt-input/types.ts","../src/components/ai-elements/mouse-grid/mouse-grid.tsx","../src/components/ai-elements/suggestion/suggestion.tsx"],"sourcesContent":["import { forwardRef, useCallback } from \"react\";\nimport type { FormEventHandler, KeyboardEventHandler, MouseEventHandler } from \"react\";\n\nimport { cn } from \"@/lib/utils\";\nimport { Button } from \"@/components/button/button\";\nimport { BUTTON_INTENT, BUTTON_SIZE } from \"@/components/button/constants\";\nimport { TextArea } from \"@/components/text-area/text-area\";\nimport { CHAT_STATUS } from \"./types\";\nimport type {\n PromptInputFooterProps,\n PromptInputProps,\n PromptInputSubmitProps,\n PromptInputTextareaProps,\n PromptInputToolsProps,\n} from \"./types\";\n\n/* ------------------------------------------------------------------------------------------------\n * Originally vendored from `@vercel/ai-elements`'s `prompt-input` registry entry. Slimmed hard to\n * the creation-surface essentials a consumer composes into a prompt-launcher hero: `PromptInput`\n * (form wrapper), `PromptInputTextarea`, `PromptInputFooter`, `PromptInputTools`, `PromptInputSubmit`.\n *\n * Everything else from the upstream entry is intentionally dropped: the action menu\n * (`PromptInputActionMenu*`), attachment / screenshot actions, the attachments strip, the\n * provider/controller contexts, and the `Select` / `HoverCard` / `Command` / `Tab` families. That\n * also sheds the upstream `nanoid` (id-per-attachment) dependency.\n *\n * Rewiring to clay's single-sourced primitives let this primitive ship with ZERO new runtime\n * dependencies — diverging from the original shared spec, which assumed the kept set would still\n * lean on `@base-ui/react`, `ai`, and `lucide-react`:\n * - upstream `InputGroupButton` (submit) → clay `Button` (filled, `type=\"button\"`, consumer label).\n * - upstream `InputGroupTextarea` → clay `TextArea` (auto-resize, transparent chrome).\n * - `lucide-react` icons → clay's already-bundled `@tabler/icons-react`.\n * - `ai`'s `ChatStatus` → a local ready/error status (see `./types`).\n *\n * Because clay's `Button` always renders `type=\"button\"`, native form submit isn't available;\n * both the Enter-key handler and the submit button trigger `form.requestSubmit()` explicitly. And\n * because clay's `TextArea` doesn't forward a `name` attribute, `PromptInput` reads the textarea's\n * value straight off the DOM at submit time rather than via `FormData` — keeping the form wrapper\n * decoupled from whoever controls the textarea's value.\n *\n * Types live in `./types`.\n * ------------------------------------------------------------------------------------------------ */\n\n/**\n * Form wrapper for a prompt composer. Renders a bordered, rounded surface; stacks its children\n * (textarea + footer) vertically. Reads the nested textarea's value on submit and forwards it to\n * `onSubmit`. Empty / whitespace-only submissions, and all submissions while `disabled`, are\n * ignored.\n */\nexport const PromptInput = ({ className, onSubmit, disabled, children, ...props }: PromptInputProps) => {\n const handleSubmit: FormEventHandler<HTMLFormElement> = useCallback(\n (event) => {\n event.preventDefault();\n if (disabled) {\n return;\n }\n const textarea = event.currentTarget.querySelector(\"textarea\");\n const text = textarea?.value ?? \"\";\n if (!text.trim()) {\n return;\n }\n onSubmit({ text }, event);\n },\n [disabled, onSubmit]\n );\n\n return (\n <form\n className={cn(\n \"flex w-full flex-col overflow-hidden rounded-xl border bg-background shadow-lg shadow-foreground/5\",\n className\n )}\n onSubmit={handleSubmit}\n {...props}\n >\n {children}\n </form>\n );\n};\n\n/**\n * Auto-resizing textarea for the prompt composer. Wraps clay's `TextArea` and adds Enter-to-submit\n * (Shift+Enter inserts a newline; submission is suppressed while an IME composition is active). The\n * consumer's `onKeyDown` runs first and can `preventDefault()` to opt out of the built-in Enter\n * handling.\n *\n * Horizontal padding lives on the textarea itself (not the wrapper) so its overflow scrollbar sits\n * flush against the wrapper's right edge; the wrapper only owns the vertical padding.\n */\nexport const PromptInputTextarea = forwardRef<HTMLTextAreaElement, PromptInputTextareaProps>(\n function PromptInputTextarea({ className, onKeyDown, placeholder = \"What would you like to know?\", ...props }, ref) {\n const handleKeyDown: KeyboardEventHandler<HTMLTextAreaElement> = useCallback(\n (event) => {\n onKeyDown?.(event);\n if (event.defaultPrevented) {\n return;\n }\n if (event.key === \"Enter\" && !event.shiftKey && !event.nativeEvent.isComposing) {\n event.preventDefault();\n event.currentTarget.form?.requestSubmit();\n }\n },\n [onKeyDown]\n );\n\n return (\n <div className={cn(\"py-3\", className)}>\n <TextArea ref={ref} className=\"px-4\" onKeyDown={handleKeyDown} placeholder={placeholder} {...props} />\n </div>\n );\n }\n);\n\n/**\n * Footer row beneath the textarea — left tools and the right-aligned submit button. Horizontal\n * padding matches {@link PromptInputTextarea}'s (`px-4`) so the tools/submit align with the\n * textarea's text edges.\n */\nexport const PromptInputFooter = ({ className, ...props }: PromptInputFooterProps) => (\n <div className={cn(\"flex items-center justify-between gap-1 px-4 pb-3\", className)} {...props} />\n);\n\n/** Left-aligned cluster inside {@link PromptInputFooter} (e.g. the submit hint). */\nexport const PromptInputTools = ({ className, ...props }: PromptInputToolsProps) => (\n <div className={cn(\"flex min-w-0 items-center gap-1\", className)} {...props} />\n);\n\n/**\n * Submit button for the prompt composer — a filled clay `Button` whose text is the consumer's\n * `label` (pass a localized string for i18n). Always submits the form via `form.requestSubmit()` on\n * click (clay buttons render `type=\"button\"`); the consumer disables it while a generation is in\n * flight. `status` drives the color: `error` renders the error (red) intent, otherwise primary.\n */\nexport const PromptInputSubmit = ({\n label = \"Submit\",\n status = CHAT_STATUS.READY,\n disabled,\n className,\n}: PromptInputSubmitProps) => {\n const handleClick = useCallback<MouseEventHandler<HTMLButtonElement>>((event) => {\n event.currentTarget.form?.requestSubmit();\n }, []);\n\n return (\n <Button\n intent={status === CHAT_STATUS.ERROR ? BUTTON_INTENT.ERROR : BUTTON_INTENT.PRIMARY}\n size={BUTTON_SIZE.XS}\n className={className}\n disabled={disabled}\n onClick={handleClick}\n >\n {label}\n </Button>\n );\n};\n","import type { FormEvent, HTMLAttributes } from \"react\";\n\nimport type { TextAreaProps } from \"@/components/text-area/types\";\n\n/**\n * Submission status the submit button reflects. The composer has no cancellation and no\n * intermediate \"generating\" state of its own — the consumer disables the button while busy — so\n * only the idle and error states are modeled here.\n */\nexport const CHAT_STATUS = {\n /** Idle / ready to submit. */\n READY: \"ready\",\n /** The last submission errored — the button shows a retry affordance. */\n ERROR: \"error\",\n} as const;\n\n/** Submission status the submit button reflects. */\nexport type ChatStatus = (typeof CHAT_STATUS)[keyof typeof CHAT_STATUS];\n\n/** Payload emitted by {@link PromptInput} on submit. v1 carries only text (attachments are cut). */\nexport interface PromptInputMessage {\n /** The current textarea value at submit time. */\n text: string;\n}\n\n/** Props for the {@link PromptInput} form wrapper. */\nexport type PromptInputProps = Omit<HTMLAttributes<HTMLFormElement>, \"onSubmit\"> & {\n /**\n * Fired when the form is submitted (Enter key or the submit button) with non-empty text.\n * Receives the captured message and the underlying form event.\n */\n onSubmit: (message: PromptInputMessage, event: FormEvent<HTMLFormElement>) => void;\n /**\n * Blocks submission across every trigger (Enter key, submit button, programmatic) when `true` —\n * e.g. while a generation is in flight. The single gate that keeps the Enter path in sync with a\n * disabled submit button. @default false\n */\n disabled?: boolean;\n};\n\n/** Props for {@link PromptInputTextarea} — clay `TextArea` props; `className` styles the wrapper. */\nexport type PromptInputTextareaProps = TextAreaProps;\n\n/** Props for {@link PromptInputFooter}. */\nexport type PromptInputFooterProps = HTMLAttributes<HTMLDivElement>;\n\n/** Props for {@link PromptInputTools}. */\nexport type PromptInputToolsProps = HTMLAttributes<HTMLDivElement>;\n\n/** Props for {@link PromptInputSubmit}. */\nexport interface PromptInputSubmitProps {\n /** Button label — pass a localized string (i18n) from the consumer. @default \"Submit\" */\n label?: string;\n /** Drives the button color: `error` renders the red error intent, otherwise primary. @default \"ready\" */\n status?: ChatStatus;\n /** Whether the button is non-interactive (e.g. empty input or a generation in flight). */\n disabled?: boolean;\n /** Additional CSS class name. */\n className?: string;\n}\n","import { useEffect, useRef, type CSSProperties } from \"react\";\n\nimport type { MouseGridProps } from \"./types\";\n\n/**\n * Absolutely-positioned grid backdrop, confined to its nearest positioned ancestor (give the\n * parent `position: relative` + `overflow: hidden` to clip it). The grid is revealed only near the\n * cursor via a radial mask whose center is driven by the `--vibe-mx` / `--vibe-my` CSS vars,\n * updated by a rAF-throttled `mousemove` listener. Renders behind all content (`z-0`) and never\n * blocks pointer events.\n *\n * Cursor coordinates are translated from viewport space (`clientX` / `clientY`) into layer-local\n * space (minus the layer's `getBoundingClientRect()` origin) and written onto the layer element\n * itself — not the document root — so the glow tracks correctly even though the layer no longer\n * fills the viewport. The rect read happens inside the rAF so it batches with paint.\n *\n * Pure `useEffect` + `requestAnimationFrame`, no animation library. The `.vibe-mouse-grid-layer` /\n * `.vibe-mouse-grid` / `.vibe-mouse-glow` styles ship in `@shapesos/clay/blocks/styles.css`.\n */\nexport function MouseGrid({ glowColor }: MouseGridProps = {}) {\n const layerRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n let raf = 0;\n let clientX = 0;\n let clientY = 0;\n function flush() {\n raf = 0;\n const layer = layerRef.current;\n if (!layer) {\n return;\n }\n const rect = layer.getBoundingClientRect();\n layer.style.setProperty(\"--vibe-mx\", `${clientX - rect.left}px`);\n layer.style.setProperty(\"--vibe-my\", `${clientY - rect.top}px`);\n }\n function onMove(event: MouseEvent) {\n clientX = event.clientX;\n clientY = event.clientY;\n if (raf) {\n return;\n }\n raf = requestAnimationFrame(flush);\n }\n window.addEventListener(\"mousemove\", onMove, { passive: true });\n return () => {\n window.removeEventListener(\"mousemove\", onMove);\n if (raf) {\n cancelAnimationFrame(raf);\n }\n };\n }, []);\n\n const glowStyle = glowColor ? ({ \"--vibe-glow-color\": glowColor } as CSSProperties) : undefined;\n\n return (\n <div ref={layerRef} aria-hidden=\"true\" className=\"vibe-mouse-grid-layer\">\n <div className=\"vibe-mouse-grid\" />\n <div className=\"vibe-mouse-glow\" style={glowStyle} />\n </div>\n );\n}\n","import { useCallback } from \"react\";\n\nimport { cn } from \"@/lib/utils\";\nimport type { SuggestionProps, SuggestionsProps } from \"./types\";\n\n/* ------------------------------------------------------------------------------------------------\n * Vendored + slimmed from `@vercel/ai-elements`'s `suggestion` registry entry. Upstream wraps the\n * pills in a shadcn `ScrollArea` and renders each as a shadcn `Button variant=\"outline\"`; clay has\n * neither, so this ships a dependency-free row (`flex flex-wrap`) of Tailwind-styled pills using\n * clay's named color tokens. Engine note: this is vendored ai-elements chrome, so it's Tailwind —\n * clay's `Button` is styled-components with fixed radii and can't produce the rounded-full pill.\n * ------------------------------------------------------------------------------------------------ */\n\n/**\n * A centered, wrapping row of {@link Suggestion} pills — e.g. starter prompts beneath a composer.\n * Compose `Suggestion` children inside.\n */\nexport const Suggestions = ({ className, ...props }: SuggestionsProps) => (\n <div className={cn(\"flex flex-wrap items-center justify-center gap-3\", className)} {...props} />\n);\n\n/**\n * A single tappable suggestion pill. Renders `children` if provided, otherwise the `suggestion`\n * text, and calls `onClick(suggestion)` when clicked.\n */\nexport const Suggestion = ({ suggestion, onClick, className, children, ...props }: SuggestionProps) => {\n const handleClick = useCallback(() => onClick?.(suggestion), [onClick, suggestion]);\n\n return (\n <button\n type=\"button\"\n onClick={handleClick}\n className={cn(\n \"cursor-pointer whitespace-nowrap rounded-full border border-brown-30 bg-white px-4 py-2 text-sm text-brown-100 transition-colors hover:bg-brown-10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brown-40\",\n className\n )}\n {...props}\n >\n {children ?? suggestion}\n </button>\n );\n};\n"],"mappings":"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","names":["PromptInputTextarea","jsx","useCallback","jsx","useCallback"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=chunk-CYC2YUKU.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=chunk-L75JPWGS.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -4,11 +4,11 @@ import {
|
|
|
4
4
|
import {
|
|
5
5
|
ArtifactServices,
|
|
6
6
|
artifactToClipboardText
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-XFJ6XMJZ.js";
|
|
8
8
|
import {
|
|
9
9
|
typographyMixin,
|
|
10
10
|
typographyTypes
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-UXT2H3FM.js";
|
|
12
12
|
import {
|
|
13
13
|
colors
|
|
14
14
|
} from "./chunk-JF3P66JF.js";
|
|
@@ -119,6 +119,18 @@ var TextBlockWrapper = styled2.div`
|
|
|
119
119
|
margin-block: 4px;
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
+
& ul {
|
|
123
|
+
list-style-type: disc;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
& ul ul {
|
|
127
|
+
list-style-type: circle;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
& ul ul ul {
|
|
131
|
+
list-style-type: square;
|
|
132
|
+
}
|
|
133
|
+
|
|
122
134
|
& ol {
|
|
123
135
|
list-style-type: decimal;
|
|
124
136
|
}
|
|
@@ -235,4 +247,4 @@ export {
|
|
|
235
247
|
BlockServices,
|
|
236
248
|
Block
|
|
237
249
|
};
|
|
238
|
-
//# sourceMappingURL=chunk-
|
|
250
|
+
//# sourceMappingURL=chunk-MDAWYZBI.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/blocks/types.ts","../src/components/blocks/artifact-ref-block/artifact-ref-block.tsx","../src/components/blocks/block-services/artifact-ref-block-service.ts","../src/components/blocks/text-block/text-block.tsx","../src/components/blocks/scrollable-table/scrollable-table-styles.ts","../src/components/blocks/scrollable-table/scrollable-table.tsx","../src/components/blocks/text-block/sanitize.ts","../src/components/blocks/text-block/text-block-styles.ts","../src/components/blocks/block-services/text-block-service.ts","../src/components/blocks/block-services/index.ts","../src/components/blocks/block/block.tsx"],"sourcesContent":["import type { ComponentType } from \"react\";\n\nimport type { ArtifactCallbacksOf, ArtifactLabelsOf, ArtifactRecord } from \"@/components/artifacts/types\";\n\n/** Block-type discriminator constants. Mirrors the wire shape from io-server / shapes-agent. */\nexport const blockTypes = {\n TEXT: \"TEXT\",\n ARTIFACT_REF: \"ARTIFACT_REF\",\n} as const;\n\n/** Discriminator for content blocks. */\nexport type BlockType = (typeof blockTypes)[keyof typeof blockTypes];\n\n/** Plain-text content block — rendered as Markdown. */\nexport interface TextBlockRecord {\n /** Block-type discriminator. */\n type: typeof blockTypes.TEXT;\n /** Type-specific data; kept as `payload` to match the wire shape 1:1. */\n payload: {\n /** The Markdown source for this text segment. */\n text: string;\n };\n}\n\n/**\n * Reference-to-an-artifact content block — dispatched to the per-type artifact component.\n *\n * The artifact-domain types (`ArtifactRecord`, `TableArtifactRecord`, etc.) live in\n * `@/components/artifacts/types` so the artifact layer can be consumed independently of the\n * block layer (e.g. by a future surface that renders an artifact outside chat). The block layer\n * imports them here to wrap an artifact in an `ARTIFACT_REF` block.\n *\n * `ArtifactRefBlockRecord` is a *distributed* union over `ArtifactRecord` branches — TS narrows\n * `labels` and `callbacks` to the slice matching `artifact.type` via `ArtifactLabelsOf<T>` /\n * `ArtifactCallbacksOf<T>`. Without this distribution, `Partial<ChartLabels | TableLabels>`\n * collapses to `Partial<ChartLabels> | Partial<TableLabels>` and TS accepts chart-only keys on\n * a TABLE block's payload (the discriminator becomes load-bearing only at runtime, not in the\n * type system).\n */\nexport type ArtifactRefBlockRecord = ArtifactRecord extends infer TArtifact\n ? TArtifact extends ArtifactRecord\n ? {\n /** Block-type discriminator. */\n type: typeof blockTypes.ARTIFACT_REF;\n /** Type-specific data; kept as `payload` to match the wire shape 1:1. */\n payload: {\n /** The artifact's stable identifier, used for keying / future cross-message reuse. */\n artifactId: string;\n /** Inlined artifact metadata — clients fetch the underlying data via `artifact.protectedAsset.presignedUrl`. */\n artifact: TArtifact;\n /**\n * Optional consumer-supplied labels for the rendered artifact's action buttons + status\n * messages — a flat slice narrowed by `artifact.type`. NOT a wire field; the consumer\n * constructs this from their i18n layer when materialising the block-record from the\n * server payload, then passes the augmented block to `<Block>`. The dispatcher forwards\n * the slice directly to the matching renderer — no per-type key wrapping (the block\n * already carries the discriminator).\n *\n * TS catches cross-type assignments here: `block.payload.labels = { downloadCsv: 'X' }`\n * on a `TableArtifactRecord`-typed block is a type error because `downloadCsv` isn't a\n * `TableArtifactLabels` key.\n */\n labels?: Partial<ArtifactLabelsOf<TArtifact>>;\n /**\n * Optional side-effect callbacks for actions on the rendered artifact (e.g. `onDownload`).\n * Flat slice narrowed by `artifact.type` via `ArtifactCallbacksOf<T>`. NOT a wire field\n * — the consumer wires this once to bridge artifact actions to their analytics /\n * observability layer.\n */\n callbacks?: Partial<ArtifactCallbacksOf<TArtifact>>;\n };\n }\n : never\n : never;\n\n/** Discriminated union of all supported content blocks. Additive — new types extend this union. */\nexport type BlockRecord = TextBlockRecord | ArtifactRefBlockRecord;\n\n/** Props every concrete block component receives from the dispatcher. */\nexport interface BlockComponentProps<TBlock extends BlockRecord = BlockRecord> {\n /** The block to render. */\n block: TBlock;\n}\n\n/** Per-block-type service — registry entry that pairs a discriminator with its concrete component. */\nexport interface BlockService<TBlock extends BlockRecord = BlockRecord> {\n /** Discriminator value this service handles. */\n type: BlockType;\n /** React component that renders a block of this type. */\n Component: ComponentType<BlockComponentProps<TBlock>>;\n /**\n * Returns a plain-text or markdown string representation of this block suitable for clipboard\n * copy. Called by `copyMessageText` when the user clicks \"copy message\".\n *\n * - `TextBlockService`: returns `block.payload.text` verbatim (markdown preserved).\n * - `ArtifactRefBlockService`: delegates to `ArtifactServices[artifact.type].toClipboardText`.\n * Falls back to `[Artifact: <title>]` when the artifact type has no registered component.\n *\n * Optional so external `BlockService` implementors don't break on upgrade — when omitted, the\n * aggregator treats the block as contributing the empty string (silently skipped). Implementations\n * must be pure, synchronous functions — no async, no hooks, no side effects.\n */\n toClipboardText?: (block: TBlock) => string;\n}\n","import type { ComponentType, ReactNode } from \"react\";\n\nimport { ArtifactServices } from \"@/components/artifacts/artifact-services\";\nimport type { ArtifactRefBlockRecord, BlockComponentProps } from \"../types\";\n\n/**\n * Renders an `ARTIFACT_REF` block by dispatching the inlined artifact to its per-type component.\n *\n * The cast at the seam is necessary because the registry-based dispatch can't statically prove\n * type-T narrowing — but it's not hiding a bug. `ArtifactRefBlockRecord` is a distributed union\n * over `ArtifactRecord` branches (see `blocks/types.ts`), so TS catches wrong-shape labels at the\n * consumer's construction site: a chart-labels bag assigned to a TABLE block's payload is a type\n * error there, not silently forwarded to the wrong renderer here.\n *\n * Unknown artifact types render as `null` — the map only contains types we know how to render,\n * so an older clay client receiving a future artifact type degrades silently rather than\n * crashing the chat.\n */\nexport function ArtifactRefBlock({ block }: BlockComponentProps<ArtifactRefBlockRecord>): ReactNode {\n const { artifact, labels, callbacks } = block.payload;\n const entry = ArtifactServices[artifact.type];\n if (!entry) return null;\n // The registry stores `ArtifactService<ArtifactRecord>` (the union), so `entry.Component` is\n // `ComponentType<ArtifactServiceProps<ArtifactRecord>>`. The labels/callbacks props in the\n // narrower per-type `ArtifactServiceProps<TArtifact>` are `Partial<ArtifactLabelsOf<TArtifact>>`\n // which the union doesn't satisfy positionally. Cast forwards the values verbatim — the\n // runtime invariant is provably correct because the distributed `ArtifactRefBlockRecord` type\n // enforces it at construction.\n const Component = entry.Component as ComponentType<{\n artifact: typeof artifact;\n labels?: typeof labels;\n callbacks?: typeof callbacks;\n }>;\n return <Component artifact={artifact} labels={labels} callbacks={callbacks} />;\n}\n","import { artifactToClipboardText } from \"@/components/artifacts/artifact-services\";\nimport { ArtifactRefBlock } from \"../artifact-ref-block/artifact-ref-block\";\nimport { blockTypes } from \"../types\";\nimport type { ArtifactRefBlockRecord, BlockService } from \"../types\";\n\nexport const ArtifactRefBlockService: BlockService<ArtifactRefBlockRecord> = {\n type: blockTypes.ARTIFACT_REF,\n Component: ArtifactRefBlock,\n /**\n * Delegates to the registered artifact component's `toClipboardText`. Falls back to\n * `[Artifact: <title>]` via `artifactToClipboardText`'s own fallback when the artifact type\n * has no registered component, so copy never silently produces an empty string.\n */\n toClipboardText: (block) => artifactToClipboardText(block.payload.artifact),\n};\n","import type { ReactNode } from \"react\";\nimport ReactMarkdown from \"react-markdown\";\nimport remarkBreaks from \"remark-breaks\";\nimport remarkGfm from \"remark-gfm\";\n\nimport { ScrollableTable } from \"../scrollable-table/scrollable-table\";\nimport type { BlockComponentProps, TextBlockRecord } from \"../types\";\nimport { sanitize } from \"./sanitize\";\nimport { TextBlockWrapper } from \"./text-block-styles\";\n\nconst markdownComponents = {\n table: ((tableProps: { node?: unknown; children?: ReactNode }) => (\n <ScrollableTable {...tableProps} />\n )) as React.ComponentType<unknown>,\n};\n\n/** Renders a TEXT block as Markdown with GFM (tables, strikethrough) and line breaks. */\nexport function TextBlock({ block }: BlockComponentProps<TextBlockRecord>): ReactNode {\n return (\n <TextBlockWrapper>\n <ReactMarkdown remarkPlugins={[remarkGfm, remarkBreaks]} components={markdownComponents}>\n {sanitize(block.payload.text)}\n </ReactMarkdown>\n </TextBlockWrapper>\n );\n}\n","import styled from \"styled-components\";\n\nexport const TableContainer = styled.div`\n position: relative;\n margin-block: 8px 16px;\n`;\n\nexport const TableScroll = styled.div`\n width: 100%;\n overflow-x: auto;\n overflow-y: hidden;\n`;\n","import { type ReactNode } from \"react\";\n\nimport { TableContainer, TableScroll } from \"./scrollable-table-styles\";\n\n/** Props for the ScrollableTable component. */\ninterface ScrollableTableProps {\n /** Unused node prop from react-markdown. */\n node?: unknown;\n /** Table children elements. */\n children?: ReactNode;\n}\n\n/** Renders a markdown table inside a horizontally scrollable wrapper. Read-only — actions live on artifact-tables. */\nexport function ScrollableTable({ node: _node, ...props }: ScrollableTableProps): ReactNode {\n return (\n <TableContainer>\n <TableScroll>\n <table {...props} />\n </TableScroll>\n </TableContainer>\n );\n}\n","// Strips io-server's `<!-- table-title: ... -->` HTML-comment metadata before rendering.\n// The marker is emitted by the server's CSV-export pipeline so the export button can label\n// the file; it has no place in the visual output. If the server-side format changes\n// (prefix, spacing, closing-tag style), this filter silently stops matching and the\n// raw HTML comment leaks into rendered Markdown — keep this contract aligned with io-server.\nexport function sanitize(text: string): string {\n return text\n .split(\"\\n\")\n .filter((line) => {\n const t = line.trim();\n return !(t.startsWith(\"<!-- table-title:\") && t.endsWith(\"-->\"));\n })\n .join(\"\\n\");\n}\n","import styled from \"styled-components\";\n\nimport { colors } from \"@/tokens/colors\";\nimport { typographyMixin, typographyTypes } from \"@/tokens/typography\";\n\nexport const TextBlockWrapper = styled.div`\n ${typographyMixin(typographyTypes.GEIST_BODY_S_REGULAR)};\n color: ${colors[\"brown-100\"]};\n word-break: break-word;\n\n & h1 {\n ${typographyMixin(typographyTypes.GEIST_HEADING_S_BOLD)};\n margin-block: 20px;\n }\n\n & h2 {\n ${typographyMixin(typographyTypes.GEIST_BODY_L_SEMI_BOLD)};\n margin-block: 12px;\n }\n\n & h3,\n & h4,\n & h5,\n & h6 {\n ${typographyMixin(typographyTypes.GEIST_BODY_M_SEMI_BOLD)};\n margin-block: 8px;\n }\n\n & strong {\n ${typographyMixin(typographyTypes.GEIST_BODY_S_SEMI_BOLD)};\n }\n\n & p {\n margin-block: 0px;\n }\n\n & p + p {\n margin-block: 8px 0px;\n }\n\n & ul,\n & ol {\n margin-block: 16px;\n padding-left: 20px;\n }\n\n & li + li {\n margin-block: 4px;\n }\n\n & ol {\n list-style-type: decimal;\n }\n\n & ol ol {\n list-style-type: lower-alpha;\n }\n\n & ol ol ol {\n list-style-type: lower-roman;\n }\n\n & a {\n color: inherit;\n text-decoration-line: underline;\n text-decoration-style: dotted;\n text-decoration-thickness: auto;\n text-underline-offset: auto;\n text-underline-position: from-font;\n transition: color 75ms ease-in-out;\n\n &:hover {\n color: ${colors[\"blue-600\"]};\n }\n }\n\n & hr {\n border: none;\n border-top: 1px solid ${colors[\"brown-40\"]};\n margin-block: 20px;\n }\n\n & table {\n margin: 0;\n width: 100%;\n border-collapse: collapse;\n }\n\n & th,\n & td {\n border: none;\n border-bottom: 1px solid ${colors[\"brown-40\"]};\n text-align: left;\n }\n\n & th {\n ${typographyMixin(typographyTypes.GEIST_LABEL_CAPTION_MEDIUM)};\n padding: 8px;\n }\n\n & td {\n ${typographyMixin(typographyTypes.GEIST_BODY_XS_MEDIUM)};\n padding: 16px 8px;\n min-width: 150px;\n width: 1%;\n }\n\n & td strong {\n font-weight: inherit;\n font-size: inherit;\n line-height: inherit;\n }\n\n & blockquote {\n margin-block: 4px;\n margin-inline: 0px;\n padding-left: 16px;\n border-left: 4px solid ${colors[\"brown-40\"]};\n color: ${colors[\"brown-80\"]};\n }\n`;\n","import { markdownToPlainText } from \"@/utils/markdown-to-plain-text\";\nimport { TextBlock } from \"../text-block/text-block\";\nimport { blockTypes } from \"../types\";\nimport type { BlockService, TextBlockRecord } from \"../types\";\n\nexport const TextBlockService: BlockService<TextBlockRecord> = {\n type: blockTypes.TEXT,\n Component: TextBlock,\n /**\n * Converts the block's markdown source to plain text so the final clipboard value is clean\n * when pasted into tools that don't render markdown. Each block type owns its own clipboard\n * conversion so `useCopyToClipboard` can write text verbatim without a second pass.\n */\n toClipboardText: (block) => markdownToPlainText(block.payload.text),\n};\n","import { blockTypes, type BlockService, type BlockType } from \"../types\";\nimport { ArtifactRefBlockService } from \"./artifact-ref-block-service\";\nimport { TextBlockService } from \"./text-block-service\";\n\n// `BlockService<TBlock>` is invariant on `TBlock` via React's `ComponentType` (props are\n// contravariant). The concrete services overlap structurally with the base `BlockService` type\n// enough for a direct `as BlockService` cast. The registry dispatches by runtime discriminator —\n// `BlockServices[block.type]` always resolves to the matching concrete service — so the cast is sound.\nexport const BlockServices: Record<BlockType, BlockService> = {\n [blockTypes.TEXT]: TextBlockService as BlockService,\n [blockTypes.ARTIFACT_REF]: ArtifactRefBlockService as BlockService,\n};\n","import type { ReactNode } from \"react\";\n\nimport { BlockServices } from \"../block-services\";\nimport type { BlockRecord } from \"../types\";\n\n/** Props for the Block dispatcher. */\ninterface BlockProps {\n /** The block to render. Dispatched to the registered concrete component for its `type`. */\n block: BlockRecord;\n}\n\n/**\n * Dispatches a single block to its registered concrete component. Unknown types render as a no-op.\n *\n * Generic by design: per-block-type extras (e.g. localized artifact labels for `ARTIFACT_REF`)\n * live on the block's `payload`, not as sibling props here. That keeps the dispatcher's contract\n * shallow — third-party `BlockService` implementors only need to handle `{ block }` — and lets\n * the consumer materialise a fully-decorated block once (from the server payload + their i18n\n * layer) instead of threading sibling props through every dispatch site.\n */\nexport function Block({ block }: BlockProps): ReactNode {\n const service = BlockServices[block.type];\n if (!service) return null;\n const { Component } = service;\n return <Component block={block} />;\n}\n"],"mappings":"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","names":["jsx","styled","styled","jsx","jsx"]}
|
|
1
|
+
{"version":3,"sources":["../src/components/blocks/types.ts","../src/components/blocks/artifact-ref-block/artifact-ref-block.tsx","../src/components/blocks/block-services/artifact-ref-block-service.ts","../src/components/blocks/text-block/text-block.tsx","../src/components/blocks/scrollable-table/scrollable-table-styles.ts","../src/components/blocks/scrollable-table/scrollable-table.tsx","../src/components/blocks/text-block/sanitize.ts","../src/components/blocks/text-block/text-block-styles.ts","../src/components/blocks/block-services/text-block-service.ts","../src/components/blocks/block-services/index.ts","../src/components/blocks/block/block.tsx"],"sourcesContent":["import type { ComponentType } from \"react\";\n\nimport type { ArtifactCallbacksOf, ArtifactLabelsOf, ArtifactRecord } from \"@/components/artifacts/types\";\n\n/** Block-type discriminator constants. Mirrors the wire shape from io-server / shapes-agent. */\nexport const blockTypes = {\n TEXT: \"TEXT\",\n ARTIFACT_REF: \"ARTIFACT_REF\",\n} as const;\n\n/** Discriminator for content blocks. */\nexport type BlockType = (typeof blockTypes)[keyof typeof blockTypes];\n\n/** Plain-text content block — rendered as Markdown. */\nexport interface TextBlockRecord {\n /** Block-type discriminator. */\n type: typeof blockTypes.TEXT;\n /** Type-specific data; kept as `payload` to match the wire shape 1:1. */\n payload: {\n /** The Markdown source for this text segment. */\n text: string;\n };\n}\n\n/**\n * Reference-to-an-artifact content block — dispatched to the per-type artifact component.\n *\n * The artifact-domain types (`ArtifactRecord`, `TableArtifactRecord`, etc.) live in\n * `@/components/artifacts/types` so the artifact layer can be consumed independently of the\n * block layer (e.g. by a future surface that renders an artifact outside chat). The block layer\n * imports them here to wrap an artifact in an `ARTIFACT_REF` block.\n *\n * `ArtifactRefBlockRecord` is a *distributed* union over `ArtifactRecord` branches — TS narrows\n * `labels` and `callbacks` to the slice matching `artifact.type` via `ArtifactLabelsOf<T>` /\n * `ArtifactCallbacksOf<T>`. Without this distribution, `Partial<ChartLabels | TableLabels>`\n * collapses to `Partial<ChartLabels> | Partial<TableLabels>` and TS accepts chart-only keys on\n * a TABLE block's payload (the discriminator becomes load-bearing only at runtime, not in the\n * type system).\n */\nexport type ArtifactRefBlockRecord = ArtifactRecord extends infer TArtifact\n ? TArtifact extends ArtifactRecord\n ? {\n /** Block-type discriminator. */\n type: typeof blockTypes.ARTIFACT_REF;\n /** Type-specific data; kept as `payload` to match the wire shape 1:1. */\n payload: {\n /** The artifact's stable identifier, used for keying / future cross-message reuse. */\n artifactId: string;\n /** Inlined artifact metadata — clients fetch the underlying data via `artifact.protectedAsset.presignedUrl`. */\n artifact: TArtifact;\n /**\n * Optional consumer-supplied labels for the rendered artifact's action buttons + status\n * messages — a flat slice narrowed by `artifact.type`. NOT a wire field; the consumer\n * constructs this from their i18n layer when materialising the block-record from the\n * server payload, then passes the augmented block to `<Block>`. The dispatcher forwards\n * the slice directly to the matching renderer — no per-type key wrapping (the block\n * already carries the discriminator).\n *\n * TS catches cross-type assignments here: `block.payload.labels = { downloadCsv: 'X' }`\n * on a `TableArtifactRecord`-typed block is a type error because `downloadCsv` isn't a\n * `TableArtifactLabels` key.\n */\n labels?: Partial<ArtifactLabelsOf<TArtifact>>;\n /**\n * Optional side-effect callbacks for actions on the rendered artifact (e.g. `onDownload`).\n * Flat slice narrowed by `artifact.type` via `ArtifactCallbacksOf<T>`. NOT a wire field\n * — the consumer wires this once to bridge artifact actions to their analytics /\n * observability layer.\n */\n callbacks?: Partial<ArtifactCallbacksOf<TArtifact>>;\n };\n }\n : never\n : never;\n\n/** Discriminated union of all supported content blocks. Additive — new types extend this union. */\nexport type BlockRecord = TextBlockRecord | ArtifactRefBlockRecord;\n\n/** Props every concrete block component receives from the dispatcher. */\nexport interface BlockComponentProps<TBlock extends BlockRecord = BlockRecord> {\n /** The block to render. */\n block: TBlock;\n}\n\n/** Per-block-type service — registry entry that pairs a discriminator with its concrete component. */\nexport interface BlockService<TBlock extends BlockRecord = BlockRecord> {\n /** Discriminator value this service handles. */\n type: BlockType;\n /** React component that renders a block of this type. */\n Component: ComponentType<BlockComponentProps<TBlock>>;\n /**\n * Returns a plain-text or markdown string representation of this block suitable for clipboard\n * copy. Called by `copyMessageText` when the user clicks \"copy message\".\n *\n * - `TextBlockService`: returns `block.payload.text` verbatim (markdown preserved).\n * - `ArtifactRefBlockService`: delegates to `ArtifactServices[artifact.type].toClipboardText`.\n * Falls back to `[Artifact: <title>]` when the artifact type has no registered component.\n *\n * Optional so external `BlockService` implementors don't break on upgrade — when omitted, the\n * aggregator treats the block as contributing the empty string (silently skipped). Implementations\n * must be pure, synchronous functions — no async, no hooks, no side effects.\n */\n toClipboardText?: (block: TBlock) => string;\n}\n","import type { ComponentType, ReactNode } from \"react\";\n\nimport { ArtifactServices } from \"@/components/artifacts/artifact-services\";\nimport type { ArtifactRefBlockRecord, BlockComponentProps } from \"../types\";\n\n/**\n * Renders an `ARTIFACT_REF` block by dispatching the inlined artifact to its per-type component.\n *\n * The cast at the seam is necessary because the registry-based dispatch can't statically prove\n * type-T narrowing — but it's not hiding a bug. `ArtifactRefBlockRecord` is a distributed union\n * over `ArtifactRecord` branches (see `blocks/types.ts`), so TS catches wrong-shape labels at the\n * consumer's construction site: a chart-labels bag assigned to a TABLE block's payload is a type\n * error there, not silently forwarded to the wrong renderer here.\n *\n * Unknown artifact types render as `null` — the map only contains types we know how to render,\n * so an older clay client receiving a future artifact type degrades silently rather than\n * crashing the chat.\n */\nexport function ArtifactRefBlock({ block }: BlockComponentProps<ArtifactRefBlockRecord>): ReactNode {\n const { artifact, labels, callbacks } = block.payload;\n const entry = ArtifactServices[artifact.type];\n if (!entry) return null;\n // The registry stores `ArtifactService<ArtifactRecord>` (the union), so `entry.Component` is\n // `ComponentType<ArtifactServiceProps<ArtifactRecord>>`. The labels/callbacks props in the\n // narrower per-type `ArtifactServiceProps<TArtifact>` are `Partial<ArtifactLabelsOf<TArtifact>>`\n // which the union doesn't satisfy positionally. Cast forwards the values verbatim — the\n // runtime invariant is provably correct because the distributed `ArtifactRefBlockRecord` type\n // enforces it at construction.\n const Component = entry.Component as ComponentType<{\n artifact: typeof artifact;\n labels?: typeof labels;\n callbacks?: typeof callbacks;\n }>;\n return <Component artifact={artifact} labels={labels} callbacks={callbacks} />;\n}\n","import { artifactToClipboardText } from \"@/components/artifacts/artifact-services\";\nimport { ArtifactRefBlock } from \"../artifact-ref-block/artifact-ref-block\";\nimport { blockTypes } from \"../types\";\nimport type { ArtifactRefBlockRecord, BlockService } from \"../types\";\n\nexport const ArtifactRefBlockService: BlockService<ArtifactRefBlockRecord> = {\n type: blockTypes.ARTIFACT_REF,\n Component: ArtifactRefBlock,\n /**\n * Delegates to the registered artifact component's `toClipboardText`. Falls back to\n * `[Artifact: <title>]` via `artifactToClipboardText`'s own fallback when the artifact type\n * has no registered component, so copy never silently produces an empty string.\n */\n toClipboardText: (block) => artifactToClipboardText(block.payload.artifact),\n};\n","import type { ReactNode } from \"react\";\nimport ReactMarkdown from \"react-markdown\";\nimport remarkBreaks from \"remark-breaks\";\nimport remarkGfm from \"remark-gfm\";\n\nimport { ScrollableTable } from \"../scrollable-table/scrollable-table\";\nimport type { BlockComponentProps, TextBlockRecord } from \"../types\";\nimport { sanitize } from \"./sanitize\";\nimport { TextBlockWrapper } from \"./text-block-styles\";\n\nconst markdownComponents = {\n table: ((tableProps: { node?: unknown; children?: ReactNode }) => (\n <ScrollableTable {...tableProps} />\n )) as React.ComponentType<unknown>,\n};\n\n/** Renders a TEXT block as Markdown with GFM (tables, strikethrough) and line breaks. */\nexport function TextBlock({ block }: BlockComponentProps<TextBlockRecord>): ReactNode {\n return (\n <TextBlockWrapper>\n <ReactMarkdown remarkPlugins={[remarkGfm, remarkBreaks]} components={markdownComponents}>\n {sanitize(block.payload.text)}\n </ReactMarkdown>\n </TextBlockWrapper>\n );\n}\n","import styled from \"styled-components\";\n\nexport const TableContainer = styled.div`\n position: relative;\n margin-block: 8px 16px;\n`;\n\nexport const TableScroll = styled.div`\n width: 100%;\n overflow-x: auto;\n overflow-y: hidden;\n`;\n","import { type ReactNode } from \"react\";\n\nimport { TableContainer, TableScroll } from \"./scrollable-table-styles\";\n\n/** Props for the ScrollableTable component. */\ninterface ScrollableTableProps {\n /** Unused node prop from react-markdown. */\n node?: unknown;\n /** Table children elements. */\n children?: ReactNode;\n}\n\n/** Renders a markdown table inside a horizontally scrollable wrapper. Read-only — actions live on artifact-tables. */\nexport function ScrollableTable({ node: _node, ...props }: ScrollableTableProps): ReactNode {\n return (\n <TableContainer>\n <TableScroll>\n <table {...props} />\n </TableScroll>\n </TableContainer>\n );\n}\n","// Strips io-server's `<!-- table-title: ... -->` HTML-comment metadata before rendering.\n// The marker is emitted by the server's CSV-export pipeline so the export button can label\n// the file; it has no place in the visual output. If the server-side format changes\n// (prefix, spacing, closing-tag style), this filter silently stops matching and the\n// raw HTML comment leaks into rendered Markdown — keep this contract aligned with io-server.\nexport function sanitize(text: string): string {\n return text\n .split(\"\\n\")\n .filter((line) => {\n const t = line.trim();\n return !(t.startsWith(\"<!-- table-title:\") && t.endsWith(\"-->\"));\n })\n .join(\"\\n\");\n}\n","import styled from \"styled-components\";\n\nimport { colors } from \"@/tokens/colors\";\nimport { typographyMixin, typographyTypes } from \"@/tokens/typography\";\n\nexport const TextBlockWrapper = styled.div`\n ${typographyMixin(typographyTypes.GEIST_BODY_S_REGULAR)};\n color: ${colors[\"brown-100\"]};\n word-break: break-word;\n\n & h1 {\n ${typographyMixin(typographyTypes.GEIST_HEADING_S_BOLD)};\n margin-block: 20px;\n }\n\n & h2 {\n ${typographyMixin(typographyTypes.GEIST_BODY_L_SEMI_BOLD)};\n margin-block: 12px;\n }\n\n & h3,\n & h4,\n & h5,\n & h6 {\n ${typographyMixin(typographyTypes.GEIST_BODY_M_SEMI_BOLD)};\n margin-block: 8px;\n }\n\n & strong {\n ${typographyMixin(typographyTypes.GEIST_BODY_S_SEMI_BOLD)};\n }\n\n & p {\n margin-block: 0px;\n }\n\n & p + p {\n margin-block: 8px 0px;\n }\n\n & ul,\n & ol {\n margin-block: 16px;\n padding-left: 20px;\n }\n\n & li + li {\n margin-block: 4px;\n }\n\n & ul {\n list-style-type: disc;\n }\n\n & ul ul {\n list-style-type: circle;\n }\n\n & ul ul ul {\n list-style-type: square;\n }\n\n & ol {\n list-style-type: decimal;\n }\n\n & ol ol {\n list-style-type: lower-alpha;\n }\n\n & ol ol ol {\n list-style-type: lower-roman;\n }\n\n & a {\n color: inherit;\n text-decoration-line: underline;\n text-decoration-style: dotted;\n text-decoration-thickness: auto;\n text-underline-offset: auto;\n text-underline-position: from-font;\n transition: color 75ms ease-in-out;\n\n &:hover {\n color: ${colors[\"blue-600\"]};\n }\n }\n\n & hr {\n border: none;\n border-top: 1px solid ${colors[\"brown-40\"]};\n margin-block: 20px;\n }\n\n & table {\n margin: 0;\n width: 100%;\n border-collapse: collapse;\n }\n\n & th,\n & td {\n border: none;\n border-bottom: 1px solid ${colors[\"brown-40\"]};\n text-align: left;\n }\n\n & th {\n ${typographyMixin(typographyTypes.GEIST_LABEL_CAPTION_MEDIUM)};\n padding: 8px;\n }\n\n & td {\n ${typographyMixin(typographyTypes.GEIST_BODY_XS_MEDIUM)};\n padding: 16px 8px;\n min-width: 150px;\n width: 1%;\n }\n\n & td strong {\n font-weight: inherit;\n font-size: inherit;\n line-height: inherit;\n }\n\n & blockquote {\n margin-block: 4px;\n margin-inline: 0px;\n padding-left: 16px;\n border-left: 4px solid ${colors[\"brown-40\"]};\n color: ${colors[\"brown-80\"]};\n }\n`;\n","import { markdownToPlainText } from \"@/utils/markdown-to-plain-text\";\nimport { TextBlock } from \"../text-block/text-block\";\nimport { blockTypes } from \"../types\";\nimport type { BlockService, TextBlockRecord } from \"../types\";\n\nexport const TextBlockService: BlockService<TextBlockRecord> = {\n type: blockTypes.TEXT,\n Component: TextBlock,\n /**\n * Converts the block's markdown source to plain text so the final clipboard value is clean\n * when pasted into tools that don't render markdown. Each block type owns its own clipboard\n * conversion so `useCopyToClipboard` can write text verbatim without a second pass.\n */\n toClipboardText: (block) => markdownToPlainText(block.payload.text),\n};\n","import { blockTypes, type BlockService, type BlockType } from \"../types\";\nimport { ArtifactRefBlockService } from \"./artifact-ref-block-service\";\nimport { TextBlockService } from \"./text-block-service\";\n\n// `BlockService<TBlock>` is invariant on `TBlock` via React's `ComponentType` (props are\n// contravariant). The concrete services overlap structurally with the base `BlockService` type\n// enough for a direct `as BlockService` cast. The registry dispatches by runtime discriminator —\n// `BlockServices[block.type]` always resolves to the matching concrete service — so the cast is sound.\nexport const BlockServices: Record<BlockType, BlockService> = {\n [blockTypes.TEXT]: TextBlockService as BlockService,\n [blockTypes.ARTIFACT_REF]: ArtifactRefBlockService as BlockService,\n};\n","import type { ReactNode } from \"react\";\n\nimport { BlockServices } from \"../block-services\";\nimport type { BlockRecord } from \"../types\";\n\n/** Props for the Block dispatcher. */\ninterface BlockProps {\n /** The block to render. Dispatched to the registered concrete component for its `type`. */\n block: BlockRecord;\n}\n\n/**\n * Dispatches a single block to its registered concrete component. Unknown types render as a no-op.\n *\n * Generic by design: per-block-type extras (e.g. localized artifact labels for `ARTIFACT_REF`)\n * live on the block's `payload`, not as sibling props here. That keeps the dispatcher's contract\n * shallow — third-party `BlockService` implementors only need to handle `{ block }` — and lets\n * the consumer materialise a fully-decorated block once (from the server payload + their i18n\n * layer) instead of threading sibling props through every dispatch site.\n */\nexport function Block({ block }: BlockProps): ReactNode {\n const service = BlockServices[block.type];\n if (!service) return null;\n const { Component } = service;\n return <Component block={block} />;\n}\n"],"mappings":"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","names":["jsx","styled","styled","jsx","jsx"]}
|
|
@@ -10,9 +10,9 @@ import styled, { css } from "styled-components";
|
|
|
10
10
|
|
|
11
11
|
// src/components/keyboard-shortcut/types.ts
|
|
12
12
|
var KEYBOARD_SHORTCUT_VARIANTS = {
|
|
13
|
-
/** Outlined keys with a subtle border —
|
|
13
|
+
/** Outlined keys with a subtle border + light glyph — for dark surfaces / app chrome. Default. */
|
|
14
14
|
OUTLINED: "outlined",
|
|
15
|
-
/** Filled keys with a solid background —
|
|
15
|
+
/** Filled keys with a solid background — for emphasis. */
|
|
16
16
|
FILLED: "filled"
|
|
17
17
|
};
|
|
18
18
|
|
|
@@ -27,6 +27,16 @@ var variantStyles = {
|
|
|
27
27
|
color: ${colors["brown-10"]};
|
|
28
28
|
`
|
|
29
29
|
};
|
|
30
|
+
var adaptiveStyles = {
|
|
31
|
+
[KEYBOARD_SHORTCUT_VARIANTS.OUTLINED]: css`
|
|
32
|
+
border: 1px solid color-mix(in oklch, currentColor 40%, transparent);
|
|
33
|
+
color: currentColor;
|
|
34
|
+
`,
|
|
35
|
+
[KEYBOARD_SHORTCUT_VARIANTS.FILLED]: css`
|
|
36
|
+
background: color-mix(in oklch, currentColor 12%, transparent);
|
|
37
|
+
color: currentColor;
|
|
38
|
+
`
|
|
39
|
+
};
|
|
30
40
|
var Wrapper = styled.div`
|
|
31
41
|
display: inline-flex;
|
|
32
42
|
align-items: center;
|
|
@@ -38,7 +48,7 @@ var Key = styled.kbd`
|
|
|
38
48
|
justify-content: center;
|
|
39
49
|
padding: 2px;
|
|
40
50
|
border-radius: 6px;
|
|
41
|
-
${({ $variant }) => variantStyles[$variant]};
|
|
51
|
+
${({ $variant, $adaptive }) => ($adaptive ? adaptiveStyles : variantStyles)[$variant]};
|
|
42
52
|
`;
|
|
43
53
|
|
|
44
54
|
// src/components/keyboard-shortcut/keyboard-shortcut.tsx
|
|
@@ -46,9 +56,10 @@ import { jsx } from "react/jsx-runtime";
|
|
|
46
56
|
function KeyboardShortcut({
|
|
47
57
|
keys,
|
|
48
58
|
variant = KEYBOARD_SHORTCUT_VARIANTS.OUTLINED,
|
|
59
|
+
adaptive = false,
|
|
49
60
|
className
|
|
50
61
|
}) {
|
|
51
|
-
return /* @__PURE__ */ jsx(Wrapper, { className, children: keys.map((KeyComponent, index) => /* @__PURE__ */ jsx(Key, { $variant: variant, children: /* @__PURE__ */ jsx(Icon, { icon: KeyComponent, size: 12 }) }, index)) });
|
|
62
|
+
return /* @__PURE__ */ jsx(Wrapper, { className, children: keys.map((KeyComponent, index) => /* @__PURE__ */ jsx(Key, { $variant: variant, $adaptive: adaptive, children: /* @__PURE__ */ jsx(Icon, { icon: KeyComponent, size: 12 }) }, index)) });
|
|
52
63
|
}
|
|
53
64
|
|
|
54
65
|
// src/components/keyboard-shortcut/icons/icon-ctrl.tsx
|
|
@@ -76,4 +87,4 @@ export {
|
|
|
76
87
|
KeyboardShortcut,
|
|
77
88
|
IconCtrl
|
|
78
89
|
};
|
|
79
|
-
//# sourceMappingURL=chunk-
|
|
90
|
+
//# sourceMappingURL=chunk-PGR2MDDU.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/keyboard-shortcut/keyboard-shortcut-styles.ts","../src/components/keyboard-shortcut/types.ts","../src/components/keyboard-shortcut/keyboard-shortcut.tsx","../src/components/keyboard-shortcut/icons/icon-ctrl.tsx"],"sourcesContent":["import styled, { css } from \"styled-components\";\n\nimport { colors } from \"../../tokens/colors\";\nimport { KEYBOARD_SHORTCUT_VARIANTS } from \"./types\";\nimport type { KeyboardShortcutVariant } from \"./types\";\n\n// Fixed-color treatments (default) — tuned for dark surfaces / app chrome.\nconst variantStyles: Record<KeyboardShortcutVariant, ReturnType<typeof css>> = {\n [KEYBOARD_SHORTCUT_VARIANTS.OUTLINED]: css`\n border: 1px solid ${colors[\"white-alpha-40\"]};\n color: ${colors[\"brown-10\"]};\n `,\n [KEYBOARD_SHORTCUT_VARIANTS.FILLED]: css`\n background: ${colors[\"brown-80\"]};\n color: ${colors[\"brown-10\"]};\n `,\n};\n\n// `adaptive` treatments — border/fill + glyph follow the surrounding text color (currentColor) so\n// the chip reads on any surface (light or dark). Selected when the `adaptive` prop is set.\nconst adaptiveStyles: Record<KeyboardShortcutVariant, ReturnType<typeof css>> = {\n [KEYBOARD_SHORTCUT_VARIANTS.OUTLINED]: css`\n border: 1px solid color-mix(in oklch, currentColor 40%, transparent);\n color: currentColor;\n `,\n [KEYBOARD_SHORTCUT_VARIANTS.FILLED]: css`\n background: color-mix(in oklch, currentColor 12%, transparent);\n color: currentColor;\n `,\n};\n\nexport const Wrapper = styled.div`\n display: inline-flex;\n align-items: center;\n gap: 2px;\n`;\n\nexport const Key = styled.kbd<{ $variant: KeyboardShortcutVariant; $adaptive: boolean }>`\n display: inline-flex;\n align-items: center;\n justify-content: center;\n padding: 2px;\n border-radius: 6px;\n ${({ $variant, $adaptive }) => ($adaptive ? adaptiveStyles : variantStyles)[$variant]};\n`;\n","import type { ComponentType, SVGProps } from \"react\";\n\n/** An SVG icon component representing a single keyboard key (e.g., Cmd, Shift, a letter). */\nexport type KeyIcon = ComponentType<SVGProps<SVGSVGElement>>;\n\n/** Available visual variants for the keyboard shortcut display. */\nexport const KEYBOARD_SHORTCUT_VARIANTS = {\n /** Outlined keys with a subtle border + light glyph — for dark surfaces / app chrome. Default. */\n OUTLINED: \"outlined\",\n /** Filled keys with a solid background — for emphasis. */\n FILLED: \"filled\",\n} as const;\n\n/** Visual variant for the KeyboardShortcut component. */\nexport type KeyboardShortcutVariant = (typeof KEYBOARD_SHORTCUT_VARIANTS)[keyof typeof KEYBOARD_SHORTCUT_VARIANTS];\n\n/** Props for the KeyboardShortcut display component. */\nexport interface KeyboardShortcutProps {\n /** Ordered array of key icon components representing the shortcut (e.g., `[IconCommand, IconLetterK]` for ⌘K). */\n keys: KeyIcon[];\n /** Visual style variant. @default \"outlined\" */\n variant?: KeyboardShortcutVariant;\n /**\n * When true, the border + glyph (outlined) or fill + glyph (filled) follow the surrounding text\n * color (`currentColor`) instead of fixed tokens, so the chip reads on any surface — light or\n * dark. Use on surfaces where the fixed-color variants wouldn't read (e.g. a prompt-launcher hero\n * on a light backdrop). @default false\n */\n adaptive?: boolean;\n /** Additional CSS class name. */\n className?: string;\n}\n","import type { ReactElement } from \"react\";\n\nimport { Icon } from \"../icon/icon\";\nimport { Key, Wrapper } from \"./keyboard-shortcut-styles\";\nimport { KEYBOARD_SHORTCUT_VARIANTS } from \"./types\";\nimport type { KeyboardShortcutProps } from \"./types\";\n\nexport function KeyboardShortcut({\n keys,\n variant = KEYBOARD_SHORTCUT_VARIANTS.OUTLINED,\n adaptive = false,\n className,\n}: KeyboardShortcutProps): ReactElement {\n return (\n <Wrapper className={className}>\n {keys.map((KeyComponent, index) => (\n <Key key={index} $variant={variant} $adaptive={adaptive}>\n <Icon icon={KeyComponent} size={12} />\n </Key>\n ))}\n </Wrapper>\n );\n}\n","import type { ReactElement, SVGProps } from \"react\";\n\nexport function IconCtrl(props: SVGProps<SVGSVGElement>): ReactElement {\n const { width = 24, height = 24, ...rest } = props;\n\n return (\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" width={width} height={height} fill=\"none\" {...rest}>\n <text\n x=\"50%\"\n y=\"50%\"\n dominantBaseline=\"central\"\n textAnchor=\"middle\"\n fill=\"currentColor\"\n fontFamily=\"Geist, sans-serif\"\n fontWeight={300}\n fontSize={14}\n >\n Ctrl\n </text>\n </svg>\n );\n}\n"],"mappings":";;;;;;;;AAAA,OAAO,UAAU,WAAW;;;ACMrB,IAAM,6BAA6B;AAAA;AAAA,EAExC,UAAU;AAAA;AAAA,EAEV,QAAQ;AACV;;;ADJA,IAAM,gBAAyE;AAAA,EAC7E,CAAC,2BAA2B,QAAQ,GAAG;AAAA,wBACjB,OAAO,gBAAgB,CAAC;AAAA,aACnC,OAAO,UAAU,CAAC;AAAA;AAAA,EAE7B,CAAC,2BAA2B,MAAM,GAAG;AAAA,kBACrB,OAAO,UAAU,CAAC;AAAA,aACvB,OAAO,UAAU,CAAC;AAAA;AAE/B;AAIA,IAAM,iBAA0E;AAAA,EAC9E,CAAC,2BAA2B,QAAQ,GAAG;AAAA;AAAA;AAAA;AAAA,EAIvC,CAAC,2BAA2B,MAAM,GAAG;AAAA;AAAA;AAAA;AAIvC;AAEO,IAAM,UAAU,OAAO;AAAA;AAAA;AAAA;AAAA;AAMvB,IAAM,MAAM,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAMtB,CAAC,EAAE,UAAU,UAAU,OAAO,YAAY,iBAAiB,eAAe,QAAQ,CAAC;AAAA;;;AE1B7E;AAVH,SAAS,iBAAiB;AAAA,EAC/B;AAAA,EACA,UAAU,2BAA2B;AAAA,EACrC,WAAW;AAAA,EACX;AACF,GAAwC;AACtC,SACE,oBAAC,WAAQ,WACN,eAAK,IAAI,CAAC,cAAc,UACvB,oBAAC,OAAgB,UAAU,SAAS,WAAW,UAC7C,8BAAC,QAAK,MAAM,cAAc,MAAM,IAAI,KAD5B,KAEV,CACD,GACH;AAEJ;;;ACfM,gBAAAA,YAAA;AALC,SAAS,SAAS,OAA8C;AACrE,QAAM,EAAE,QAAQ,IAAI,SAAS,IAAI,GAAG,KAAK,IAAI;AAE7C,SACE,gBAAAA,KAAC,SAAI,OAAM,8BAA6B,SAAQ,aAAY,OAAc,QAAgB,MAAK,QAAQ,GAAG,MACxG,0BAAAA;AAAA,IAAC;AAAA;AAAA,MACC,GAAE;AAAA,MACF,GAAE;AAAA,MACF,kBAAiB;AAAA,MACjB,YAAW;AAAA,MACX,MAAK;AAAA,MACL,YAAW;AAAA,MACX,YAAY;AAAA,MACZ,UAAU;AAAA,MACX;AAAA;AAAA,EAED,GACF;AAEJ;","names":["jsx"]}
|