@pindoba/core-code 0.0.0-alpha-20260829223243 → 0.0.0-alpha-20261008173743

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 CHANGED
@@ -1,3 +1,3 @@
1
- import { CodeApi, CodeBaseProps, CodeFeedback, CodeLine, CodeLineItem, CodePassThrough, CodeSize, CodeSlotPassThrough, CodeSlots, CodeToken, CodeTokenItem, ConnectCodeOptions, ElementAttributes, PanelElement, PanelRootProps } from "./src/types.js";
2
- import { codeDefaults, connectCode } from "./src/connect-code.js";
3
- export { type CodeApi, type CodeBaseProps, type CodeFeedback, type CodeLine, type CodeLineItem, type CodePassThrough, type CodeSize, type CodeSlotPassThrough, type CodeSlots, type CodeToken, type CodeTokenItem, type ConnectCodeOptions, type ElementAttributes, type PanelElement, type PanelRootProps, codeDefaults, connectCode };
1
+ import { CodeApi, CodeBaseProps, CodeCardChrome, CodeCardDefaults, CodeCardFrameNodes, CodeCardProps, CodeFeedback, CodeLine, CodeLineItem, CodePassThrough, CodeResolvedCardFrame, CodeSize, CodeSlotPassThrough, CodeSlots, CodeToken, CodeTokenItem, ConnectCodeOptions, ElementAttributes, PanelElement, PanelRootProps } from "./src/types.js";
2
+ import { codeDefaults, connectCode, resolveCodeCardFrame } from "./src/connect-code.js";
3
+ export { type CodeApi, type CodeBaseProps, type CodeCardChrome, type CodeCardDefaults, type CodeCardFrameNodes, type CodeCardProps, type CodeFeedback, type CodeLine, type CodeLineItem, type CodePassThrough, type CodeResolvedCardFrame, type CodeSize, type CodeSlotPassThrough, type CodeSlots, type CodeToken, type CodeTokenItem, type ConnectCodeOptions, type ElementAttributes, type PanelElement, type PanelRootProps, codeDefaults, connectCode, resolveCodeCardFrame };
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import { codeDefaults, connectCode } from "./src/connect-code.js";
2
- export { codeDefaults, connectCode };
1
+ import { codeDefaults, connectCode, resolveCodeCardFrame } from "./src/connect-code.js";
2
+ export { codeDefaults, connectCode, resolveCodeCardFrame };
@@ -1,4 +1,5 @@
1
- import { CodeApi, ConnectCodeOptions } from "./types.js";
1
+ import { CodeApi, CodeCardChrome, CodeCardFrameNodes, CodeResolvedCardFrame, ConnectCodeOptions } from "./types.js";
2
+ import { CardFrameProps } from "@pindoba/core-card";
2
3
  //#region src/connect-code.d.ts
