@pindoba/core-code 0.0.0-alpha-20260829223243 → 0.0.0-alpha-20261110151933
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/index.d.ts +4 -3
- package/dist/index.js +3 -2
- package/dist/src/code-actions.d.ts +23 -0
- package/dist/src/code-actions.d.ts.map +1 -0
- package/dist/src/code-actions.js +44 -0
- package/dist/src/code-actions.js.map +1 -0
- package/dist/src/connect-code.d.ts +21 -5
- package/dist/src/connect-code.d.ts.map +1 -1
- package/dist/src/connect-code.js +164 -9
- package/dist/src/connect-code.js.map +1 -1
- package/dist/src/runtime.d.ts +13 -0
- package/dist/src/runtime.d.ts.map +1 -0
- package/dist/src/runtime.js +253 -0
- package/dist/src/runtime.js.map +1 -0
- package/dist/src/shiki.d.ts +6 -7
- package/dist/src/shiki.d.ts.map +1 -1
- package/dist/{src/types.d.ts → types-DcQoTmcO.d.ts} +92 -10
- package/dist/types-DcQoTmcO.d.ts.map +1 -0
- package/package.json +17 -8
- package/dist/src/types.d.ts.map +0 -1
- package/dist/src/types.js +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
1
|
+
import { CODE_COPIED_MS, codeCopyLabel, codeExpandLabel, createCodeCopy } from "./src/code-actions.js";
|
|
2
|
+
import { _ as ConnectCodeOptions, a as CodeCardFrameNodes, b as PanelRootProps, c as CodeLineItem, d as CodeSize, f as CodeSlotPassThrough, g as CodeTone, h as CodeTokenItem, i as CodeCardDefaults, l as CodePassThrough, m as CodeToken, n as CodeBaseProps, o as CodeCardProps, p as CodeSlots, r as CodeCardChrome, s as CodeLine, t as CodeApi, u as CodeResolvedCardFrame, v as ElementAttributes, y as PanelElement } from "./types-DcQoTmcO.js";
|
|
3
|
+
import { codeDefaults, connectCode, resolveCodeCardFrame } from "./src/connect-code.js";
|
|
4
|
+
export { CODE_COPIED_MS, type CodeApi, type CodeBaseProps, type CodeCardChrome, type CodeCardDefaults, type CodeCardFrameNodes, type CodeCardProps, type CodeLine, type CodeLineItem, type CodePassThrough, type CodeResolvedCardFrame, type CodeSize, type CodeSlotPassThrough, type CodeSlots, type CodeToken, type CodeTokenItem, type CodeTone, type ConnectCodeOptions, type ElementAttributes, type PanelElement, type PanelRootProps, codeCopyLabel, codeDefaults, codeExpandLabel, connectCode, createCodeCopy, resolveCodeCardFrame };
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { CODE_COPIED_MS, codeCopyLabel, codeExpandLabel, createCodeCopy } from "./src/code-actions.js";
|
|
2
|
+
import { codeDefaults, connectCode, resolveCodeCardFrame } from "./src/connect-code.js";
|
|
3
|
+
export { CODE_COPIED_MS, codeCopyLabel, codeDefaults, codeExpandLabel, connectCode, createCodeCopy, resolveCodeCardFrame };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
//#region src/code-actions.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* The behaviour of a code block, framework-free: the clipboard write with its
|
|
4
|
+
* "Copied" tone window and the button labels. The Code runtime
|
|
5
|
+
* (`./runtime`) wires them; every interactive Astro block hydrates on it.
|
|
6
|
+
*/
|
|
7
|
+
/** How long the copy button shows its "Copied" state. */
|
|
8
|
+
export declare const CODE_COPIED_MS = 2000;
|
|
9
|
+
/**
|
|
10
|
+
* The copy action: write `text` to the clipboard, flip the copied state on for
|
|
11
|
+
* `resetMs`, and report the text to `onCopy`. A denied clipboard permission
|
|
12
|
+
* must not break the block, so a failed write changes nothing.
|
|
13
|
+
*/
|
|
14
|
+
export declare function createCodeCopy(setCopied: (copied: boolean) => void, resetMs?: number): {
|
|
15
|
+
copy(text: string, onCopy?: (text: string) => void): Promise<void>;
|
|
16
|
+
dispose(): void;
|
|
17
|
+
};
|
|
18
|
+
/** The expand button's label. */
|
|
19
|
+
export declare function codeExpandLabel(expanded: boolean, lineCount: number): string;
|
|
20
|
+
/** The copy button's label. */
|
|
21
|
+
export declare function codeCopyLabel(copied: boolean): string;
|
|
22
|
+
//#endregion
|
|
23
|
+
//# sourceMappingURL=code-actions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code-actions.d.ts","names":[],"sources":["../../src/code-actions.ts"],"mappings":";;;;;;;qBAOa;;;;;;wBAOG,eACd,YAAY,0BACZ;EAEA,KAAK,cAAc,UAAU,wBAAwB;EACrD;;;wBAsBc,gBAAgB,mBAAmB;;wBAKnC,cAAc"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
//#region src/code-actions.ts
|
|
2
|
+
/**
|
|
3
|
+
* The behaviour of a code block, framework-free: the clipboard write with its
|
|
4
|
+
* "Copied" tone window and the button labels. The Code runtime
|
|
5
|
+
* (`./runtime`) wires them; every interactive Astro block hydrates on it.
|
|
6
|
+
*/
|
|
7
|
+
/** How long the copy button shows its "Copied" state. */
|
|
8
|
+
const CODE_COPIED_MS = 2e3;
|
|
9
|
+
/**
|
|
10
|
+
* The copy action: write `text` to the clipboard, flip the copied state on for
|
|
11
|
+
* `resetMs`, and report the text to `onCopy`. A denied clipboard permission
|
|
12
|
+
* must not break the block, so a failed write changes nothing.
|
|
13
|
+
*/
|
|
14
|
+
function createCodeCopy(setCopied, resetMs = CODE_COPIED_MS) {
|
|
15
|
+
let timer;
|
|
16
|
+
return {
|
|
17
|
+
async copy(text, onCopy) {
|
|
18
|
+
try {
|
|
19
|
+
await navigator.clipboard.writeText(text);
|
|
20
|
+
} catch {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
setCopied(true);
|
|
24
|
+
onCopy?.(text);
|
|
25
|
+
clearTimeout(timer);
|
|
26
|
+
timer = setTimeout(() => setCopied(false), resetMs);
|
|
27
|
+
},
|
|
28
|
+
dispose() {
|
|
29
|
+
clearTimeout(timer);
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/** The expand button's label. */
|
|
34
|
+
function codeExpandLabel(expanded, lineCount) {
|
|
35
|
+
return expanded ? "Show less" : `Show all ${lineCount} lines`;
|
|
36
|
+
}
|
|
37
|
+
/** The copy button's label. */
|
|
38
|
+
function codeCopyLabel(copied) {
|
|
39
|
+
return copied ? "Copied" : "Copy";
|
|
40
|
+
}
|
|
41
|
+
//#endregion
|
|
42
|
+
export { CODE_COPIED_MS, codeCopyLabel, codeExpandLabel, createCodeCopy };
|
|
43
|
+
|
|
44
|
+
//# sourceMappingURL=code-actions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code-actions.js","names":[],"sources":["../../src/code-actions.ts"],"sourcesContent":["/**\n * The behaviour of a code block, framework-free: the clipboard write with its\n * \"Copied\" tone window and the button labels. The Code runtime\n * (`./runtime`) wires them; every interactive Astro block hydrates on it.\n */\n\n/** How long the copy button shows its \"Copied\" state. */\nexport const CODE_COPIED_MS = 2000;\n\n/**\n * The copy action: write `text` to the clipboard, flip the copied state on for\n * `resetMs`, and report the text to `onCopy`. A denied clipboard permission\n * must not break the block, so a failed write changes nothing.\n */\nexport function createCodeCopy(\n setCopied: (copied: boolean) => void,\n resetMs: number = CODE_COPIED_MS,\n): {\n copy(text: string, onCopy?: (text: string) => void): Promise<void>;\n dispose(): void;\n} {\n let timer: ReturnType<typeof setTimeout> | undefined;\n return {\n async copy(text, onCopy) {\n try {\n await navigator.clipboard.writeText(text);\n } catch {\n return;\n }\n setCopied(true);\n onCopy?.(text);\n clearTimeout(timer);\n timer = setTimeout(() => setCopied(false), resetMs);\n },\n dispose() {\n clearTimeout(timer);\n },\n };\n}\n\n/** The expand button's label. */\nexport function codeExpandLabel(expanded: boolean, lineCount: number): string {\n return expanded ? \"Show less\" : `Show all ${lineCount} lines`;\n}\n\n/** The copy button's label. */\nexport function codeCopyLabel(copied: boolean): string {\n return copied ? \"Copied\" : \"Copy\";\n}\n"],"mappings":";;;;;;;AAOA,MAAa,iBAAiB;;;;;;AAO9B,SAAgB,eACd,WACA,UAAkB,gBAIlB;CACA,IAAI;CACJ,OAAO;EACL,MAAM,KAAK,MAAM,QAAQ;GACvB,IAAI;IACF,MAAM,UAAU,UAAU,UAAU,IAAI;GAC1C,QAAQ;IACN;GACF;GACA,UAAU,IAAI;GACd,SAAS,IAAI;GACb,aAAa,KAAK;GAClB,QAAQ,iBAAiB,UAAU,KAAK,GAAG,OAAO;EACpD;EACA,UAAU;GACR,aAAa,KAAK;EACpB;CACF;AACF;;AAGA,SAAgB,gBAAgB,UAAmB,WAA2B;CAC5E,OAAO,WAAW,cAAc,YAAY,UAAU;AACxD;;AAGA,SAAgB,cAAc,QAAyB;CACrD,OAAO,SAAS,WAAW;AAC7B"}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { CodeApi,
|
|
1
|
+
import { _ as ConnectCodeOptions, a as CodeCardFrameNodes, r as CodeCardChrome, t as CodeApi, u as CodeResolvedCardFrame } from "../types-DcQoTmcO.js";
|
|
2
|
+
import { CardFrameProps } from "@pindoba/core-card";
|
|
2
3
|
//#region src/connect-code.d.ts
|
|
3
|
-
declare const codeDefaults: {
|
|
4
|
+
export declare const codeDefaults: {
|
|
4
5
|
readonly size: "sm";
|
|
5
|
-
readonly
|
|
6
|
+
readonly tone: "neutral";
|
|
6
7
|
readonly wrap: false;
|
|
7
8
|
readonly showLineNumbers: false;
|
|
8
9
|
readonly startLine: 1;
|
|
@@ -26,7 +27,22 @@ declare const codeDefaults: {
|
|
|
26
27
|
* `code` as plain text, so a block without a highlighter is still readable
|
|
27
28
|
* rather than empty.
|
|
28
29
|
*/
|
|
29
|
-
declare function connectCode(options?: ConnectCodeOptions): CodeApi;
|
|
30
|
+
export declare function connectCode(options?: ConnectCodeOptions): CodeApi;
|
|
31
|
+
/**
|
|
32
|
+
* Resolve a framed block's Card: the consumer's `card` prop over the block's
|
|
33
|
+
* own defaults (`api.cardChrome`), with the framework's chrome appended AFTER
|
|
34
|
+
* the consumer's slots — the copy button into the header's trailing slot, the
|
|
35
|
+
* expand button into the footer's trailing slot. The same call in all four
|
|
36
|
+
* frameworks; they differ only in their slot type (Svelte snippets, React /
|
|
37
|
+
* Vue nodes; Astro fills `chrome-*` slots and passes none).
|
|
38
|
+
*
|
|
39
|
+
* - `card.heading` / `card.eyebrow` win over `title` / `lang`.
|
|
40
|
+
* - `card.content` merges over the block's `padding: "none"` default, so
|
|
41
|
+
* `{ contentBackground }` keeps the code edge to edge on an inset panel.
|
|
42
|
+
* - `card.passThrough` merges over `passThrough.card` and the frame styles.
|
|
43
|
+
* - `data-card-kind` (root) and the clamp markers (content) are re-applied
|
|
44
|
+
* last: nothing overrides them.
|
|
45
|
+
*/
|
|
46
|
+
export declare function resolveCodeCardFrame<TSlot>(card: CardFrameProps<TSlot> | false | undefined, chrome: CodeCardChrome, nodes?: CodeCardFrameNodes<TSlot>): CodeResolvedCardFrame<TSlot>;
|
|
30
47
|
//#endregion
|
|
31
|
-
export { codeDefaults, connectCode };
|
|
32
48
|
//# sourceMappingURL=connect-code.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connect-code.d.ts","names":[],"sources":["../../src/connect-code.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"connect-code.d.ts","names":[],"sources":["../../src/connect-code.ts"],"mappings":";;;qBAyBa;;;;;;;;;;;;;;;;;;;;;;;;;;wBAyDG,YAAY,UAAS,qBAA0B;;;;;;;;;;;;;;;;wBA2Z/C,qBAAqB,OACnC,MAAM,eAAe,4BACrB,QAAQ,gBACR,QAAO,mBAAmB,SACzB,sBAAsB"}
|
package/dist/src/connect-code.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { css, cx } from "@pindoba/styled-system/css";
|
|
2
2
|
import { connectPanel } from "@pindoba/core-panel";
|
|
3
|
-
import {
|
|
3
|
+
import { reactiveConfig } from "@pindoba/core-runtime/reactive-config";
|
|
4
|
+
import { resolveCardContentSurface, resolveCardFrame } from "@pindoba/core-card";
|
|
5
|
+
import { defineConnect, toClassString } from "@pindoba/normalize-props";
|
|
4
6
|
import { codeStyles } from "@pindoba/styles-code";
|
|
5
7
|
//#region src/connect-code.ts
|
|
6
8
|
const codeDefaults = {
|
|
7
9
|
size: "sm",
|
|
8
|
-
|
|
10
|
+
tone: "neutral",
|
|
9
11
|
wrap: false,
|
|
10
12
|
showLineNumbers: false,
|
|
11
13
|
startLine: 1,
|
|
@@ -42,15 +44,29 @@ function sourceFromLines(lines) {
|
|
|
42
44
|
* rather than empty.
|
|
43
45
|
*/
|
|
44
46
|
function connectCode(options = {}) {
|
|
45
|
-
const { lines, code = "", lang, size = codeDefaults.size,
|
|
47
|
+
const { lines, code = "", lang, size = codeDefaults.size, tone = codeDefaults.tone, wrap = codeDefaults.wrap, showLineNumbers = codeDefaults.showLineNumbers, startLine = codeDefaults.startLine, highlightLines, maxLines, collapsible = codeDefaults.collapsible, expanded = codeDefaults.expanded, copyable = codeDefaults.copyable, copied = codeDefaults.copied, label = codeDefaults.label, title, card, as = "div", background = codeDefaults.background, emphasis = codeDefaults.emphasis, translucent, padding = codeDefaults.padding, radius = codeDefaults.radius, radiusTop, radiusBottom, radiusLeft, radiusRight, border = codeDefaults.border, borderInteract, borderActive, reserveBorderSpace, shadow = codeDefaults.shadow, interactive, active, activeEmphasis, reactive, passThrough, class: className, ...rest } = options;
|
|
48
|
+
const { reactive: _reactive, ...configInputs } = options;
|
|
49
|
+
const configProps = reactive ? reactiveConfig("code", configInputs) : null;
|
|
46
50
|
const resolvedLines = lines ?? (code ? linesFromSource(code) : []);
|
|
47
51
|
const lineCount = resolvedLines.length;
|
|
48
52
|
const overflowable = maxLines != null && maxLines > 0 && lineCount > maxLines;
|
|
49
53
|
const clamped = overflowable && !expanded;
|
|
54
|
+
const framed = card != null && card !== false;
|
|
55
|
+
const frameInput = framed ? card : {};
|
|
56
|
+
const cardContent = {
|
|
57
|
+
padding: "none",
|
|
58
|
+
...frameInput.content
|
|
59
|
+
};
|
|
60
|
+
const { inset, surface: contentSurface } = resolveCardContentSurface(cardContent, frameInput.contentBackground);
|
|
61
|
+
const surfaceBackground = inset ? contentSurface.background : frameInput.background ?? background;
|
|
50
62
|
const classes = codeStyles.raw({
|
|
51
63
|
size,
|
|
52
64
|
wrap,
|
|
53
|
-
showLineNumbers
|
|
65
|
+
showLineNumbers,
|
|
66
|
+
...framed ? {
|
|
67
|
+
framed: true,
|
|
68
|
+
fadeSurface: surfaceBackground
|
|
69
|
+
} : null
|
|
54
70
|
});
|
|
55
71
|
const highlighted = new Set(highlightLines ?? []);
|
|
56
72
|
const items = resolvedLines.map((line, index) => {
|
|
@@ -85,14 +101,14 @@ function connectCode(options = {}) {
|
|
|
85
101
|
};
|
|
86
102
|
});
|
|
87
103
|
const copyText = lines ? sourceFromLines(lines) : code;
|
|
88
|
-
const showToolbar = Boolean(lang) || copyable;
|
|
104
|
+
const showToolbar = Boolean(lang) || copyable || Boolean(title);
|
|
89
105
|
const showExpand = Boolean(collapsible && overflowable);
|
|
90
106
|
const panelApi = connectPanel({
|
|
91
107
|
as,
|
|
92
108
|
dataComponent: "code",
|
|
93
109
|
background,
|
|
94
110
|
emphasis,
|
|
95
|
-
|
|
111
|
+
tone,
|
|
96
112
|
translucent,
|
|
97
113
|
padding,
|
|
98
114
|
radius,
|
|
@@ -114,6 +130,7 @@ function connectCode(options = {}) {
|
|
|
114
130
|
style: passThrough?.root?.style,
|
|
115
131
|
props: {
|
|
116
132
|
...passThrough?.root?.props,
|
|
133
|
+
...configProps,
|
|
117
134
|
"data-component": "code",
|
|
118
135
|
"data-lang": lang ?? "",
|
|
119
136
|
"data-clamped": clamped ? "true" : "false",
|
|
@@ -123,6 +140,53 @@ function connectCode(options = {}) {
|
|
|
123
140
|
class: className,
|
|
124
141
|
...rest
|
|
125
142
|
});
|
|
143
|
+
const rootId = "id" in rest ? rest.id : void 0;
|
|
144
|
+
const cardPassThrough = passThrough?.card ?? {};
|
|
145
|
+
const invariantContentProps = {
|
|
146
|
+
"data-code-body": "",
|
|
147
|
+
"data-lang": lang ?? "",
|
|
148
|
+
"data-clamped": clamped ? "true" : "false",
|
|
149
|
+
...maxLines != null ? { style: `--code-max-lines:${maxLines}` } : {}
|
|
150
|
+
};
|
|
151
|
+
const cardDefaults = {
|
|
152
|
+
size,
|
|
153
|
+
background,
|
|
154
|
+
tone,
|
|
155
|
+
emphasis,
|
|
156
|
+
...translucent !== void 0 ? { translucent } : null,
|
|
157
|
+
radius,
|
|
158
|
+
border,
|
|
159
|
+
shadow,
|
|
160
|
+
...inset ? null : { divided: true },
|
|
161
|
+
content: { padding: "none" }
|
|
162
|
+
};
|
|
163
|
+
const cardChrome = {
|
|
164
|
+
heading: title || void 0,
|
|
165
|
+
eyebrow: lang || void 0,
|
|
166
|
+
headingId: typeof rootId === "string" && rootId !== "" ? `${rootId}-heading` : void 0,
|
|
167
|
+
defaults: cardDefaults,
|
|
168
|
+
rootProps: {
|
|
169
|
+
"data-card-flushable": "",
|
|
170
|
+
...rest,
|
|
171
|
+
...passThrough?.root?.props,
|
|
172
|
+
...configProps
|
|
173
|
+
},
|
|
174
|
+
invariantRootProps: { "data-card-kind": "code" },
|
|
175
|
+
invariantContentProps,
|
|
176
|
+
passThrough: {
|
|
177
|
+
...cardPassThrough,
|
|
178
|
+
...passThrough?.root?.style || cardPassThrough.root ? { root: {
|
|
179
|
+
...cardPassThrough.root,
|
|
180
|
+
style: css.raw(passThrough?.root?.style, cardPassThrough.root?.style)
|
|
181
|
+
} } : null,
|
|
182
|
+
content: {
|
|
183
|
+
...cardPassThrough.content,
|
|
184
|
+
style: css.raw(classes.frameContent, cardPassThrough.content?.style)
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
class: toClassString(className)
|
|
188
|
+
};
|
|
189
|
+
const { card: _cardPassThrough, ...slotPassThrough } = passThrough ?? {};
|
|
126
190
|
return {
|
|
127
191
|
...defineConnect({
|
|
128
192
|
slots: {
|
|
@@ -131,6 +195,10 @@ function connectCode(options = {}) {
|
|
|
131
195
|
"data-code-toolbar": "",
|
|
132
196
|
class: css(classes.toolbar, passThrough?.toolbar?.style)
|
|
133
197
|
},
|
|
198
|
+
title: {
|
|
199
|
+
"data-code-title": "",
|
|
200
|
+
class: css(classes.title, passThrough?.title?.style)
|
|
201
|
+
},
|
|
134
202
|
language: {
|
|
135
203
|
"data-code-language": "",
|
|
136
204
|
class: css(classes.language, passThrough?.language?.style)
|
|
@@ -177,13 +245,16 @@ function connectCode(options = {}) {
|
|
|
177
245
|
size: "xs",
|
|
178
246
|
"aria-expanded": expanded ? "true" : "false",
|
|
179
247
|
class: css(classes.expandButton, passThrough?.expandButton?.style)
|
|
180
|
-
}
|
|
248
|
+
},
|
|
249
|
+
frameContent: { class: css(classes.frameContent) }
|
|
181
250
|
},
|
|
182
251
|
passThrough: {
|
|
183
|
-
...
|
|
252
|
+
...slotPassThrough,
|
|
184
253
|
root: void 0
|
|
185
254
|
}
|
|
186
255
|
}),
|
|
256
|
+
frame: framed ? "card" : "bare",
|
|
257
|
+
cardChrome,
|
|
187
258
|
rootProps: panelApi.rootProps,
|
|
188
259
|
element: panelApi.element,
|
|
189
260
|
items,
|
|
@@ -194,7 +265,91 @@ function connectCode(options = {}) {
|
|
|
194
265
|
lineCount
|
|
195
266
|
};
|
|
196
267
|
}
|
|
268
|
+
/** Merge one card slot's passThrough: `over` wins (styles merged, props spread). */
|
|
269
|
+
function mergeCardSlot(base, over) {
|
|
270
|
+
if (!base) return over;
|
|
271
|
+
if (!over) return base;
|
|
272
|
+
return {
|
|
273
|
+
style: css.raw(base.style, over.style),
|
|
274
|
+
props: {
|
|
275
|
+
...base.props,
|
|
276
|
+
...over.props
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
function slotOf(bag, key) {
|
|
281
|
+
if (!bag) return void 0;
|
|
282
|
+
const entry = Reflect.get(bag, key);
|
|
283
|
+
return typeof entry === "object" && entry !== null ? entry : void 0;
|
|
284
|
+
}
|
|
285
|
+
/** Merge two card passThrough bags slot by slot (`over` wins). */
|
|
286
|
+
function mergeCardPassThrough(base, over) {
|
|
287
|
+
const merged = {};
|
|
288
|
+
const keys = /* @__PURE__ */ new Set([...Object.keys(base), ...Object.keys(over ?? {})]);
|
|
289
|
+
for (const key of keys) merged[key] = mergeCardSlot(slotOf(base, key), slotOf(over, key));
|
|
290
|
+
return merged;
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Resolve a framed block's Card: the consumer's `card` prop over the block's
|
|
294
|
+
* own defaults (`api.cardChrome`), with the framework's chrome appended AFTER
|
|
295
|
+
* the consumer's slots — the copy button into the header's trailing slot, the
|
|
296
|
+
* expand button into the footer's trailing slot. The same call in all four
|
|
297
|
+
* frameworks; they differ only in their slot type (Svelte snippets, React /
|
|
298
|
+
* Vue nodes; Astro fills `chrome-*` slots and passes none).
|
|
299
|
+
*
|
|
300
|
+
* - `card.heading` / `card.eyebrow` win over `title` / `lang`.
|
|
301
|
+
* - `card.content` merges over the block's `padding: "none"` default, so
|
|
302
|
+
* `{ contentBackground }` keeps the code edge to edge on an inset panel.
|
|
303
|
+
* - `card.passThrough` merges over `passThrough.card` and the frame styles.
|
|
304
|
+
* - `data-card-kind` (root) and the clamp markers (content) are re-applied
|
|
305
|
+
* last: nothing overrides them.
|
|
306
|
+
*/
|
|
307
|
+
function resolveCodeCardFrame(card, chrome, nodes = {}) {
|
|
308
|
+
const frame = card || {};
|
|
309
|
+
const eyebrow = frame.eyebrow ?? chrome.eyebrow;
|
|
310
|
+
const resolved = resolveCardFrame({
|
|
311
|
+
...frame,
|
|
312
|
+
...eyebrow !== void 0 ? { eyebrow } : null,
|
|
313
|
+
content: {
|
|
314
|
+
...chrome.defaults.content,
|
|
315
|
+
...frame.content
|
|
316
|
+
},
|
|
317
|
+
passThrough: mergeCardPassThrough(chrome.passThrough, frame.passThrough)
|
|
318
|
+
}, {
|
|
319
|
+
heading: chrome.heading,
|
|
320
|
+
headingId: chrome.headingId,
|
|
321
|
+
defaults: chrome.defaults,
|
|
322
|
+
rootProps: chrome.rootProps,
|
|
323
|
+
trailing: nodes.copy,
|
|
324
|
+
footerTrailing: nodes.expand
|
|
325
|
+
});
|
|
326
|
+
const passThrough = resolved.cardProps.passThrough;
|
|
327
|
+
return {
|
|
328
|
+
...resolved,
|
|
329
|
+
cardProps: {
|
|
330
|
+
...resolved.cardProps,
|
|
331
|
+
passThrough: {
|
|
332
|
+
...passThrough,
|
|
333
|
+
root: {
|
|
334
|
+
...passThrough.root,
|
|
335
|
+
props: {
|
|
336
|
+
...passThrough.root?.props,
|
|
337
|
+
...chrome.invariantRootProps
|
|
338
|
+
}
|
|
339
|
+
},
|
|
340
|
+
content: {
|
|
341
|
+
...passThrough.content,
|
|
342
|
+
props: {
|
|
343
|
+
...passThrough.content?.props,
|
|
344
|
+
...chrome.invariantContentProps
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
},
|
|
348
|
+
...chrome.class !== void 0 ? { class: chrome.class } : null
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
}
|
|
197
352
|
//#endregion
|
|
198
|
-
export { codeDefaults, connectCode };
|
|
353
|
+
export { codeDefaults, connectCode, resolveCodeCardFrame };
|
|
199
354
|
|
|
200
355
|
//# sourceMappingURL=connect-code.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connect-code.js","names":[],"sources":["../../src/connect-code.ts"],"sourcesContent":["import { css, cx } from \"@pindoba/styled-system/css\";\nimport { connectPanel } from \"@pindoba/core-panel\";\nimport { defineConnect } from \"@pindoba/normalize-props\";\nimport { codeStyles } from \"@pindoba/styles-code\";\nimport type {\n CodeApi,\n CodeBaseProps,\n CodeLine,\n CodeLineItem,\n CodeToken,\n ConnectCodeOptions,\n} from \"./types.js\";\n\nexport const codeDefaults = {\n size: \"sm\",\n feedback: \"neutral\",\n wrap: false,\n showLineNumbers: false,\n startLine: 1,\n collapsible: false,\n expanded: false,\n copyable: false,\n copied: false,\n label: \"Code block\",\n // --- Panel surface defaults ---\n // The root is a Panel; these reproduce the surface the recipe used to paint\n // by hand (`colorPalette.surface.hill` + a 1px muted border + an `sm`\n // corner). `padding: \"none\"` because the block's inset lives on the toolbar /\n // scroller, not the root — so no `--panel-out-padding` republish is needed.\n emphasis: \"secondary\",\n background: \"surface.hill\",\n padding: \"none\",\n radius: \"sm\",\n border: \"muted\",\n shadow: \"none\",\n} as const satisfies Partial<CodeBaseProps>;\n\n/** Split raw source into single-token lines — the unhighlighted fallback. */\nfunction linesFromSource(code: string): CodeLine[] {\n // A trailing newline is a line terminator, not an empty final line; keeping\n // it would render a stray blank row under every file.\n const body = code.endsWith(\"\\n\") ? code.slice(0, -1) : code;\n return body.split(\"\\n\").map((text) => ({ tokens: [{ text }] }));\n}\n\n/** A token's per-instance theme styling, as an inline CSS string. */\nfunction tokenStyle(token: CodeToken): string {\n return [\n token.color != null ? `color:${token.color}` : \"\",\n token.fontStyle != null ? `font-style:${token.fontStyle}` : \"\",\n ]\n .filter(Boolean)\n .join(\";\");\n}\n\n/** Reconstruct the plain text of tokenized lines, for the clipboard. */\nfunction sourceFromLines(lines: CodeLine[]): string {\n return lines\n .map((line) => line.tokens.map((t) => t.text).join(\"\"))\n .join(\"\\n\");\n}\n\n/**\n * Connect Contract v1.1 — the `Code` bags.\n *\n * Highlighting is somebody else's job: this takes already-tokenized `lines` and\n * turns them into spreadable bags. With no `lines` it falls back to rendering\n * `code` as plain text, so a block without a highlighter is still readable\n * rather than empty.\n */\nexport function connectCode(options: ConnectCodeOptions = {}): CodeApi {\n const {\n lines,\n code = \"\",\n lang,\n size = codeDefaults.size,\n feedback = codeDefaults.feedback,\n wrap = codeDefaults.wrap,\n showLineNumbers = codeDefaults.showLineNumbers,\n startLine = codeDefaults.startLine,\n highlightLines,\n maxLines,\n collapsible = codeDefaults.collapsible,\n expanded = codeDefaults.expanded,\n copyable = codeDefaults.copyable,\n copied = codeDefaults.copied,\n label = codeDefaults.label,\n // --- Panel surface axes (delegated to connectPanel) ---\n as = \"div\",\n background = codeDefaults.background,\n emphasis = codeDefaults.emphasis,\n translucent,\n padding = codeDefaults.padding,\n radius = codeDefaults.radius,\n radiusTop,\n radiusBottom,\n radiusLeft,\n radiusRight,\n border = codeDefaults.border,\n borderInteract,\n borderActive,\n reserveBorderSpace,\n shadow = codeDefaults.shadow,\n interactive,\n active,\n activeEmphasis,\n reactive,\n passThrough,\n class: className,\n ...rest\n } = options;\n\n const resolvedLines: CodeLine[] =\n lines ?? (code ? linesFromSource(code) : []);\n const lineCount = resolvedLines.length;\n\n // Clamp only when there is genuinely more than fits — a `maxLines` larger\n // than the content must not paint a fade over nothing.\n const overflowable = maxLines != null && maxLines > 0 && lineCount > maxLines;\n const clamped = overflowable && !expanded;\n\n const classes = codeStyles.raw({\n size,\n wrap,\n showLineNumbers,\n });\n const highlighted = new Set(highlightLines ?? []);\n\n const items: CodeLineItem[] = resolvedLines.map((line, index) => {\n const number = startLine + index;\n const isHighlighted = highlighted.has(number);\n return {\n id: `line-${number}`,\n number,\n marker: line.marker,\n lineProps: {\n \"data-line\": String(number),\n \"data-highlighted\": isHighlighted ? \"true\" : \"false\",\n class: css(classes.line, passThrough?.line?.style),\n },\n lineNumberProps: {\n // The number is decoration duplicated by `data-line`; announcing it\n // before every line would bury the code itself.\n \"aria-hidden\": \"true\",\n class: css(classes.lineNumber, passThrough?.lineNumber?.style),\n },\n lineContentProps: {\n class: css(classes.lineContent, passThrough?.lineContent?.style),\n },\n ...(line.marker != null\n ? {\n markerProps: {\n \"aria-hidden\": \"true\",\n class: css(classes.marker, passThrough?.marker?.style),\n },\n }\n : {}),\n tokens: line.tokens.map((token, tokenIndex) => ({\n id: `line-${number}-token-${tokenIndex}`,\n text: token.text,\n tokenProps: {\n // Theme colour arrives per token and is inherently per-instance, so\n // it stays an inline style (which the contract allowlists) rather\n // than becoming an unbounded set of generated classes.\n //\n // A **string**, not an object: that is what connect bags emit\n // (`reactProps` converts it for React; Vue accepts either), and\n // Svelte drops most properties of a spread style object.\n ...(tokenStyle(token) ? { style: tokenStyle(token) } : {}),\n class: cx(\n css(classes.token, passThrough?.token?.style),\n token.className,\n ),\n },\n })),\n };\n });\n\n const copyText = lines ? sourceFromLines(lines) : code;\n const showToolbar = Boolean(lang) || copyable;\n const showExpand = Boolean(collapsible && overflowable);\n\n // The root's surface (background / border / corner) belongs to Panel; the\n // recipe keeps only layout + typography and rides the `inheritedStyle`\n // channel, so the compose order stays panel recipe → code recipe → consumer\n // `passThrough.root.style` → literal `class`. `feedback` reaches the recipe's\n // `colorPalette.*` reads through the panel's own feedback variant.\n const panelApi = connectPanel({\n as,\n dataComponent: \"code\",\n background,\n emphasis,\n feedback,\n translucent,\n padding,\n radius,\n radiusTop,\n radiusBottom,\n radiusLeft,\n radiusRight,\n border,\n borderInteract,\n borderActive,\n reserveBorderSpace,\n shadow,\n interactive,\n active,\n activeEmphasis,\n reactive,\n inheritedStyle: classes.root,\n passThrough: {\n root: {\n style: passThrough?.root?.style,\n props: {\n // The consumer's overrides go FIRST so everything below stays an\n // invariant, exactly as when these were baked after the rest spread:\n // the Astro boot script keys its copy/expand wiring off\n // `data-component=\"code\"` + `data-clamped`, and the recipe reads the\n // clamp height from the inline `--code-max-lines`. Extra root styling\n // belongs on `passThrough.root.style` (the Panda channel), which is\n // merged into the class rather than fighting this attribute.\n ...passThrough?.root?.props,\n \"data-component\": \"code\",\n \"data-lang\": lang ?? \"\",\n \"data-clamped\": clamped ? \"true\" : \"false\",\n ...(maxLines != null\n ? { style: `--code-max-lines:${maxLines}` }\n : {}),\n },\n },\n },\n class: className,\n ...rest,\n });\n\n const api = defineConnect({\n slots: {\n // Placeholder: the panel owns the real root bag (including its\n // `passThrough.root` merge, where the invariants sit ON TOP of the\n // consumer's props). `defineConnect`'s slot map is keyed by the recipe's\n // slot union so `root` has to be present, but the value below is\n // discarded — `rootProps` is replaced with `panelApi.rootProps` in the\n // returned api. `passThrough.root` is withheld here so the consumer's\n // props are not re-applied last and allowed to clobber the markers.\n root: { class: \"\" },\n toolbar: {\n \"data-code-toolbar\": \"\",\n class: css(classes.toolbar, passThrough?.toolbar?.style),\n },\n language: {\n \"data-code-language\": \"\",\n class: css(classes.language, passThrough?.language?.style),\n },\n copyButton: {\n \"data-code-copy\": \"\",\n type: \"button\",\n emphasis: \"ghost\" as const,\n size: \"xs\" as const,\n // The label carries the state, so a screen reader hears the result of\n // the press rather than a button whose name never changes.\n \"aria-label\": copied ? \"Copied\" : \"Copy code\",\n \"data-copied\": copied ? \"true\" : \"false\",\n class: css(classes.copyButton, passThrough?.copyButton?.style),\n },\n scroller: {\n \"data-code-scroller\": \"\",\n // A horizontally scrollable region must be keyboard-reachable, or the\n // overflow is unreadable without a pointer.\n tabindex: 0,\n role: \"region\",\n \"aria-label\": label,\n class: css(classes.scroller, passThrough?.scroller?.style),\n },\n pre: {\n class: css(classes.pre, passThrough?.pre?.style),\n },\n codeEl: {\n ...(lang ? { \"data-language\": lang } : {}),\n class: css(classes.codeEl, passThrough?.codeEl?.style),\n },\n lineNumbers: {\n \"aria-hidden\": \"true\",\n class: css(classes.lineNumbers, passThrough?.lineNumbers?.style),\n },\n line: {\n class: css(classes.line, passThrough?.line?.style),\n },\n lineNumber: {\n class: css(classes.lineNumber, passThrough?.lineNumber?.style),\n },\n lineContent: {\n class: css(classes.lineContent, passThrough?.lineContent?.style),\n },\n token: {\n class: css(classes.token, passThrough?.token?.style),\n },\n marker: {\n class: css(classes.marker, passThrough?.marker?.style),\n },\n overflowFade: {\n \"data-code-fade\": \"\",\n \"aria-hidden\": \"true\",\n class: css(classes.overflowFade, passThrough?.overflowFade?.style),\n },\n expandButton: {\n \"data-code-expand\": \"\",\n type: \"button\",\n emphasis: \"secondary\" as const,\n size: \"xs\" as const,\n \"aria-expanded\": expanded ? \"true\" : \"false\",\n class: css(classes.expandButton, passThrough?.expandButton?.style),\n },\n },\n passThrough: { ...passThrough, root: undefined },\n });\n\n return {\n ...api,\n rootProps: panelApi.rootProps,\n element: panelApi.element,\n items,\n copyText,\n showToolbar,\n showExpand,\n overflowable,\n lineCount,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"connect-code.js","names":[],"sources":["../../src/connect-code.ts"],"sourcesContent":["import { css, cx } from \"@pindoba/styled-system/css\";\nimport { connectPanel } from \"@pindoba/core-panel\";\nimport { reactiveConfig } from \"@pindoba/core-runtime/reactive-config\";\nimport {\n resolveCardContentSurface,\n resolveCardFrame,\n type CardFrameProps,\n type CardPassThrough,\n type CardSlotPassThrough,\n} from \"@pindoba/core-card\";\nimport { defineConnect, toClassString } from \"@pindoba/normalize-props\";\nimport { codeStyles } from \"@pindoba/styles-code\";\nimport type {\n CodeApi,\n CodeBaseProps,\n CodeCardChrome,\n CodeCardDefaults,\n CodeCardFrameNodes,\n CodeResolvedCardFrame,\n CodeLine,\n CodeLineItem,\n CodeToken,\n ConnectCodeOptions,\n} from \"./types.js\";\n\nexport const codeDefaults = {\n size: \"sm\",\n tone: \"neutral\",\n wrap: false,\n showLineNumbers: false,\n startLine: 1,\n collapsible: false,\n expanded: false,\n copyable: false,\n copied: false,\n label: \"Code block\",\n // --- Panel surface defaults ---\n // The root is a Panel; these reproduce the surface the recipe used to paint\n // by hand (`colorPalette.surface.hill` + a 1px muted border + an `sm`\n // corner). `padding: \"none\"` because the block's inset lives on the toolbar /\n // scroller, not the root — so no `--panel-out-padding` republish is needed.\n emphasis: \"secondary\",\n background: \"surface.hill\",\n padding: \"none\",\n radius: \"sm\",\n border: \"muted\",\n shadow: \"none\",\n} as const satisfies Partial<CodeBaseProps>;\n\n/** Split raw source into single-token lines — the unhighlighted fallback. */\nfunction linesFromSource(code: string): CodeLine[] {\n // A trailing newline is a line terminator, not an empty final line; keeping\n // it would render a stray blank row under every file.\n const body = code.endsWith(\"\\n\") ? code.slice(0, -1) : code;\n return body.split(\"\\n\").map((text) => ({ tokens: [{ text }] }));\n}\n\n/** A token's per-instance theme styling, as an inline CSS string. */\nfunction tokenStyle(token: CodeToken): string {\n return [\n token.color != null ? `color:${token.color}` : \"\",\n token.fontStyle != null ? `font-style:${token.fontStyle}` : \"\",\n ]\n .filter(Boolean)\n .join(\";\");\n}\n\n/** Reconstruct the plain text of tokenized lines, for the clipboard. */\nfunction sourceFromLines(lines: CodeLine[]): string {\n return lines\n .map((line) => line.tokens.map((t) => t.text).join(\"\"))\n .join(\"\\n\");\n}\n\n/**\n * Connect Contract v1.1 — the `Code` bags.\n *\n * Highlighting is somebody else's job: this takes already-tokenized `lines` and\n * turns them into spreadable bags. With no `lines` it falls back to rendering\n * `code` as plain text, so a block without a highlighter is still readable\n * rather than empty.\n */\nexport function connectCode(options: ConnectCodeOptions = {}): CodeApi {\n const {\n lines,\n code = \"\",\n lang,\n size = codeDefaults.size,\n tone = codeDefaults.tone,\n wrap = codeDefaults.wrap,\n showLineNumbers = codeDefaults.showLineNumbers,\n startLine = codeDefaults.startLine,\n highlightLines,\n maxLines,\n collapsible = codeDefaults.collapsible,\n expanded = codeDefaults.expanded,\n copyable = codeDefaults.copyable,\n copied = codeDefaults.copied,\n label = codeDefaults.label,\n title,\n card,\n // --- Panel surface axes (delegated to connectPanel) ---\n as = \"div\",\n background = codeDefaults.background,\n emphasis = codeDefaults.emphasis,\n translucent,\n padding = codeDefaults.padding,\n radius = codeDefaults.radius,\n radiusTop,\n radiusBottom,\n radiusLeft,\n radiusRight,\n border = codeDefaults.border,\n borderInteract,\n borderActive,\n reserveBorderSpace,\n shadow = codeDefaults.shadow,\n interactive,\n active,\n activeEmphasis,\n reactive,\n passThrough,\n class: className,\n ...rest\n } = options;\n\n // The client runtime's hydration payload (`reactive`): every input, so\n // `getPindoba(el, \"code\")` can `update()` the block. Rides the block's root\n // bag — the Card root when framed.\n const { reactive: _reactive, ...configInputs } = options;\n const configProps = reactive ? reactiveConfig(\"code\", configInputs) : null;\n\n const resolvedLines: CodeLine[] =\n lines ?? (code ? linesFromSource(code) : []);\n const lineCount = resolvedLines.length;\n\n // Clamp only when there is genuinely more than fits — a `maxLines` larger\n // than the content must not paint a fade over nothing.\n const overflowable = maxLines != null && maxLines > 0 && lineCount > maxLines;\n const clamped = overflowable && !expanded;\n\n // Card-optional: bare unless a card object is passed. The bare path must\n // stay byte-identical to the pre-card block, so the card-only recipe\n // variants are added ONLY when framed.\n const framed = card != null && card !== false;\n const frameInput: CardFrameProps = framed ? card : {};\n const cardContent = { padding: \"none\" as const, ...frameInput.content };\n const { inset, surface: contentSurface } = resolveCardContentSurface(\n cardContent,\n frameInput.contentBackground,\n );\n const surfaceBackground = inset\n ? contentSurface.background\n : (frameInput.background ?? background);\n\n const classes = codeStyles.raw({\n size,\n wrap,\n showLineNumbers,\n ...(framed ? { framed: true, fadeSurface: surfaceBackground } : null),\n });\n const highlighted = new Set(highlightLines ?? []);\n\n const items: CodeLineItem[] = resolvedLines.map((line, index) => {\n const number = startLine + index;\n const isHighlighted = highlighted.has(number);\n return {\n id: `line-${number}`,\n number,\n marker: line.marker,\n lineProps: {\n \"data-line\": String(number),\n \"data-highlighted\": isHighlighted ? \"true\" : \"false\",\n class: css(classes.line, passThrough?.line?.style),\n },\n lineNumberProps: {\n // The number is decoration duplicated by `data-line`; announcing it\n // before every line would bury the code itself.\n \"aria-hidden\": \"true\",\n class: css(classes.lineNumber, passThrough?.lineNumber?.style),\n },\n lineContentProps: {\n class: css(classes.lineContent, passThrough?.lineContent?.style),\n },\n ...(line.marker != null\n ? {\n markerProps: {\n \"aria-hidden\": \"true\",\n class: css(classes.marker, passThrough?.marker?.style),\n },\n }\n : {}),\n tokens: line.tokens.map((token, tokenIndex) => ({\n id: `line-${number}-token-${tokenIndex}`,\n text: token.text,\n tokenProps: {\n // Theme colour arrives per token and is inherently per-instance, so\n // it stays an inline style (which the contract allowlists) rather\n // than becoming an unbounded set of generated classes.\n //\n // A **string**, not an object: that is what connect bags emit\n // (`reactProps` converts it for React; Vue accepts either), and\n // Svelte drops most properties of a spread style object.\n ...(tokenStyle(token) ? { style: tokenStyle(token) } : {}),\n class: cx(\n css(classes.token, passThrough?.token?.style),\n token.className,\n ),\n },\n })),\n };\n });\n\n const copyText = lines ? sourceFromLines(lines) : code;\n const showToolbar = Boolean(lang) || copyable || Boolean(title);\n const showExpand = Boolean(collapsible && overflowable);\n\n // The root's surface (background / border / corner) belongs to Panel; the\n // recipe keeps only layout + typography and rides the `inheritedStyle`\n // channel, so the compose order stays panel recipe → code recipe → consumer\n // `passThrough.root.style` → literal `class`. `tone` reaches the recipe's\n // `colorPalette.*` reads through the panel's own tone variant.\n const panelApi = connectPanel({\n as,\n dataComponent: \"code\",\n background,\n emphasis,\n tone,\n translucent,\n padding,\n radius,\n radiusTop,\n radiusBottom,\n radiusLeft,\n radiusRight,\n border,\n borderInteract,\n borderActive,\n reserveBorderSpace,\n shadow,\n interactive,\n active,\n activeEmphasis,\n reactive,\n inheritedStyle: classes.root,\n passThrough: {\n root: {\n style: passThrough?.root?.style,\n props: {\n // The consumer's overrides go FIRST so everything below stays an\n // invariant, exactly as when these were baked after the rest spread:\n // the Astro boot script keys its copy/expand wiring off\n // `data-component=\"code\"` + `data-clamped`, and the recipe reads the\n // clamp height from the inline `--code-max-lines`. Extra root styling\n // belongs on `passThrough.root.style` (the Panda channel), which is\n // merged into the class rather than fighting this attribute.\n ...passThrough?.root?.props,\n ...configProps,\n \"data-component\": \"code\",\n \"data-lang\": lang ?? \"\",\n \"data-clamped\": clamped ? \"true\" : \"false\",\n ...(maxLines != null\n ? { style: `--code-max-lines:${maxLines}` }\n : {}),\n },\n },\n },\n class: className,\n ...rest,\n });\n\n // --- Card frame input (used only when `frame === \"card\"`) ---------------\n const rootId: unknown = \"id\" in rest ? rest.id : undefined;\n const cardPassThrough: CardPassThrough = passThrough?.card ?? {};\n // The clamp host: in card mode the content region wraps the scroller + fade,\n // so the markers the bare root carries (and the recipe's `[data-clamped]\n // > &` rules and the Astro boot read) move here. Invariant — a consumer's\n // `card.passThrough.content.props` must not unclamp the block.\n const invariantContentProps: Record<string, unknown> = {\n \"data-code-body\": \"\",\n \"data-lang\": lang ?? \"\",\n \"data-clamped\": clamped ? \"true\" : \"false\",\n ...(maxLines != null ? { style: `--code-max-lines:${maxLines}` } : {}),\n };\n const cardDefaults: CodeCardDefaults = {\n size,\n background,\n tone,\n emphasis,\n ...(translucent !== undefined ? { translucent } : null),\n radius,\n border,\n shadow,\n // A hairline under the header / over the footer stands in for the old\n // toolbar's bottom border. An inset card separates by its panel instead.\n ...(inset ? null : { divided: true }),\n content: { padding: \"none\" },\n };\n const cardChrome: CodeCardChrome = {\n heading: title || undefined,\n eyebrow: lang || undefined,\n headingId:\n typeof rootId === \"string\" && rootId !== \"\"\n ? `${rootId}-heading`\n : undefined,\n defaults: cardDefaults,\n rootProps: {\n \"data-card-flushable\": \"\",\n ...rest,\n ...passThrough?.root?.props,\n ...configProps,\n },\n invariantRootProps: { \"data-card-kind\": \"code\" },\n invariantContentProps,\n passThrough: {\n ...cardPassThrough,\n ...(passThrough?.root?.style || cardPassThrough.root\n ? {\n root: {\n ...cardPassThrough.root,\n style: css.raw(\n passThrough?.root?.style,\n cardPassThrough.root?.style,\n ),\n },\n }\n : null),\n content: {\n ...cardPassThrough.content,\n style: css.raw(classes.frameContent, cardPassThrough.content?.style),\n },\n },\n class: toClassString(className),\n };\n\n // `passThrough.card` is the Card's, not a Code slot's.\n const { card: _cardPassThrough, ...slotPassThrough } = passThrough ?? {};\n\n const api = defineConnect({\n slots: {\n // Placeholder: the panel owns the real root bag (including its\n // `passThrough.root` merge, where the invariants sit ON TOP of the\n // consumer's props). `defineConnect`'s slot map is keyed by the recipe's\n // slot union so `root` has to be present, but the value below is\n // discarded — `rootProps` is replaced with `panelApi.rootProps` in the\n // returned api. `passThrough.root` is withheld here so the consumer's\n // props are not re-applied last and allowed to clobber the markers.\n root: { class: \"\" },\n toolbar: {\n \"data-code-toolbar\": \"\",\n class: css(classes.toolbar, passThrough?.toolbar?.style),\n },\n title: {\n \"data-code-title\": \"\",\n class: css(classes.title, passThrough?.title?.style),\n },\n language: {\n \"data-code-language\": \"\",\n class: css(classes.language, passThrough?.language?.style),\n },\n copyButton: {\n \"data-code-copy\": \"\",\n type: \"button\",\n emphasis: \"ghost\" as const,\n size: \"xs\" as const,\n // The label carries the state, so a screen reader hears the result of\n // the press rather than a button whose name never changes.\n \"aria-label\": copied ? \"Copied\" : \"Copy code\",\n \"data-copied\": copied ? \"true\" : \"false\",\n class: css(classes.copyButton, passThrough?.copyButton?.style),\n },\n scroller: {\n \"data-code-scroller\": \"\",\n // A horizontally scrollable region must be keyboard-reachable, or the\n // overflow is unreadable without a pointer.\n tabindex: 0,\n role: \"region\",\n \"aria-label\": label,\n class: css(classes.scroller, passThrough?.scroller?.style),\n },\n pre: {\n class: css(classes.pre, passThrough?.pre?.style),\n },\n codeEl: {\n ...(lang ? { \"data-language\": lang } : {}),\n class: css(classes.codeEl, passThrough?.codeEl?.style),\n },\n lineNumbers: {\n \"aria-hidden\": \"true\",\n class: css(classes.lineNumbers, passThrough?.lineNumbers?.style),\n },\n line: {\n class: css(classes.line, passThrough?.line?.style),\n },\n lineNumber: {\n class: css(classes.lineNumber, passThrough?.lineNumber?.style),\n },\n lineContent: {\n class: css(classes.lineContent, passThrough?.lineContent?.style),\n },\n token: {\n class: css(classes.token, passThrough?.token?.style),\n },\n marker: {\n class: css(classes.marker, passThrough?.marker?.style),\n },\n overflowFade: {\n \"data-code-fade\": \"\",\n \"aria-hidden\": \"true\",\n class: css(classes.overflowFade, passThrough?.overflowFade?.style),\n },\n expandButton: {\n \"data-code-expand\": \"\",\n type: \"button\",\n emphasis: \"secondary\" as const,\n size: \"xs\" as const,\n \"aria-expanded\": expanded ? \"true\" : \"false\",\n class: css(classes.expandButton, passThrough?.expandButton?.style),\n },\n // Card mode only: the frame styles ride `cardChrome.passThrough.content`\n // into the Card's content bag; nothing spreads this one directly.\n frameContent: {\n class: css(classes.frameContent),\n },\n },\n passThrough: { ...slotPassThrough, root: undefined },\n });\n\n return {\n ...api,\n frame: framed ? \"card\" : \"bare\",\n cardChrome,\n rootProps: panelApi.rootProps,\n element: panelApi.element,\n items,\n copyText,\n showToolbar,\n showExpand,\n overflowable,\n lineCount,\n };\n}\n\n/** Merge one card slot's passThrough: `over` wins (styles merged, props spread). */\nfunction mergeCardSlot(\n base: CardSlotPassThrough | undefined,\n over: CardSlotPassThrough | undefined,\n): CardSlotPassThrough | undefined {\n if (!base) return over;\n if (!over) return base;\n return {\n style: css.raw(base.style, over.style),\n props: { ...base.props, ...over.props },\n };\n}\n\nfunction slotOf(\n bag: CardPassThrough | undefined,\n key: string,\n): CardSlotPassThrough | undefined {\n if (!bag) return undefined;\n const entry: unknown = Reflect.get(bag, key);\n return typeof entry === \"object\" && entry !== null ? entry : undefined;\n}\n\n/** Merge two card passThrough bags slot by slot (`over` wins). */\nfunction mergeCardPassThrough(\n base: CardPassThrough,\n over: CardPassThrough | undefined,\n): CardPassThrough {\n const merged: Record<string, CardSlotPassThrough | undefined> = {};\n const keys = new Set([...Object.keys(base), ...Object.keys(over ?? {})]);\n for (const key of keys) {\n merged[key] = mergeCardSlot(slotOf(base, key), slotOf(over, key));\n }\n return merged;\n}\n\n/**\n * Resolve a framed block's Card: the consumer's `card` prop over the block's\n * own defaults (`api.cardChrome`), with the framework's chrome appended AFTER\n * the consumer's slots — the copy button into the header's trailing slot, the\n * expand button into the footer's trailing slot. The same call in all four\n * frameworks; they differ only in their slot type (Svelte snippets, React /\n * Vue nodes; Astro fills `chrome-*` slots and passes none).\n *\n * - `card.heading` / `card.eyebrow` win over `title` / `lang`.\n * - `card.content` merges over the block's `padding: \"none\"` default, so\n * `{ contentBackground }` keeps the code edge to edge on an inset panel.\n * - `card.passThrough` merges over `passThrough.card` and the frame styles.\n * - `data-card-kind` (root) and the clamp markers (content) are re-applied\n * last: nothing overrides them.\n */\nexport function resolveCodeCardFrame<TSlot>(\n card: CardFrameProps<TSlot> | false | undefined,\n chrome: CodeCardChrome,\n nodes: CodeCardFrameNodes<TSlot> = {},\n): CodeResolvedCardFrame<TSlot> {\n const frame: CardFrameProps<TSlot> = card || {};\n const eyebrow = frame.eyebrow ?? chrome.eyebrow;\n const resolved = resolveCardFrame<TSlot>(\n {\n ...frame,\n ...(eyebrow !== undefined ? { eyebrow } : null),\n content: { ...chrome.defaults.content, ...frame.content },\n passThrough: mergeCardPassThrough(chrome.passThrough, frame.passThrough),\n },\n {\n heading: chrome.heading,\n headingId: chrome.headingId,\n defaults: chrome.defaults,\n rootProps: chrome.rootProps,\n trailing: nodes.copy,\n footerTrailing: nodes.expand,\n },\n );\n const passThrough = resolved.cardProps.passThrough;\n return {\n ...resolved,\n cardProps: {\n ...resolved.cardProps,\n passThrough: {\n ...passThrough,\n root: {\n ...passThrough.root,\n props: { ...passThrough.root?.props, ...chrome.invariantRootProps },\n },\n content: {\n ...passThrough.content,\n props: {\n ...passThrough.content?.props,\n ...chrome.invariantContentProps,\n },\n },\n },\n ...(chrome.class !== undefined ? { class: chrome.class } : null),\n },\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { n as CodeBaseProps, v as ElementAttributes } from "../types-DcQoTmcO.js";
|
|
2
|
+
import { Snippet } from "@pindoba/core-card/runtime";
|
|
3
|
+
import { ComponentRuntime } from "@pindoba/core-runtime";
|
|
4
|
+
//#region src/runtime.d.ts
|
|
5
|
+
export type CodeRuntimeProps = Partial<CodeBaseProps<ElementAttributes, Snippet>> & {
|
|
6
|
+
/** Called after the source has been written to the clipboard. */
|
|
7
|
+
onCopy?: (text: string) => void;
|
|
8
|
+
[key: string]: unknown;
|
|
9
|
+
};
|
|
10
|
+
/** Bare and framed share one state, so `copied` / `expanded` survive a `card` change. */
|
|
11
|
+
export declare const codeRuntime: ComponentRuntime<CodeRuntimeProps>;
|
|
12
|
+
//#endregion
|
|
13
|
+
//# sourceMappingURL=runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","names":[],"sources":["../../src/runtime.ts"],"mappings":";;;;YAiDY,mBAAmB,QAC7B,cAAc,mBAAmB;;EAGjC,UAAU;GACT;;;qBAoRU,aAAa,iBAAiB"}
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import { codeCopyLabel, codeExpandLabel, createCodeCopy } from "./code-actions.js";
|
|
2
|
+
import { connectCode, resolveCodeCardFrame } from "./connect-code.js";
|
|
3
|
+
import { buttonRuntime } from "@pindoba/core-button/runtime";
|
|
4
|
+
import { cardFramePropsOf, cardRuntime, componentSnippet, nth, switchMode, textSnippet, treeRegion } from "@pindoba/core-card/runtime";
|
|
5
|
+
import { adopt, computed, onCleanup, rootComponent, scope, untrack } from "@pindoba/core-runtime";
|
|
6
|
+
//#region src/runtime.ts
|
|
7
|
+
/**
|
|
8
|
+
* Code — the vanilla runtime of `code.svelte`.
|
|
9
|
+
*
|
|
10
|
+
* Bare: the Panel root, the optional toolbar (title, language, copy button),
|
|
11
|
+
* the scroller with its lines, the overflow fade and the expand button. Framed
|
|
12
|
+
* (`card`): the Card runtime renders the block from `resolveCodeCardFrame` —
|
|
13
|
+
* the scroller is the card's content, the copy button lands in the header's
|
|
14
|
+
* trailing flank and the expand button in the footer's — exactly the Svelte
|
|
15
|
+
* tree. A structural change re-renders in place (the card root, or the bare
|
|
16
|
+
* root, stays); `card` itself may change too, which swaps the block's root
|
|
17
|
+
* (the hydrated instance follows it).
|
|
18
|
+
*
|
|
19
|
+
* Like the Svelte component it owns `copied` / `expanded` (a click writes
|
|
20
|
+
* them, an incoming prop adopts a new value). The clipboard behaviour is core
|
|
21
|
+
* (`createCodeCopy`); the labels are core (`codeCopyLabel`,
|
|
22
|
+
* `codeExpandLabel`). The buttons are child pindoba components, rendered
|
|
23
|
+
* through the Button runtime.
|
|
24
|
+
*/
|
|
25
|
+
function createState(props) {
|
|
26
|
+
const copied = adopt(() => props().copied ?? false);
|
|
27
|
+
const expanded = adopt(() => props().expanded ?? false);
|
|
28
|
+
const copier = createCodeCopy((value) => copied(value));
|
|
29
|
+
onCleanup(() => copier.dispose());
|
|
30
|
+
return {
|
|
31
|
+
copied,
|
|
32
|
+
expanded,
|
|
33
|
+
copier,
|
|
34
|
+
view: computed(() => ({
|
|
35
|
+
api: connectCode({
|
|
36
|
+
...props(),
|
|
37
|
+
copied: copied(),
|
|
38
|
+
expanded: expanded()
|
|
39
|
+
}),
|
|
40
|
+
props: props()
|
|
41
|
+
}))
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/** The lines (`{#each api.items}`) — rebuilt when their structure changes. */
|
|
45
|
+
function linesShape({ api, props }) {
|
|
46
|
+
return JSON.stringify([Boolean(props.showLineNumbers), api.items.map((l) => [
|
|
47
|
+
l.id,
|
|
48
|
+
Boolean(l.marker && l.markerProps),
|
|
49
|
+
l.tokens.length
|
|
50
|
+
])]);
|
|
51
|
+
}
|
|
52
|
+
function lineKids(view) {
|
|
53
|
+
const { api, props } = view();
|
|
54
|
+
const lineNumbers = Boolean(props.showLineNumbers);
|
|
55
|
+
return api.items.map((line, i) => {
|
|
56
|
+
const at = () => view().api.items[i];
|
|
57
|
+
const kids = [];
|
|
58
|
+
let child = 0;
|
|
59
|
+
if (lineNumbers) kids.push({
|
|
60
|
+
el: "span",
|
|
61
|
+
select: nth(child++),
|
|
62
|
+
bag: () => at()?.lineNumberProps ?? {},
|
|
63
|
+
kids: [{ text: () => String(at()?.number ?? "") }]
|
|
64
|
+
});
|
|
65
|
+
if (line.marker && line.markerProps) kids.push({
|
|
66
|
+
el: "span",
|
|
67
|
+
select: nth(child++),
|
|
68
|
+
bag: () => at()?.markerProps ?? {},
|
|
69
|
+
kids: [{ text: () => at()?.marker ?? "" }]
|
|
70
|
+
});
|
|
71
|
+
kids.push({
|
|
72
|
+
el: "span",
|
|
73
|
+
select: nth(child),
|
|
74
|
+
bag: () => at()?.lineContentProps ?? {},
|
|
75
|
+
kids: line.tokens.map((_, j) => ({
|
|
76
|
+
el: "span",
|
|
77
|
+
select: nth(j),
|
|
78
|
+
bag: () => at()?.tokens[j]?.tokenProps ?? {},
|
|
79
|
+
kids: [{ text: () => at()?.tokens[j]?.text ?? "" }]
|
|
80
|
+
}))
|
|
81
|
+
});
|
|
82
|
+
return {
|
|
83
|
+
el: "span",
|
|
84
|
+
select: nth(i),
|
|
85
|
+
bag: () => at()?.lineProps ?? {},
|
|
86
|
+
kids
|
|
87
|
+
};
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
/** The scroller and (when the block can overflow) its fade (`{#snippet scroller()}`). */
|
|
91
|
+
function scrollerKids(view, fade) {
|
|
92
|
+
const out = [{
|
|
93
|
+
el: "div",
|
|
94
|
+
select: "[data-code-scroller]",
|
|
95
|
+
bag: () => view().api.scrollerProps,
|
|
96
|
+
kids: [{
|
|
97
|
+
el: "pre",
|
|
98
|
+
select: ":scope > pre",
|
|
99
|
+
bag: () => view().api.preProps,
|
|
100
|
+
kids: [{
|
|
101
|
+
el: "code",
|
|
102
|
+
select: ":scope > code",
|
|
103
|
+
bag: () => view().api.codeElProps,
|
|
104
|
+
ref: (code) => treeRegion(code, () => linesShape(view()), () => lineKids(view))
|
|
105
|
+
}]
|
|
106
|
+
}]
|
|
107
|
+
}];
|
|
108
|
+
if (fade) out.push({
|
|
109
|
+
el: "div",
|
|
110
|
+
select: "[data-code-fade]",
|
|
111
|
+
bag: () => view().api.overflowFadeProps
|
|
112
|
+
});
|
|
113
|
+
return out;
|
|
114
|
+
}
|
|
115
|
+
function copyNode(state) {
|
|
116
|
+
return {
|
|
117
|
+
component: buttonRuntime,
|
|
118
|
+
select: "[data-code-copy]",
|
|
119
|
+
props: () => ({
|
|
120
|
+
...state.view().api.copyButtonProps,
|
|
121
|
+
onclick: () => {
|
|
122
|
+
const { api, props } = state.view();
|
|
123
|
+
state.copier.copy(api.copyText, props.onCopy);
|
|
124
|
+
}
|
|
125
|
+
}),
|
|
126
|
+
children: textSnippet(() => codeCopyLabel(state.copied()))
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
function expandNode(state) {
|
|
130
|
+
return {
|
|
131
|
+
component: buttonRuntime,
|
|
132
|
+
select: "[data-code-expand]",
|
|
133
|
+
props: () => ({
|
|
134
|
+
...state.view().api.expandButtonProps,
|
|
135
|
+
onclick: () => state.expanded(!state.expanded())
|
|
136
|
+
}),
|
|
137
|
+
children: textSnippet(() => codeExpandLabel(state.expanded(), state.view().api.lineCount))
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
function bareShape({ api, props }) {
|
|
141
|
+
return JSON.stringify([
|
|
142
|
+
api.showToolbar,
|
|
143
|
+
Boolean(props.title),
|
|
144
|
+
Boolean(props.copyable),
|
|
145
|
+
api.overflowable,
|
|
146
|
+
api.showExpand
|
|
147
|
+
]);
|
|
148
|
+
}
|
|
149
|
+
function bareKids(state) {
|
|
150
|
+
const { api, props } = state.view();
|
|
151
|
+
const view = state.view;
|
|
152
|
+
const kids = [];
|
|
153
|
+
if (api.showToolbar) {
|
|
154
|
+
const toolbar = [];
|
|
155
|
+
if (props.title) toolbar.push({
|
|
156
|
+
el: "span",
|
|
157
|
+
select: "[data-code-title]",
|
|
158
|
+
bag: () => view().api.titleProps,
|
|
159
|
+
kids: [{ text: () => String(view().props.title ?? "") }]
|
|
160
|
+
});
|
|
161
|
+
toolbar.push({
|
|
162
|
+
el: "span",
|
|
163
|
+
select: "[data-code-language]",
|
|
164
|
+
bag: () => view().api.languageProps,
|
|
165
|
+
kids: [{ text: () => view().props.lang ?? "" }]
|
|
166
|
+
});
|
|
167
|
+
if (props.copyable) toolbar.push(copyNode(state));
|
|
168
|
+
kids.push({
|
|
169
|
+
el: "div",
|
|
170
|
+
select: "[data-code-toolbar]",
|
|
171
|
+
bag: () => view().api.toolbarProps,
|
|
172
|
+
kids: toolbar
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
kids.push(...scrollerKids(view, api.overflowable));
|
|
176
|
+
if (api.showExpand) kids.push(expandNode(state));
|
|
177
|
+
return kids;
|
|
178
|
+
}
|
|
179
|
+
const bare = rootComponent({
|
|
180
|
+
state: (props) => untrack(props),
|
|
181
|
+
api: (_props, state) => ({
|
|
182
|
+
tag: state.view().api.element,
|
|
183
|
+
root: state.view().api.rootProps
|
|
184
|
+
}),
|
|
185
|
+
layout: [],
|
|
186
|
+
bind(root, _api, state) {
|
|
187
|
+
treeRegion(root, () => bareShape(state.view()), () => bareKids(state));
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
/** The Card the framed block renders (card-frame.svelte over `resolveCodeCardFrame`). */
|
|
191
|
+
function framedProps(state) {
|
|
192
|
+
const copy = componentSnippet(copyNode(state));
|
|
193
|
+
const expand = componentSnippet(expandNode(state));
|
|
194
|
+
const body = (fade) => {
|
|
195
|
+
const snippet = () => {
|
|
196
|
+
const host = document.createElement("div");
|
|
197
|
+
treeRegion(host, () => "", () => scrollerKids(state.view, fade));
|
|
198
|
+
return [...host.childNodes];
|
|
199
|
+
};
|
|
200
|
+
snippet.hydrate = (_nodes, parent) => treeRegion(parent, () => "", () => scrollerKids(state.view, fade));
|
|
201
|
+
return snippet;
|
|
202
|
+
};
|
|
203
|
+
const bodies = {
|
|
204
|
+
true: body(true),
|
|
205
|
+
false: body(false)
|
|
206
|
+
};
|
|
207
|
+
const cache = /* @__PURE__ */ new Map();
|
|
208
|
+
return () => {
|
|
209
|
+
const { api, props } = state.view();
|
|
210
|
+
const frame = resolveCodeCardFrame(props.card, api.cardChrome, {
|
|
211
|
+
copy: props.copyable ? copy : void 0,
|
|
212
|
+
expand: api.showExpand ? expand : void 0
|
|
213
|
+
});
|
|
214
|
+
return {
|
|
215
|
+
...cardFramePropsOf(frame, cache),
|
|
216
|
+
children: bodies[String(api.overflowable)]
|
|
217
|
+
};
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
const isFramed = (props) => props.card != null && props.card !== false;
|
|
221
|
+
function render(state) {
|
|
222
|
+
let cardProps;
|
|
223
|
+
const card = () => cardProps ??= framedProps(state);
|
|
224
|
+
return {
|
|
225
|
+
mount: (mode) => mode === "card" ? cardRuntime.mount(card(), {}) : bare.mount(() => state, {}),
|
|
226
|
+
hydrate: (mode, root) => mode === "card" ? cardRuntime.hydrate(root, card()) : bare.hydrate(root, () => state)
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
const modeOf = (state) => () => isFramed(state.view().props) ? "card" : "bare";
|
|
230
|
+
/** Bare and framed share one state, so `copied` / `expanded` survive a `card` change. */
|
|
231
|
+
const codeRuntime = {
|
|
232
|
+
mount(props) {
|
|
233
|
+
let nodes = [];
|
|
234
|
+
const dispose = scope(() => {
|
|
235
|
+
const state = createState(props);
|
|
236
|
+
nodes = switchMode(modeOf(state), render(state));
|
|
237
|
+
});
|
|
238
|
+
return {
|
|
239
|
+
nodes,
|
|
240
|
+
dispose
|
|
241
|
+
};
|
|
242
|
+
},
|
|
243
|
+
hydrate(root, props) {
|
|
244
|
+
return { destroy: scope(() => {
|
|
245
|
+
const state = createState(props);
|
|
246
|
+
switchMode(modeOf(state), render(state), root);
|
|
247
|
+
}) };
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
//#endregion
|
|
251
|
+
export { codeRuntime };
|
|
252
|
+
|
|
253
|
+
//# sourceMappingURL=runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.js","names":[],"sources":["../../src/runtime.ts"],"sourcesContent":["/**\n * Code — the vanilla runtime of `code.svelte`.\n *\n * Bare: the Panel root, the optional toolbar (title, language, copy button),\n * the scroller with its lines, the overflow fade and the expand button. Framed\n * (`card`): the Card runtime renders the block from `resolveCodeCardFrame` —\n * the scroller is the card's content, the copy button lands in the header's\n * trailing flank and the expand button in the footer's — exactly the Svelte\n * tree. A structural change re-renders in place (the card root, or the bare\n * root, stays); `card` itself may change too, which swaps the block's root\n * (the hydrated instance follows it).\n *\n * Like the Svelte component it owns `copied` / `expanded` (a click writes\n * them, an incoming prop adopts a new value). The clipboard behaviour is core\n * (`createCodeCopy`); the labels are core (`codeCopyLabel`,\n * `codeExpandLabel`). The buttons are child pindoba components, rendered\n * through the Button runtime.\n */\nimport { buttonRuntime } from \"@pindoba/core-button/runtime\";\nimport {\n cardFramePropsOf,\n cardRuntime,\n componentSnippet,\n nth,\n switchMode,\n textSnippet,\n treeRegion,\n type CardFramePlanCache,\n type Snippet,\n type TreeNode,\n} from \"@pindoba/core-card/runtime\";\nimport {\n adopt,\n computed,\n onCleanup,\n rootComponent,\n scope,\n untrack,\n type Accessor,\n type ComponentRuntime,\n} from \"@pindoba/core-runtime\";\nimport {\n codeCopyLabel,\n codeExpandLabel,\n createCodeCopy,\n} from \"./code-actions.js\";\nimport { connectCode, resolveCodeCardFrame } from \"./connect-code.js\";\nimport type { CodeApi, CodeBaseProps, ElementAttributes } from \"./types.js\";\n\nexport type CodeRuntimeProps = Partial<\n CodeBaseProps<ElementAttributes, Snippet>\n> & {\n /** Called after the source has been written to the clipboard. */\n onCopy?: (text: string) => void;\n [key: string]: unknown;\n};\n\ninterface View {\n api: CodeApi;\n props: CodeRuntimeProps;\n}\n\nfunction createState(props: Accessor<CodeRuntimeProps>) {\n const copied = adopt(() => props().copied ?? false);\n const expanded = adopt(() => props().expanded ?? false);\n const copier = createCodeCopy((value) => copied(value));\n onCleanup(() => copier.dispose());\n const view = computed<View>(() => ({\n api: connectCode({\n ...props(),\n copied: copied(),\n expanded: expanded(),\n }),\n props: props(),\n }));\n return { copied, expanded, copier, view };\n}\ntype State = ReturnType<typeof createState>;\n\n/** The lines (`{#each api.items}`) — rebuilt when their structure changes. */\nfunction linesShape({ api, props }: View): string {\n return JSON.stringify([\n Boolean(props.showLineNumbers),\n api.items.map((l) => [\n l.id,\n Boolean(l.marker && l.markerProps),\n l.tokens.length,\n ]),\n ]);\n}\n\nfunction lineKids(view: Accessor<View>): TreeNode[] {\n const { api, props } = view();\n const lineNumbers = Boolean(props.showLineNumbers);\n return api.items.map((line, i) => {\n const at = () => view().api.items[i];\n const kids: TreeNode[] = [];\n let child = 0;\n if (lineNumbers) {\n kids.push({\n el: \"span\",\n select: nth(child++),\n bag: () => at()?.lineNumberProps ?? {},\n kids: [{ text: () => String(at()?.number ?? \"\") }],\n });\n }\n if (line.marker && line.markerProps) {\n kids.push({\n el: \"span\",\n select: nth(child++),\n bag: () => at()?.markerProps ?? {},\n kids: [{ text: () => at()?.marker ?? \"\" }],\n });\n }\n kids.push({\n el: \"span\",\n select: nth(child),\n bag: () => at()?.lineContentProps ?? {},\n kids: line.tokens.map((_, j) => ({\n el: \"span\",\n select: nth(j),\n bag: () => at()?.tokens[j]?.tokenProps ?? {},\n kids: [{ text: () => at()?.tokens[j]?.text ?? \"\" }],\n })),\n });\n return {\n el: \"span\",\n select: nth(i),\n bag: () => at()?.lineProps ?? {},\n kids,\n };\n });\n}\n\n/** The scroller and (when the block can overflow) its fade (`{#snippet scroller()}`). */\nfunction scrollerKids(view: Accessor<View>, fade: boolean): TreeNode[] {\n const out: TreeNode[] = [\n {\n el: \"div\",\n select: \"[data-code-scroller]\",\n bag: () => view().api.scrollerProps,\n kids: [\n {\n el: \"pre\",\n select: \":scope > pre\",\n bag: () => view().api.preProps,\n kids: [\n {\n el: \"code\",\n select: \":scope > code\",\n bag: () => view().api.codeElProps,\n ref: (code) =>\n treeRegion(\n code,\n () => linesShape(view()),\n () => lineKids(view),\n ),\n },\n ],\n },\n ],\n },\n ];\n if (fade) {\n out.push({\n el: \"div\",\n select: \"[data-code-fade]\",\n bag: () => view().api.overflowFadeProps,\n });\n }\n return out;\n}\n\nfunction copyNode(state: State): TreeNode {\n return {\n component: buttonRuntime,\n select: \"[data-code-copy]\",\n props: () => ({\n ...state.view().api.copyButtonProps,\n onclick: () => {\n const { api, props } = state.view();\n void state.copier.copy(api.copyText, props.onCopy);\n },\n }),\n children: textSnippet(() => codeCopyLabel(state.copied())),\n };\n}\n\nfunction expandNode(state: State): TreeNode {\n return {\n component: buttonRuntime,\n select: \"[data-code-expand]\",\n props: () => ({\n ...state.view().api.expandButtonProps,\n onclick: () => state.expanded(!state.expanded()),\n }),\n children: textSnippet(() =>\n codeExpandLabel(state.expanded(), state.view().api.lineCount),\n ),\n };\n}\n\n// ─── bare ───────────────────────────────────────────────────────────────────\n\nfunction bareShape({ api, props }: View): string {\n return JSON.stringify([\n api.showToolbar,\n Boolean(props.title),\n Boolean(props.copyable),\n api.overflowable,\n api.showExpand,\n ]);\n}\n\nfunction bareKids(state: State): TreeNode[] {\n const { api, props } = state.view();\n const view = state.view;\n const kids: TreeNode[] = [];\n if (api.showToolbar) {\n const toolbar: TreeNode[] = [];\n if (props.title) {\n toolbar.push({\n el: \"span\",\n select: \"[data-code-title]\",\n bag: () => view().api.titleProps,\n kids: [{ text: () => String(view().props.title ?? \"\") }],\n });\n }\n toolbar.push({\n el: \"span\",\n select: \"[data-code-language]\",\n bag: () => view().api.languageProps,\n kids: [{ text: () => view().props.lang ?? \"\" }],\n });\n if (props.copyable) toolbar.push(copyNode(state));\n kids.push({\n el: \"div\",\n select: \"[data-code-toolbar]\",\n bag: () => view().api.toolbarProps,\n kids: toolbar,\n });\n }\n kids.push(...scrollerKids(view, api.overflowable));\n if (api.showExpand) kids.push(expandNode(state));\n return kids;\n}\n\nconst bare = rootComponent<State, State>({\n // The props are the shared state itself (see `codeRuntime`).\n state: (props) => untrack(props),\n api: (_props, state) => ({\n tag: state.view().api.element,\n root: state.view().api.rootProps,\n }),\n layout: [],\n bind(root, _api, state) {\n treeRegion(\n root,\n () => bareShape(state.view()),\n () => bareKids(state),\n );\n },\n});\n\n// ─── framed ─────────────────────────────────────────────────────────────────\n\n/** The Card the framed block renders (card-frame.svelte over `resolveCodeCardFrame`). */\nfunction framedProps(state: State) {\n const copy = componentSnippet(copyNode(state));\n const expand = componentSnippet(expandNode(state));\n // The scroller is the card's content; a different snippet per fade\n // presence, so the body re-renders when the block starts / stops\n // overflowing (and is left alone otherwise).\n const body = (fade: boolean): Snippet => {\n const snippet: Snippet = () => {\n const host = document.createElement(\"div\");\n treeRegion(\n host,\n () => \"\",\n () => scrollerKids(state.view, fade),\n );\n return [...host.childNodes];\n };\n snippet.hydrate = (_nodes, parent) =>\n treeRegion(\n parent,\n () => \"\",\n () => scrollerKids(state.view, fade),\n );\n return snippet;\n };\n const bodies = { true: body(true), false: body(false) };\n const cache: CardFramePlanCache = new Map();\n return () => {\n const { api, props } = state.view();\n const frame = resolveCodeCardFrame<Snippet>(props.card, api.cardChrome, {\n copy: props.copyable ? copy : undefined,\n expand: api.showExpand ? expand : undefined,\n });\n return {\n ...cardFramePropsOf(frame, cache),\n children: bodies[String(api.overflowable) as \"true\" | \"false\"],\n };\n };\n}\n\n// ─── the runtime ────────────────────────────────────────────────────────────\n\nconst isFramed = (props: CodeRuntimeProps) =>\n props.card != null && props.card !== false;\n\nfunction render(state: State) {\n let cardProps: ReturnType<typeof framedProps> | undefined;\n const card = () => (cardProps ??= framedProps(state));\n return {\n mount: (mode: \"card\" | \"bare\") =>\n mode === \"card\"\n ? cardRuntime.mount(card(), {})\n : bare.mount(() => state, {}),\n hydrate: (mode: \"card\" | \"bare\", root: Element) =>\n mode === \"card\"\n ? cardRuntime.hydrate(root, card())\n : bare.hydrate(root, () => state),\n };\n}\n\nconst modeOf = (state: State) => () =>\n isFramed(state.view().props) ? (\"card\" as const) : (\"bare\" as const);\n\n/** Bare and framed share one state, so `copied` / `expanded` survive a `card` change. */\nexport const codeRuntime: ComponentRuntime<CodeRuntimeProps> = {\n mount(props) {\n let nodes: Node[] = [];\n const dispose = scope(() => {\n const state = createState(props);\n nodes = switchMode(modeOf(state), render(state));\n });\n return { nodes, dispose };\n },\n hydrate(root, props) {\n const destroy = scope(() => {\n const state = createState(props);\n switchMode(modeOf(state), render(state), root);\n });\n return { destroy };\n },\n};\n"],"mappings":"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"}
|
package/dist/src/shiki.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { CodeLine } from "
|
|
1
|
+
import { s as CodeLine } from "../types-DcQoTmcO.js";
|
|
2
2
|
//#region src/shiki.d.ts
|
|
3
3
|
/** The shape of a Shiki themed token — structurally, not by import. */
|
|
4
|
-
interface ShikiThemedToken {
|
|
4
|
+
export interface ShikiThemedToken {
|
|
5
5
|
content: string;
|
|
6
6
|
color?: string;
|
|
7
7
|
/** Shiki's font-style bitfield: 1 italic, 2 bold, 4 underline. */
|
|
@@ -9,11 +9,11 @@ interface ShikiThemedToken {
|
|
|
9
9
|
htmlStyle?: Record<string, string> | string;
|
|
10
10
|
}
|
|
11
11
|
/** Shiki's `codeToTokens()` result, narrowed to what the adapter reads. */
|
|
12
|
-
interface ShikiTokensResult {
|
|
12
|
+
export interface ShikiTokensResult {
|
|
13
13
|
tokens: ShikiThemedToken[][];
|
|
14
14
|
}
|
|
15
15
|
/** Translate Shiki's bitfield into a CSS declaration string. */
|
|
16
|
-
declare function shikiFontStyle(fontStyle: number | undefined): string | undefined;
|
|
16
|
+
export declare function shikiFontStyle(fontStyle: number | undefined): string | undefined;
|
|
17
17
|
/**
|
|
18
18
|
* Convert `codeToTokens()` output into the `lines` a `Code` component takes.
|
|
19
19
|
*
|
|
@@ -26,12 +26,11 @@ declare function shikiFontStyle(fontStyle: number | undefined): string | undefin
|
|
|
26
26
|
* );
|
|
27
27
|
* ```
|
|
28
28
|
*/
|
|
29
|
-
declare function shikiToLines(result: ShikiTokensResult): CodeLine[];
|
|
29
|
+
export declare function shikiToLines(result: ShikiTokensResult): CodeLine[];
|
|
30
30
|
/**
|
|
31
31
|
* Same conversion from a bare token grid, for callers holding
|
|
32
32
|
* `result.tokens` rather than the whole result object.
|
|
33
33
|
*/
|
|
34
|
-
declare function shikiTokensToLines(tokens: ShikiThemedToken[][]): CodeLine[];
|
|
34
|
+
export declare function shikiTokensToLines(tokens: ShikiThemedToken[][]): CodeLine[];
|
|
35
35
|
//#endregion
|
|
36
|
-
export { ShikiThemedToken, ShikiTokensResult, shikiFontStyle, shikiToLines, shikiTokensToLines };
|
|
37
36
|
//# sourceMappingURL=shiki.d.ts.map
|
package/dist/src/shiki.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shiki.d.ts","names":[],"sources":["../../src/shiki.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"shiki.d.ts","names":[],"sources":["../../src/shiki.ts"],"mappings":";;;iBAgBiB;EACf;EACA;;EAEA;EACA,YAAY;;;iBAIG;EACf,QAAQ;;;wBASM,eACd;;;;;;;;;;;;;wBAgCc,aAAa,QAAQ,oBAAoB;;;;;wBAQzC,mBAAmB,QAAQ,uBAAuB"}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { PanelElement, PanelElement as PanelElement$1, PanelInheritedProps, PanelRootProps, PanelRootProps as PanelRootProps$1 } from "@pindoba/core-panel";
|
|
2
|
+
import { CardFrameProps, CardFrameSlotInput, CardPassThrough, ResolvedCardFrame } from "@pindoba/core-card";
|
|
2
3
|
import { ConnectReturn } from "@pindoba/normalize-props";
|
|
3
4
|
import { CodeSlots, CodeStyleProps } from "@pindoba/styles-code";
|
|
4
5
|
import { SystemStyleObject } from "@pindoba/styled-system/types";
|
|
5
|
-
import {
|
|
6
|
+
import { PanelTone } from "@pindoba/styles-panel";
|
|
6
7
|
//#region src/types.d.ts
|
|
7
8
|
type ElementAttributes = Partial<Record<string, unknown>>;
|
|
8
9
|
type CodeSize = NonNullable<CodeStyleProps["size"]>;
|
|
9
|
-
type
|
|
10
|
+
type CodeTone = PanelTone;
|
|
10
11
|
/**
|
|
11
12
|
* One coloured run of text within a line.
|
|
12
13
|
*
|
|
@@ -40,8 +41,22 @@ interface CodeSlotPassThrough<T = ElementAttributes> {
|
|
|
40
41
|
style?: SystemStyleObject;
|
|
41
42
|
props?: T;
|
|
42
43
|
}
|
|
43
|
-
|
|
44
|
-
|
|
44
|
+
/** Recipe slots that are card-mode styling hooks, not consumer-addressable elements. */
|
|
45
|
+
type CodeInternalSlots = "frameContent";
|
|
46
|
+
type CodePassThrough<RootElementAttributes = ElementAttributes> = { [K in Exclude<CodeSlots, CodeInternalSlots>]?: CodeSlotPassThrough<K extends "root" ? RootElementAttributes : ElementAttributes>; } & {
|
|
47
|
+
/**
|
|
48
|
+
* Card mode: passThrough forwarded to the Card (`root`, `header`, `content`,
|
|
49
|
+
* `footer`, …). Merged under `card.passThrough`. In card mode
|
|
50
|
+
* `passThrough.root` still reaches the Card root.
|
|
51
|
+
*/
|
|
52
|
+
card?: CardPassThrough;
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* The `card` prop: frame the block in a Card. Every Card frame prop is
|
|
56
|
+
* accepted; `heading` defaults to `title` and `eyebrow` to `lang`.
|
|
57
|
+
*/
|
|
58
|
+
type CodeCardProps<TSlot = unknown> = CardFrameProps<TSlot>;
|
|
59
|
+
interface CodeBaseProps<RootElementAttributes = ElementAttributes, TSlot = unknown> extends Omit<PanelInheritedProps<RootElementAttributes>, "passThrough"> {
|
|
45
60
|
/**
|
|
46
61
|
* Pre-tokenized lines from a highlighter. When omitted, `code` is rendered
|
|
47
62
|
* as unhighlighted plain text — so the component degrades to a readable
|
|
@@ -56,11 +71,26 @@ interface CodeBaseProps<RootElementAttributes = ElementAttributes> extends Omit<
|
|
|
56
71
|
*/
|
|
57
72
|
code?: string;
|
|
58
73
|
/**
|
|
59
|
-
* Language label shown in the toolbar
|
|
60
|
-
* does no highlighting of its own.
|
|
74
|
+
* Language label shown in the toolbar (the card eyebrow when framed).
|
|
75
|
+
* Purely presentational — the component does no highlighting of its own.
|
|
61
76
|
* @group Content
|
|
62
77
|
*/
|
|
63
78
|
lang?: string;
|
|
79
|
+
/**
|
|
80
|
+
* A title for the block, e.g. its filename. Bare: the toolbar's leading
|
|
81
|
+
* text, before the language label. Framed (`card`): the card heading.
|
|
82
|
+
* @group Content
|
|
83
|
+
*/
|
|
84
|
+
title?: string;
|
|
85
|
+
/**
|
|
86
|
+
* Frame the block in a Card. Unset or `false` renders the bare block (the
|
|
87
|
+
* default). With an object, `title` becomes the card heading, `lang` the
|
|
88
|
+
* eyebrow, the copy button moves to the header's trailing slot, the expand
|
|
89
|
+
* button to the footer, and the scroller is the card content
|
|
90
|
+
* (`content.padding: "none"`; set `contentBackground` for an inset panel).
|
|
91
|
+
* @group Appearance
|
|
92
|
+
*/
|
|
93
|
+
card?: CodeCardProps<TSlot> | false;
|
|
64
94
|
/**
|
|
65
95
|
* Type scale of the block. `xs` and `sm` currently render at the same font
|
|
66
96
|
* size — the scale's smallest step is `xs`, so separating them means moving
|
|
@@ -158,11 +188,63 @@ interface CodeLineItem {
|
|
|
158
188
|
markerProps?: Record<string, unknown>;
|
|
159
189
|
tokens: CodeTokenItem[];
|
|
160
190
|
}
|
|
191
|
+
/** The card defaults the block derives from its own surface props. */
|
|
192
|
+
type CodeCardDefaults = Pick<CardFrameProps, "size" | "background" | "tone" | "emphasis" | "translucent" | "radius" | "border" | "shadow" | "divided" | "content">;
|
|
193
|
+
/**
|
|
194
|
+
* Everything `connectCode` resolves for the block's Card frame except the
|
|
195
|
+
* framework's own chrome nodes (copy / expand buttons). Feed it to
|
|
196
|
+
* `resolveCodeCardFrame(card, api.cardChrome, nodes)`.
|
|
197
|
+
*/
|
|
198
|
+
interface CodeCardChrome {
|
|
199
|
+
/** The card heading the block derives itself — its `title`. */
|
|
200
|
+
heading: string | undefined;
|
|
201
|
+
/** The card eyebrow the block derives itself — its `lang`. */
|
|
202
|
+
eyebrow: string | undefined;
|
|
203
|
+
/** Deterministic heading id (`${id}-heading`) when the block has an `id`. */
|
|
204
|
+
headingId: string | undefined;
|
|
205
|
+
/** The block's surface as card defaults (under the consumer's `card`). */
|
|
206
|
+
defaults: CodeCardDefaults;
|
|
207
|
+
/** Card root attributes: flush marker, the block's HTML attributes, `passThrough.root.props`. */
|
|
208
|
+
rootProps: Record<string, unknown>;
|
|
209
|
+
/** Card root attributes nothing may override (`data-card-kind`). */
|
|
210
|
+
invariantRootProps: Record<string, unknown>;
|
|
211
|
+
/**
|
|
212
|
+
* Card content attributes nothing may override: the clamp host markers
|
|
213
|
+
* (`data-code-body`, `data-lang`, `data-clamped`, `--code-max-lines`).
|
|
214
|
+
*/
|
|
215
|
+
invariantContentProps: Record<string, unknown>;
|
|
216
|
+
/** `passThrough.card` + the block's frame styles (`passThrough.root.style` included). */
|
|
217
|
+
passThrough: CardPassThrough;
|
|
218
|
+
/** The consumer's `class`, merged onto the card root. */
|
|
219
|
+
class: string | undefined;
|
|
220
|
+
}
|
|
221
|
+
/** The chrome a framework relocates into the block's Card. */
|
|
222
|
+
interface CodeCardFrameNodes<TSlot> {
|
|
223
|
+
/** The copy button → header trailing (after the consumer's `card.trailing`). */
|
|
224
|
+
copy?: CardFrameSlotInput<TSlot>;
|
|
225
|
+
/** The expand button → footer trailing (after the consumer's `card.footer.trailing`). */
|
|
226
|
+
expand?: CardFrameSlotInput<TSlot>;
|
|
227
|
+
}
|
|
228
|
+
/** `resolveCodeCardFrame()` — a resolved card frame whose root also carries the block's `class`. */
|
|
229
|
+
type CodeResolvedCardFrame<TSlot> = ResolvedCardFrame<TSlot> & {
|
|
230
|
+
cardProps: {
|
|
231
|
+
class?: string;
|
|
232
|
+
};
|
|
233
|
+
};
|
|
161
234
|
type CodeApi = ConnectReturn<CodeSlots> & {
|
|
235
|
+
/**
|
|
236
|
+
* `"bare"` (default, and `card={false}`) — render the block root
|
|
237
|
+
* `<element {...rootProps}>` with its toolbar. `"card"` (`card={{…}}`) —
|
|
238
|
+
* render `<CardFrame>` from `resolveCodeCardFrame(card, cardChrome, nodes)`
|
|
239
|
+
* with the scroller + fade as its content.
|
|
240
|
+
*/
|
|
241
|
+
frame: "card" | "bare";
|
|
242
|
+
/** The card frame input (card mode). See {@link CodeCardChrome}. */
|
|
243
|
+
cardChrome: CodeCardChrome;
|
|
162
244
|
/** The panel-produced root bag (`data-component="code"` + the nest hook). */
|
|
163
|
-
rootProps: PanelRootProps
|
|
245
|
+
rootProps: PanelRootProps;
|
|
164
246
|
/** Resolved root tag from `as` — templates render this element. */
|
|
165
|
-
element: PanelElement
|
|
247
|
+
element: PanelElement;
|
|
166
248
|
/** Per-line bags, in order. */
|
|
167
249
|
items: CodeLineItem[];
|
|
168
250
|
/** The exact text the copy button should write to the clipboard. */
|
|
@@ -181,5 +263,5 @@ type CodeApi = ConnectReturn<CodeSlots> & {
|
|
|
181
263
|
lineCount: number;
|
|
182
264
|
};
|
|
183
265
|
//#endregion
|
|
184
|
-
export {
|
|
185
|
-
//# sourceMappingURL=types.d.ts.map
|
|
266
|
+
export { ConnectCodeOptions as _, CodeCardFrameNodes as a, PanelRootProps$1 as b, CodeLineItem as c, CodeSize as d, CodeSlotPassThrough as f, CodeTone as g, CodeTokenItem as h, CodeCardDefaults as i, CodePassThrough as l, CodeToken as m, CodeBaseProps as n, CodeCardProps as o, CodeSlots as p, CodeCardChrome as r, CodeLine as s, CodeApi as t, CodeResolvedCardFrame as u, ElementAttributes as v, PanelElement$1 as y };
|
|
267
|
+
//# sourceMappingURL=types-DcQoTmcO.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types-DcQoTmcO.d.ts","names":[],"sources":["../src/types.ts"],"mappings":";;;;;;;KAmBY,oBAAoB,QAAQ;KAE5B,WAAW,YAAY;KACvB,WAAW;;;;;;;;;;;;;;;UAkBN;EACf;;EAEA;;EAEA;;EAEA;;;UAIe;EACf,QAAQ;;EAER;;UAKe,oBAAoB,IAAI;EACvC,QAAQ;EACR,QAAQ;;;KAIL;KAEO,gBAAgB,wBAAwB,wBACjD,KAAK,QAAQ,WAAW,sBAAsB,oBAC7C,mBAAmB,wBAAwB;;;;;;EAQ7C,OAAO;;;;;;KASG,cAAc,mBAAmB,eAAe;UAE3C,cACf,wBAAwB,mBACxB,yBAOQ,KAAK,oBAAoB;;;;;;;EAOjC,QAAQ;;;;;;EAMR;;;;;;EAMA;;;;;;EAMA;;;;;;;;;EASA,OAAO,cAAc;;;;;;;;EAQrB,OAAO;;;;;;EAMP;;;;;;EAMA;;;;;;;EAOA;;;;;EAKA;;;;;EAKA;;;;;;EAMA;;;;;;;EAOA;;;;;;EAMA;;;;;;;EAOA;;;;;;EAMA;;;;;EAKA,cAAc,gBAAgB;;UAKf,2BAA2B,QAC1C,cAAc;EAEd;GACC;;;UAIc;EACf;EACA;EACA,YAAY;;;UAIG;EACf;;EAEA;EACA;EACA,WAAW;EACX,iBAAiB;EACjB,kBAAkB;EAClB,cAAc;EACd,QAAQ;;;KAIE,mBAAmB,KAC7B;;;;;;UAkBe;;EAEf;;EAEA;;EAEA;;EAEA,UAAU;;EAEV,WAAW;;EAEX,oBAAoB;;;;;EAKpB,uBAAuB;;EAEvB,aAAa;;EAEb;;;UAIe,mBAAmB;;EAElC,OAAO,mBAAmB;;EAE1B,SAAS,mBAAmB;;;KAIlB,sBAAsB,SAAS,kBAAkB;EAC3D;IAAa;;;KAGH,UAAU,cAAc;;;;;;;EAOlC;;EAEA,YAAY;;EAEZ,WAAW;;EAEX,SAAS;;EAET,OAAO;;EAEP;;EAEA;;EAEA;;;;;;EAMA;;EAEA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pindoba/core-code",
|
|
3
|
-
"version": "0.0.0-alpha-
|
|
3
|
+
"version": "0.0.0-alpha-20261110151933",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -11,24 +11,33 @@
|
|
|
11
11
|
"types": "./dist/index.d.ts",
|
|
12
12
|
"import": "./dist/index.js"
|
|
13
13
|
},
|
|
14
|
+
"./runtime": {
|
|
15
|
+
"types": "./dist/src/runtime.d.ts",
|
|
16
|
+
"import": "./dist/src/runtime.js"
|
|
17
|
+
},
|
|
14
18
|
"./shiki": {
|
|
15
19
|
"types": "./dist/src/shiki.d.ts",
|
|
16
20
|
"import": "./dist/src/shiki.js"
|
|
17
21
|
}
|
|
18
22
|
},
|
|
19
23
|
"files": [
|
|
20
|
-
"dist"
|
|
24
|
+
"dist",
|
|
25
|
+
"!dist/**/__test__"
|
|
21
26
|
],
|
|
22
27
|
"dependencies": {
|
|
23
|
-
"@pindoba/core-
|
|
24
|
-
"@pindoba/
|
|
25
|
-
"@pindoba/
|
|
26
|
-
"@pindoba/
|
|
27
|
-
"@pindoba/
|
|
28
|
+
"@pindoba/core-button": "0.0.0-alpha-20261110151933",
|
|
29
|
+
"@pindoba/core-card": "0.0.0-alpha-20261110151933",
|
|
30
|
+
"@pindoba/core-panel": "0.0.0-alpha-20261110151933",
|
|
31
|
+
"@pindoba/core-runtime": "0.0.0-alpha-20261110151933",
|
|
32
|
+
"@pindoba/normalize-props": "0.0.0-alpha-20261107033529",
|
|
33
|
+
"@pindoba/styled-system": "0.0.0-alpha-20261110151933",
|
|
34
|
+
"@pindoba/styles-code": "0.0.0-alpha-20261110151933",
|
|
35
|
+
"@pindoba/styles-panel": "0.0.0-alpha-20261110151933"
|
|
28
36
|
},
|
|
29
37
|
"devDependencies": {
|
|
38
|
+
"@pindoba/build-config": "0.0.0",
|
|
30
39
|
"@pindoba/typescript-config": "0.0.0-alpha-20260320034942",
|
|
31
|
-
"tsdown": "^0.
|
|
40
|
+
"tsdown": "^0.23.0",
|
|
32
41
|
"typescript": "^7.0.2"
|
|
33
42
|
},
|
|
34
43
|
"peerDependencies": {
|
package/dist/src/types.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","names":[],"sources":["../../src/types.ts"],"mappings":";;;;;;KAaY,oBAAoB,QAAQ;KAE5B,WAAW,YAAY;KACvB,eAAe,YAAY;;;;;;;;;;;;;;;UAkBtB;EACf;;EAEA;;EAEA;;EAEA;;;UAIe;EACf,QAAQ;;EAER;;UAKe,oBAAoB,IAAI;EACvC,QAAQ;EACR,QAAQ;;KAGE,gBAAgB,wBAAwB,wBACjD,KAAK,aAAa,oBACjB,mBAAmB,wBAAwB;UAM9B,cAAc,wBAAwB,2BAM7C,KAAK,oBAAoB;;;;;;;EAOjC,QAAQ;;;;;;EAMR;;;;;;EAMA;;;;;;;;EAQA,OAAO;;;;;;EAMP;;;;;;EAMA;;;;;;;EAOA;;;;;EAKA;;;;;EAKA;;;;;;EAMA;;;;;;;EAOA;;;;;;EAMA;;;;;;;EAOA;;;;;;EAMA;;;;;EAKA,cAAc,gBAAgB;;UAKf,2BAA2B,QAC1C,cAAc;EAEd;GACC;;;UAIc;EACf;EACA;EACA,YAAY;;;UAIG;EACf;;EAEA;EACA;EACA,WAAW;EACX,iBAAiB;EACjB,kBAAkB;EAClB,cAAc;EACd,QAAQ;;KAGE,UAAU,cAAc;;EAElC,WAAW;;EAEX,SAAS;;EAET,OAAO;;EAEP;;EAEA;;EAEA;;;;;;EAMA;;EAEA"}
|
package/dist/src/types.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|