3
4
  declare const codeDefaults: {
4
5
  readonly size: "sm";
@@ -27,6 +28,22 @@ declare const codeDefaults: {
27
28
  * rather than empty.
28
29
  */
29
30
  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
+ declare function resolveCodeCardFrame<TSlot>(card: CardFrameProps<TSlot> | false | undefined, chrome: CodeCardChrome, nodes?: CodeCardFrameNodes<TSlot>): CodeResolvedCardFrame<TSlot>;
30
47
  //#endregion
31
- export { codeDefaults, connectCode };
48
+ export { codeDefaults, connectCode, resolveCodeCardFrame };
32
49
  //# sourceMappingURL=connect-code.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"connect-code.d.ts","names":[],"sources":["../../src/connect-code.ts"],"mappings":";;cAaa;;;;;;;;;;;;;;;;;;;;;;;;;;iBAyDG,YAAY,UAAS,qBAA0B"}
1
+ {"version":3,"file":"connect-code.d.ts","names":[],"sources":["../../src/connect-code.ts"],"mappings":";;;cAwBa;;;;;;;;;;;;;;;;;;;;;;;;;;iBAyDG,YAAY,UAAS,qBAA0B;;;;;;;;;;;;;;;;iBAmZ/C,qBAAqB,OACnC,MAAM,eAAe,4BACrB,QAAQ,gBACR,QAAO,mBAAmB,SACzB,sBAAsB"}
@@ -1,5 +1,6 @@
1
1
  import { css, cx } from "@pindoba/styled-system/css";
2
2
  import { connectPanel } from "@pindoba/core-panel";
3
+ import { resolveCardContentSurface, resolveCardFrame } from "@pindoba/core-card";
3
4
  import { defineConnect } from "@pindoba/normalize-props";
4
5
  import { codeStyles } from "@pindoba/styles-code";
5
6
  //#region src/connect-code.ts
@@ -42,15 +43,27 @@ function sourceFromLines(lines) {
42
43
  * rather than empty.
43
44
  */
44
45
  function connectCode(options = {}) {
45
- const { lines, code = "", lang, size = codeDefaults.size, feedback = codeDefaults.feedback, 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, 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;
46
+ const { lines, code = "", lang, size = codeDefaults.size, feedback = codeDefaults.feedback, 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;
46
47
  const resolvedLines = lines ?? (code ? linesFromSource(code) : []);
47
48
  const lineCount = resolvedLines.length;
48
49
  const overflowable = maxLines != null && maxLines > 0 && lineCount > maxLines;
49
50
  const clamped = overflowable && !expanded;
51
+ const framed = card != null && card !== false;
52
+ const frameInput = framed ? card : {};
53
+ const cardContent = {
54
+ padding: "none",
55
+ ...frameInput.content
56
+ };
57
+ const { inset, surface: contentSurface } = resolveCardContentSurface(cardContent, frameInput.contentBackground);
58
+ const surfaceBackground = inset ? contentSurface.background : frameInput.background ?? background;
50
59
  const classes = codeStyles.raw({
51
60
  size,
52
61
  wrap,
53
- showLineNumbers
62
+ showLineNumbers,
63
+ ...framed ? {
64
+ framed: true,
65
+ fadeSurface: surfaceBackground
66
+ } : null
54
67
  });
55
68
  const highlighted = new Set(highlightLines ?? []);
56
69
  const items = resolvedLines.map((line, index) => {
@@ -85,7 +98,7 @@ function connectCode(options = {}) {
85
98
  };
86
99
  });
87
100
  const copyText = lines ? sourceFromLines(lines) : code;
88
- const showToolbar = Boolean(lang) || copyable;
101
+ const showToolbar = Boolean(lang) || copyable || Boolean(title);
89
102
  const showExpand = Boolean(collapsible && overflowable);
90
103
  const panelApi = connectPanel({
91
104
  as,
@@ -123,6 +136,52 @@ function connectCode(options = {}) {
123
136
  class: className,
124
137
  ...rest
125
138
  });
139
+ const rootId = "id" in rest ? rest.id : void 0;
140
+ const cardPassThrough = passThrough?.card ?? {};
141
+ const invariantContentProps = {
142
+ "data-code-body": "",
143
+ "data-lang": lang ?? "",
144
+ "data-clamped": clamped ? "true" : "false",
145
+ ...maxLines != null ? { style: `--code-max-lines:${maxLines}` } : {}
146
+ };
147
+ const cardDefaults = {
148
+ size,
149
+ background,
150
+ feedback,
151
+ emphasis,
152
+ ...translucent !== void 0 ? { translucent } : null,
153
+ radius,
154
+ border,
155
+ shadow,
156
+ ...inset ? null : { divided: true },
157
+ content: { padding: "none" }
158
+ };
159
+ const cardChrome = {
160
+ heading: title || void 0,
161
+ eyebrow: lang || void 0,
162
+ headingId: typeof rootId === "string" && rootId !== "" ? `${rootId}-heading` : void 0,
163
+ defaults: cardDefaults,
164
+ rootProps: {
165
+ "data-card-flushable": "",
166
+ ...rest,
167
+ ...passThrough?.root?.props
168
+ },
169
+ invariantRootProps: { "data-card-kind": "code" },
170
+ invariantContentProps,
171
+ passThrough: {
172
+ ...cardPassThrough,
173
+ ...passThrough?.root?.style || cardPassThrough.root ? { root: {
174
+ ...cardPassThrough.root,
175
+ style: css.raw(passThrough?.root?.style, cardPassThrough.root?.style)
176
+ } } : null,
177
+ content: {
178
+ ...cardPassThrough.content,
179
+ style: css.raw(classes.frameContent, cardPassThrough.content?.style)
180
+ }
181
+ },
182
+ class: className != null ? String(className) : void 0
183
+ };
184
+ const { card: _cardPassThrough, ...slotPassThrough } = passThrough ?? {};
126
185
  return {
127
186
  ...defineConnect({
128
187
  slots: {
@@ -131,6 +190,10 @@ function connectCode(options = {}) {
131
190
  "data-code-toolbar": "",
132
191
  class: css(classes.toolbar, passThrough?.toolbar?.style)
133
192
  },
193
+ title: {
194
+ "data-code-title": "",
195
+ class: css(classes.title, passThrough?.title?.style)
196
+ },
134
197
  language: {
135
198
  "data-code-language": "",
136
199
  class: css(classes.language, passThrough?.language?.style)
@@ -177,13 +240,16 @@ function connectCode(options = {}) {
177
240
  size: "xs",
178
241
  "aria-expanded": expanded ? "true" : "false",
179
242
  class: css(classes.expandButton, passThrough?.expandButton?.style)
180
- }
243
+ },
244
+ frameContent: { class: css(classes.frameContent) }
181
245
  },
182
246
  passThrough: {
183
- ...passThrough,
247
+ ...slotPassThrough,
184
248
  root: void 0
185
249
  }
186
250
  }),
251
+ frame: framed ? "card" : "bare",
252
+ cardChrome,
187
253
  rootProps: panelApi.rootProps,
188
254
  element: panelApi.element,
189
255
  items,
@@ -194,7 +260,91 @@ function connectCode(options = {}) {
194
260
  lineCount
195
261
  };
196
262
  }
263
+ /** Merge one card slot's passThrough: `over` wins (styles merged, props spread). */
264
+ function mergeCardSlot(base, over) {
265
+ if (!base) return over;
266
+ if (!over) return base;
267
+ return {
268
+ style: css.raw(base.style, over.style),
269
+ props: {
270
+ ...base.props,
271
+ ...over.props
272
+ }
273
+ };
274
+ }
275
+ function slotOf(bag, key) {
276
+ if (!bag) return void 0;
277
+ const entry = Reflect.get(bag, key);
278
+ return typeof entry === "object" && entry !== null ? entry : void 0;
279
+ }
280
+ /** Merge two card passThrough bags slot by slot (`over` wins). */
281
+ function mergeCardPassThrough(base, over) {
282
+ const merged = {};
283
+ const keys = /* @__PURE__ */ new Set([...Object.keys(base), ...Object.keys(over ?? {})]);
284
+ for (const key of keys) merged[key] = mergeCardSlot(slotOf(base, key), slotOf(over, key));
285
+ return merged;
286
+ }
287
+ /**
288
+ * Resolve a framed block's Card: the consumer's `card` prop over the block's
289
+ * own defaults (`api.cardChrome`), with the framework's chrome appended AFTER
290
+ * the consumer's slots — the copy button into the header's trailing slot, the
291
+ * expand button into the footer's trailing slot. The same call in all four
292
+ * frameworks; they differ only in their slot type (Svelte snippets, React /
293
+ * Vue nodes; Astro fills `chrome-*` slots and passes none).
294
+ *
295
+ * - `card.heading` / `card.eyebrow` win over `title` / `lang`.
296
+ * - `card.content` merges over the block's `padding: "none"` default, so
297
+ * `{ contentBackground }` keeps the code edge to edge on an inset panel.
298
+ * - `card.passThrough` merges over `passThrough.card` and the frame styles.
299
+ * - `data-card-kind` (root) and the clamp markers (content) are re-applied
300
+ * last: nothing overrides them.
301
+ */
302
+ function resolveCodeCardFrame(card, chrome, nodes = {}) {
303
+ const frame = card || {};
304
+ const eyebrow = frame.eyebrow ?? chrome.eyebrow;
305
+ const resolved = resolveCardFrame({
306
+ ...frame,
307
+ ...eyebrow !== void 0 ? { eyebrow } : null,
308
+ content: {
309
+ ...chrome.defaults.content,
310
+ ...frame.content
311
+ },
312
+ passThrough: mergeCardPassThrough(chrome.passThrough, frame.passThrough)
313
+ }, {
314
+ heading: chrome.heading,
315
+ headingId: chrome.headingId,
316
+ defaults: chrome.defaults,
317
+ rootProps: chrome.rootProps,
318
+ trailing: nodes.copy,
319
+ footerTrailing: nodes.expand
320
+ });
321
+ const passThrough = resolved.cardProps.passThrough;
322
+ return {
323
+ ...resolved,
324
+ cardProps: {
325
+ ...resolved.cardProps,
326
+ passThrough: {
327
+ ...passThrough,
328
+ root: {
329
+ ...passThrough.root,
330
+ props: {
331
+ ...passThrough.root?.props,
332
+ ...chrome.invariantRootProps
333
+ }
334
+ },
335
+ content: {
336
+ ...passThrough.content,
337
+ props: {
338
+ ...passThrough.content?.props,
339
+ ...chrome.invariantContentProps
340
+ }
341
+ }
342
+ },
343
+ ...chrome.class !== void 0 ? { class: chrome.class } : null
344
+ }
345
+ };
346
+ }
197
347
  //#endregion
198
- export { codeDefaults, connectCode };
348
+ export { codeDefaults, connectCode, resolveCodeCardFrame };
199
349
 
200
350
  //# 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 {\n resolveCardContentSurface,\n resolveCardFrame,\n type CardFrameProps,\n type CardPassThrough,\n type CardSlotPassThrough,\n} from \"@pindoba/core-card\";\nimport { defineConnect } 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 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 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 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`. `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 // --- 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 feedback,\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 },\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: className != null ? String(className) : undefined,\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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"}
@@ -1,4 +1,5 @@
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";
@@ -40,8 +41,22 @@ interface CodeSlotPassThrough<T = ElementAttributes> {
40
41
  style?: SystemStyleObject;
41
42
  props?: T;
42
43
  }
43
- type CodePassThrough<RootElementAttributes = ElementAttributes> = { [K in CodeSlots]?: CodeSlotPassThrough<K extends "root" ? RootElementAttributes : ElementAttributes>; };
44
- interface CodeBaseProps<RootElementAttributes = ElementAttributes> extends Omit<PanelInheritedProps<RootElementAttributes>, "passThrough"> {
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. Purely presentational — the component
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,7 +188,59 @@ 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" | "feedback" | "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
245
  rootProps: PanelRootProps$1;
164
246
  /** Resolved root tag from `as` — templates render this element. */
@@ -181,5 +263,5 @@ type CodeApi = ConnectReturn<CodeSlots> & {
181
263
  lineCount: number;
182
264
  };
183
265
  //#endregion
184
- export { CodeApi, CodeBaseProps, CodeFeedback, CodeLine, CodeLineItem, CodePassThrough, CodeSize, CodeSlotPassThrough, type CodeSlots, CodeToken, CodeTokenItem, ConnectCodeOptions, ElementAttributes, type PanelElement, type PanelRootProps };
266
+ export { CodeApi, CodeBaseProps, CodeCardChrome, CodeCardDefaults, CodeCardFrameNodes, CodeCardProps, CodeFeedback, CodeLine, CodeLineItem, CodePassThrough, CodeResolvedCardFrame, CodeSize, CodeSlotPassThrough, type CodeSlots, CodeToken, CodeTokenItem, ConnectCodeOptions, ElementAttributes, type PanelElement, type PanelRootProps };
185
267
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
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"}
1
+ {"version":3,"file":"types.d.ts","names":[],"sources":["../../src/types.ts"],"mappings":";;;;;;;KAmBY,oBAAoB,QAAQ;KAE5B,WAAW,YAAY;KACvB,eAAe,YAAY;;;;;;;;;;;;;;;UAkBtB;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-20260829223243",
3
+ "version": "0.0.0-alpha-20261008173743",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "sideEffects": false,
@@ -17,14 +17,16 @@
17
17
  }
18
18
  },
19
19
  "files": [
20
- "dist"
20
+ "dist",
21
+ "!dist/**/__test__"
21
22
  ],
22
23
  "dependencies": {
23
- "@pindoba/core-panel": "0.0.0-alpha-20260829223243",
24
- "@pindoba/normalize-props": "0.0.0-alpha-20260829223243",
25
- "@pindoba/styled-system": "0.0.0-alpha-20260829223243",
26
- "@pindoba/styles-code": "0.0.0-alpha-20260829223243",
27
- "@pindoba/styles-panel": "0.0.0-alpha-20260829223243"
24
+ "@pindoba/core-card": "0.0.0-alpha-20261008173743",
25
+ "@pindoba/core-panel": "0.0.0-alpha-20261008173743",
26
+ "@pindoba/normalize-props": "0.0.0-alpha-20261008173743",
27
+ "@pindoba/styled-system": "0.0.0-alpha-20261008173743",
28
+ "@pindoba/styles-code": "0.0.0-alpha-20261008173743",
29
+ "@pindoba/styles-panel": "0.0.0-alpha-20261008173743"
28
30
  },
29
31
  "devDependencies": {
30
32
  "@pindoba/typescript-config": "0.0.0-alpha-20260320034942",