@shapesos/clay 0.10.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/artifacts.cjs +741 -0
- package/dist/artifacts.cjs.map +1 -0
- package/dist/artifacts.d.cts +56 -0
- package/dist/artifacts.d.ts +56 -0
- package/dist/artifacts.js +23 -0
- package/dist/blocks.cjs +685 -53
- package/dist/blocks.cjs.map +1 -1
- package/dist/blocks.css +2 -0
- package/dist/blocks.d.cts +22 -10
- package/dist/blocks.d.ts +22 -10
- package/dist/blocks.js +15 -5
- package/dist/button.d.cts +2 -2
- package/dist/button.d.ts +2 -2
- package/dist/chat.cjs +728 -296
- package/dist/chat.cjs.map +1 -1
- package/dist/chat.d.cts +27 -10
- package/dist/chat.d.ts +27 -10
- package/dist/chat.js +7 -3
- package/dist/chunk-BX5TCEPR.js +436 -0
- package/dist/chunk-BX5TCEPR.js.map +1 -0
- package/dist/chunk-JGMN6W72.js +12 -0
- package/dist/chunk-JGMN6W72.js.map +1 -0
- package/dist/{chunk-27GJUWVN.js → chunk-JJUIBBBU.js} +14 -8
- package/dist/chunk-JJUIBBBU.js.map +1 -0
- package/dist/chunk-L35M3OD5.js +99 -0
- package/dist/chunk-L35M3OD5.js.map +1 -0
- package/dist/chunk-MEJESPTZ.js +1 -0
- package/dist/chunk-MEJESPTZ.js.map +1 -0
- package/dist/{chunk-R3BGPOAM.js → chunk-MXOPG747.js} +53 -45
- package/dist/chunk-MXOPG747.js.map +1 -0
- package/dist/{chunk-MLCRDVQ2.js → chunk-OBOXCBDL.js} +13 -5
- package/dist/chunk-OBOXCBDL.js.map +1 -0
- package/dist/{chunk-WS4IPADR.js → chunk-OUW6PUEB.js} +42 -110
- package/dist/chunk-OUW6PUEB.js.map +1 -0
- package/dist/icon.cjs +12 -4
- package/dist/icon.cjs.map +1 -1
- package/dist/icon.d.cts +19 -6
- package/dist/icon.d.ts +19 -6
- package/dist/icon.js +1 -1
- package/dist/index.cjs +794 -332
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -4
- package/dist/index.d.ts +6 -4
- package/dist/index.js +50 -25
- package/dist/lottie.d.cts +2 -2
- package/dist/lottie.d.ts +2 -2
- package/dist/table.cjs +142 -0
- package/dist/table.cjs.map +1 -0
- package/dist/table.d.cts +44 -0
- package/dist/table.d.ts +44 -0
- package/dist/table.js +24 -0
- package/dist/table.js.map +1 -0
- package/dist/text-area.d.cts +2 -2
- package/dist/text-area.d.ts +2 -2
- package/dist/types-3Gzk7cRt.d.cts +121 -0
- package/dist/types-3Gzk7cRt.d.ts +121 -0
- package/dist/types-C9XX-Uhk.d.ts +68 -0
- package/dist/types-DuuRI4ll.d.cts +68 -0
- package/dist/utils.cjs +14 -7
- package/dist/utils.cjs.map +1 -1
- package/dist/utils.d.cts +18 -1
- package/dist/utils.d.ts +18 -1
- package/dist/utils.js +6 -4
- package/package.json +29 -4
- package/dist/chunk-27GJUWVN.js.map +0 -1
- package/dist/chunk-MLCRDVQ2.js.map +0 -1
- package/dist/chunk-OKPNST44.js +0 -1
- package/dist/chunk-R3BGPOAM.js.map +0 -1
- package/dist/chunk-WS4IPADR.js.map +0 -1
- package/dist/types-Q9aqd9nq.d.cts +0 -34
- package/dist/types-Q9aqd9nq.d.ts +0 -34
- /package/dist/{chunk-OKPNST44.js.map → artifacts.js.map} +0 -0
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { ComponentType } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Artifact-domain types — wire-protocol shapes shared between artifact components, block-services, and
|
|
5
|
+
* consumers. The block layer (`@/components/blocks`) imports these to define
|
|
6
|
+
* `ArtifactRefBlockRecord`; the artifact components consume them directly.
|
|
7
|
+
*
|
|
8
|
+
* Mirrored from the agent / io-server contract — see the `chat-typed-content-blocks-and-artifacts`
|
|
9
|
+
* plan, §6.4 (table contract).
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Artifact-type discriminator constants. Uppercase to match the server-side `artifactTypes`
|
|
14
|
+
* constant — io-server stores `artifact.type === "TABLE"` (aligned with the GraphQL enum).
|
|
15
|
+
*/
|
|
16
|
+
declare const artifactTypes: {
|
|
17
|
+
readonly TABLE: "TABLE";
|
|
18
|
+
};
|
|
19
|
+
/** Discriminator for artifact types. Additive — new types extend this union. */
|
|
20
|
+
type ArtifactType = (typeof artifactTypes)[keyof typeof artifactTypes];
|
|
21
|
+
/**
|
|
22
|
+
* A single column descriptor for a `TableArtifact`. Carries the ORDER and LABEL for the
|
|
23
|
+
* rendered table — `key` selects a column from the CSV header; `label` overrides the rendered
|
|
24
|
+
* header text. The CSV is always the source of truth for the data shape; clay never coerces,
|
|
25
|
+
* reformats, or interprets cell values.
|
|
26
|
+
*/
|
|
27
|
+
interface TableColumn {
|
|
28
|
+
/** Matches a header in the CSV. Required. Used to project + order the CSV's columns. */
|
|
29
|
+
key: string;
|
|
30
|
+
/** Human-readable header text. Required. */
|
|
31
|
+
label: string;
|
|
32
|
+
/**
|
|
33
|
+
* Accepted in the wire payload but not consulted by the renderer — every cell is rendered as
|
|
34
|
+
* the raw CSV string (or empty for null). Format conversion is a server concern; if a column
|
|
35
|
+
* needs to be "Yes/No" instead of "true/false", the server should emit it that way in the CSV.
|
|
36
|
+
* Kept in the interface so callers that pass `type` (e.g. io-client) don't get a type error.
|
|
37
|
+
*/
|
|
38
|
+
type?: "string" | "number" | "date" | "boolean";
|
|
39
|
+
/**
|
|
40
|
+
* Accepted in the wire payload but not consulted by the renderer — every column is left-aligned
|
|
41
|
+
* by design. Kept in the interface for forward-compat; restore per-column alignment by editing
|
|
42
|
+
* `table-artifact-content.tsx` if a future use case justifies it.
|
|
43
|
+
*/
|
|
44
|
+
align?: "left" | "center" | "right";
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Optional stats embedded in `TableConfig` so clients can pre-render UI before fetching the
|
|
48
|
+
* actual data payload (e.g. N loading shimmers sized to the eventual row count).
|
|
49
|
+
*
|
|
50
|
+
* Backward-compat: optional everywhere — older artifacts persisted before this field shipped
|
|
51
|
+
* still validate. The renderer falls back to a single status-message line when `stats` is absent.
|
|
52
|
+
*/
|
|
53
|
+
interface TableStats {
|
|
54
|
+
/** Total number of rows in the materialised CSV. Lets the loading branch render shimmers. */
|
|
55
|
+
rowCount: number;
|
|
56
|
+
}
|
|
57
|
+
/** Rendering config for a table artifact, stored at `artifact.config` (JSONB on the server). */
|
|
58
|
+
interface TableConfig {
|
|
59
|
+
/** Column descriptors in render order. */
|
|
60
|
+
columns: TableColumn[];
|
|
61
|
+
/** Optional metadata describing the underlying data — used for loading-state UI. */
|
|
62
|
+
stats?: TableStats;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* A row inside a table artifact's parsed CSV. Every cell is the raw string from the CSV (or
|
|
66
|
+
* `null` for the empty string). The renderer never coerces or interprets — type-aware formatting
|
|
67
|
+
* is a server concern (the CSV's contents are what the user sees).
|
|
68
|
+
*/
|
|
69
|
+
type TableRow = Record<string, string | null>;
|
|
70
|
+
/** Common shape every artifact record carries. Discriminated by `type`. */
|
|
71
|
+
interface ArtifactBase<TType extends ArtifactType, TConfig> {
|
|
72
|
+
/** Artifact-type discriminator. */
|
|
73
|
+
type: TType;
|
|
74
|
+
/** Human-readable label. Nullable when the artifact has no title. */
|
|
75
|
+
title: string | null;
|
|
76
|
+
/** Rendering config — shape varies per artifact type. */
|
|
77
|
+
config: TConfig;
|
|
78
|
+
/** Protected-asset metadata pointing to the underlying data payload. Null while the asset is unavailable. */
|
|
79
|
+
protectedAsset: {
|
|
80
|
+
/** Time-limited URL the renderer fetches the artifact's data from. */
|
|
81
|
+
presignedUrl: string;
|
|
82
|
+
} | null;
|
|
83
|
+
}
|
|
84
|
+
/** Concrete table artifact record. Future artifact types extend `ArtifactRecord` here. */
|
|
85
|
+
type TableArtifactRecord = ArtifactBase<typeof artifactTypes.TABLE, TableConfig>;
|
|
86
|
+
/**
|
|
87
|
+
* Discriminated union of all supported artifacts. Single-member today; extension pattern:
|
|
88
|
+
*
|
|
89
|
+
* export type ArtifactRecord = TableArtifactRecord | ChartArtifactRecord;
|
|
90
|
+
*
|
|
91
|
+
* Every consumer that narrows on `artifact.type` must add the new branch here AND in the
|
|
92
|
+
* `ArtifactServices` map (single source of truth for the type→component mapping). Older clay
|
|
93
|
+
* versions that don't know about a new type silently render nothing — see `ArtifactRefBlock` guard.
|
|
94
|
+
*/
|
|
95
|
+
type ArtifactRecord = TableArtifactRecord;
|
|
96
|
+
/** Props every concrete artifact component receives from the dispatcher. */
|
|
97
|
+
interface ArtifactServiceProps<TArtifact extends ArtifactRecord = ArtifactRecord> {
|
|
98
|
+
/** The artifact metadata (config + presigned URL). */
|
|
99
|
+
artifact: TArtifact;
|
|
100
|
+
}
|
|
101
|
+
/** Per-artifact-type service — registry entry that pairs a discriminator with its concrete component. */
|
|
102
|
+
interface ArtifactService<TArtifact extends ArtifactRecord = ArtifactRecord> {
|
|
103
|
+
/** Discriminator value this service handles. */
|
|
104
|
+
type: ArtifactType;
|
|
105
|
+
/** React component that renders an artifact of this type — composes ai-elements primitives
|
|
106
|
+
* (`Artifact`, `ArtifactHeader`, `ArtifactTitle`, `ArtifactActions`, `ArtifactContent`) directly. */
|
|
107
|
+
Component: ComponentType<ArtifactServiceProps<TArtifact>>;
|
|
108
|
+
/**
|
|
109
|
+
* Returns a plain-text / markdown string suitable for clipboard copy.
|
|
110
|
+
*
|
|
111
|
+
* Called by `ArtifactRefBlockService.toClipboardText` when the user copies a message that
|
|
112
|
+
* contains this artifact. Must be pure and synchronous.
|
|
113
|
+
*
|
|
114
|
+
* Implementation notes per type:
|
|
115
|
+
* - `table`: returns the artifact title as plain text (see `TableArtifactService`).
|
|
116
|
+
* - Future types implement their own representation in a co-located `*-service.ts` file.
|
|
117
|
+
*/
|
|
118
|
+
toClipboardText: (artifact: TArtifact) => string;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export { type ArtifactRecord as A, type TableArtifactRecord as T, type ArtifactService as a, type ArtifactServiceProps as b, type ArtifactType as c, type TableColumn as d, type TableConfig as e, type TableRow as f, type TableStats as g, artifactTypes as h };
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { ComponentType } from 'react';
|
|
2
|
+
import { A as ArtifactRecord } from './types-3Gzk7cRt.js';
|
|
3
|
+
|
|
4
|
+
/** Block-type discriminator constants. Mirrors the wire shape from io-server / shapes-agent. */
|
|
5
|
+
declare const blockTypes: {
|
|
6
|
+
readonly TEXT: "TEXT";
|
|
7
|
+
readonly ARTIFACT_REF: "ARTIFACT_REF";
|
|
8
|
+
};
|
|
9
|
+
/** Discriminator for content blocks. */
|
|
10
|
+
type BlockType = (typeof blockTypes)[keyof typeof blockTypes];
|
|
11
|
+
/** Plain-text content block — rendered as Markdown. */
|
|
12
|
+
interface TextBlockRecord {
|
|
13
|
+
/** Block-type discriminator. */
|
|
14
|
+
type: typeof blockTypes.TEXT;
|
|
15
|
+
/** Type-specific data; kept as `payload` to match the wire shape 1:1. */
|
|
16
|
+
payload: {
|
|
17
|
+
/** The Markdown source for this text segment. */
|
|
18
|
+
text: string;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Reference-to-an-artifact content block — dispatched to the per-type artifact component.
|
|
23
|
+
*
|
|
24
|
+
* The artifact-domain types (`ArtifactRecord`, `TableArtifactRecord`, etc.) live in
|
|
25
|
+
* `@/components/artifacts/types` so the artifact layer can be consumed independently of the
|
|
26
|
+
* block layer (e.g. by a future surface that renders an artifact outside chat). The block layer
|
|
27
|
+
* imports them here to wrap an artifact in an `ARTIFACT_REF` block.
|
|
28
|
+
*/
|
|
29
|
+
interface ArtifactRefBlockRecord {
|
|
30
|
+
/** Block-type discriminator. */
|
|
31
|
+
type: typeof blockTypes.ARTIFACT_REF;
|
|
32
|
+
/** Type-specific data; kept as `payload` to match the wire shape 1:1. */
|
|
33
|
+
payload: {
|
|
34
|
+
/** The artifact's stable identifier, used for keying / future cross-message reuse. */
|
|
35
|
+
artifactId: string;
|
|
36
|
+
/** Inlined artifact metadata — clients fetch the underlying data via `artifact.protectedAsset.presignedUrl`. */
|
|
37
|
+
artifact: ArtifactRecord;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/** Discriminated union of all supported content blocks. Additive — new types extend this union. */
|
|
41
|
+
type BlockRecord = TextBlockRecord | ArtifactRefBlockRecord;
|
|
42
|
+
/** Props every concrete block component receives from the dispatcher. */
|
|
43
|
+
interface BlockComponentProps<TBlock extends BlockRecord = BlockRecord> {
|
|
44
|
+
/** The block to render. */
|
|
45
|
+
block: TBlock;
|
|
46
|
+
}
|
|
47
|
+
/** Per-block-type service — registry entry that pairs a discriminator with its concrete component. */
|
|
48
|
+
interface BlockService<TBlock extends BlockRecord = BlockRecord> {
|
|
49
|
+
/** Discriminator value this service handles. */
|
|
50
|
+
type: BlockType;
|
|
51
|
+
/** React component that renders a block of this type. */
|
|
52
|
+
Component: ComponentType<BlockComponentProps<TBlock>>;
|
|
53
|
+
/**
|
|
54
|
+
* Returns a plain-text or markdown string representation of this block suitable for clipboard
|
|
55
|
+
* copy. Called by `copyMessageText` when the user clicks "copy message".
|
|
56
|
+
*
|
|
57
|
+
* - `TextBlockService`: returns `block.payload.text` verbatim (markdown preserved).
|
|
58
|
+
* - `ArtifactRefBlockService`: delegates to `ArtifactServices[artifact.type].toClipboardText`.
|
|
59
|
+
* Falls back to `[Artifact: <title>]` when the artifact type has no registered component.
|
|
60
|
+
*
|
|
61
|
+
* Optional so external `BlockService` implementors don't break on upgrade — when omitted, the
|
|
62
|
+
* aggregator treats the block as contributing the empty string (silently skipped). Implementations
|
|
63
|
+
* must be pure, synchronous functions — no async, no hooks, no side effects.
|
|
64
|
+
*/
|
|
65
|
+
toClipboardText?: (block: TBlock) => string;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export { type ArtifactRefBlockRecord as A, type BlockComponentProps as B, type TextBlockRecord as T, type BlockRecord as a, type BlockService as b, type BlockType as c, blockTypes as d };
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { ComponentType } from 'react';
|
|
2
|
+
import { A as ArtifactRecord } from './types-3Gzk7cRt.cjs';
|
|
3
|
+
|
|
4
|
+
/** Block-type discriminator constants. Mirrors the wire shape from io-server / shapes-agent. */
|
|
5
|
+
declare const blockTypes: {
|
|
6
|
+
readonly TEXT: "TEXT";
|
|
7
|
+
readonly ARTIFACT_REF: "ARTIFACT_REF";
|
|
8
|
+
};
|
|
9
|
+
/** Discriminator for content blocks. */
|
|
10
|
+
type BlockType = (typeof blockTypes)[keyof typeof blockTypes];
|
|
11
|
+
/** Plain-text content block — rendered as Markdown. */
|
|
12
|
+
interface TextBlockRecord {
|
|
13
|
+
/** Block-type discriminator. */
|
|
14
|
+
type: typeof blockTypes.TEXT;
|
|
15
|
+
/** Type-specific data; kept as `payload` to match the wire shape 1:1. */
|
|
16
|
+
payload: {
|
|
17
|
+
/** The Markdown source for this text segment. */
|
|
18
|
+
text: string;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Reference-to-an-artifact content block — dispatched to the per-type artifact component.
|
|
23
|
+
*
|
|
24
|
+
* The artifact-domain types (`ArtifactRecord`, `TableArtifactRecord`, etc.) live in
|
|
25
|
+
* `@/components/artifacts/types` so the artifact layer can be consumed independently of the
|
|
26
|
+
* block layer (e.g. by a future surface that renders an artifact outside chat). The block layer
|
|
27
|
+
* imports them here to wrap an artifact in an `ARTIFACT_REF` block.
|
|
28
|
+
*/
|
|
29
|
+
interface ArtifactRefBlockRecord {
|
|
30
|
+
/** Block-type discriminator. */
|
|
31
|
+
type: typeof blockTypes.ARTIFACT_REF;
|
|
32
|
+
/** Type-specific data; kept as `payload` to match the wire shape 1:1. */
|
|
33
|
+
payload: {
|
|
34
|
+
/** The artifact's stable identifier, used for keying / future cross-message reuse. */
|
|
35
|
+
artifactId: string;
|
|
36
|
+
/** Inlined artifact metadata — clients fetch the underlying data via `artifact.protectedAsset.presignedUrl`. */
|
|
37
|
+
artifact: ArtifactRecord;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/** Discriminated union of all supported content blocks. Additive — new types extend this union. */
|
|
41
|
+
type BlockRecord = TextBlockRecord | ArtifactRefBlockRecord;
|
|
42
|
+
/** Props every concrete block component receives from the dispatcher. */
|
|
43
|
+
interface BlockComponentProps<TBlock extends BlockRecord = BlockRecord> {
|
|
44
|
+
/** The block to render. */
|
|
45
|
+
block: TBlock;
|
|
46
|
+
}
|
|
47
|
+
/** Per-block-type service — registry entry that pairs a discriminator with its concrete component. */
|
|
48
|
+
interface BlockService<TBlock extends BlockRecord = BlockRecord> {
|
|
49
|
+
/** Discriminator value this service handles. */
|
|
50
|
+
type: BlockType;
|
|
51
|
+
/** React component that renders a block of this type. */
|
|
52
|
+
Component: ComponentType<BlockComponentProps<TBlock>>;
|
|
53
|
+
/**
|
|
54
|
+
* Returns a plain-text or markdown string representation of this block suitable for clipboard
|
|
55
|
+
* copy. Called by `copyMessageText` when the user clicks "copy message".
|
|
56
|
+
*
|
|
57
|
+
* - `TextBlockService`: returns `block.payload.text` verbatim (markdown preserved).
|
|
58
|
+
* - `ArtifactRefBlockService`: delegates to `ArtifactServices[artifact.type].toClipboardText`.
|
|
59
|
+
* Falls back to `[Artifact: <title>]` when the artifact type has no registered component.
|
|
60
|
+
*
|
|
61
|
+
* Optional so external `BlockService` implementors don't break on upgrade — when omitted, the
|
|
62
|
+
* aggregator treats the block as contributing the empty string (silently skipped). Implementations
|
|
63
|
+
* must be pure, synchronous functions — no async, no hooks, no side effects.
|
|
64
|
+
*/
|
|
65
|
+
toClipboardText?: (block: TBlock) => string;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export { type ArtifactRefBlockRecord as A, type BlockComponentProps as B, type TextBlockRecord as T, type BlockRecord as a, type BlockService as b, type BlockType as c, blockTypes as d };
|
package/dist/utils.cjs
CHANGED
|
@@ -21,6 +21,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var utils_exports = {};
|
|
22
22
|
__export(utils_exports, {
|
|
23
23
|
convertTableToCSV: () => convertTableToCSV,
|
|
24
|
+
escapeCsvField: () => escapeCsvField,
|
|
24
25
|
markdownToPlainText: () => markdownToPlainText,
|
|
25
26
|
mergeRefs: () => mergeRefs,
|
|
26
27
|
parseMarkdownTable: () => parseMarkdownTable
|
|
@@ -112,21 +113,27 @@ function parseMarkdownTable(markdown, tableIndex = 0) {
|
|
|
112
113
|
return null;
|
|
113
114
|
}
|
|
114
115
|
|
|
115
|
-
// src/utils/
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
116
|
+
// src/utils/escape-csv-field.ts
|
|
117
|
+
var FORMULA_TRIGGERS = /* @__PURE__ */ new Set(["=", "+", "@", " ", "\r"]);
|
|
118
|
+
function escapeCsvField(field) {
|
|
119
|
+
const needsNeutralise = field.length > 0 && FORMULA_TRIGGERS.has(field[0]);
|
|
120
|
+
const safeField = needsNeutralise ? ` ${field}` : field;
|
|
121
|
+
if (needsNeutralise || safeField.includes(",") || safeField.includes('"') || safeField.includes("\n") || safeField.includes("\r")) {
|
|
122
|
+
return `"${safeField.replace(/"/g, '""')}"`;
|
|
119
123
|
}
|
|
120
|
-
return
|
|
124
|
+
return safeField;
|
|
121
125
|
}
|
|
126
|
+
|
|
127
|
+
// src/utils/convert-table-to-csv.ts
|
|
122
128
|
function convertTableToCSV(table) {
|
|
123
|
-
const headerRow = table.columns.map(
|
|
124
|
-
const dataRows = table.rows.map((row) => row.map(
|
|
129
|
+
const headerRow = table.columns.map(escapeCsvField).join(",");
|
|
130
|
+
const dataRows = table.rows.map((row) => row.map(escapeCsvField).join(","));
|
|
125
131
|
return [headerRow, ...dataRows].join("\r\n");
|
|
126
132
|
}
|
|
127
133
|
// Annotate the CommonJS export names for ESM import in node:
|
|
128
134
|
0 && (module.exports = {
|
|
129
135
|
convertTableToCSV,
|
|
136
|
+
escapeCsvField,
|
|
130
137
|
markdownToPlainText,
|
|
131
138
|
mergeRefs,
|
|
132
139
|
parseMarkdownTable
|
package/dist/utils.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utils/index.ts","../src/utils/markdown-to-plain-text.ts","../src/utils/merge-refs.ts","../src/utils/parse-markdown-table.ts","../src/utils/convert-table-to-csv.ts"],"sourcesContent":["export { markdownToPlainText } from \"./markdown-to-plain-text\";\nexport { mergeRefs } from \"./merge-refs\";\nexport { parseMarkdownTable } from \"./parse-markdown-table\";\nexport type { MarkdownTable } from \"./parse-markdown-table\";\nexport { convertTableToCSV } from \"./convert-table-to-csv\";\n","const PLACEHOLDER_PREFIX = \"\\u200B\\u200BIC\";\nconst PLACEHOLDER_SUFFIX = \"\\u200B\\u200B\";\nconst PLACEHOLDER_REGEX = new RegExp(`${PLACEHOLDER_PREFIX}(\\\\d+)${PLACEHOLDER_SUFFIX}`, \"g\");\n\n/**\n * Strips only inline markdown marks (bold, italic, strikethrough) from a string.\n * Does not touch block-level syntax (headings, blockquotes, horizontal rules, links, images)\n * so values like `> 100`, `# items`, or `---` are preserved as-is.\n * @param text - The text to strip inline marks from.\n * @returns The text with inline marks removed.\n */\nexport function stripInlineMarks(text: string): string {\n return (\n text\n // Bold + italic\n .replace(/\\*{3}(.+?)\\*{3}/g, \"$1\")\n // Bold\n .replace(/\\*{2}(.+?)\\*{2}/g, \"$1\")\n .replace(/_{2}(.+?)_{2}/g, \"$1\")\n // Italic\n .replace(/\\*(.+?)\\*/g, \"$1\")\n .replace(/_(.+?)_/g, \"$1\")\n // Strikethrough\n .replace(/~~(.+?)~~/g, \"$1\")\n );\n}\n\n/**\n * Converts a Markdown string to plain text by stripping all formatting syntax.\n *\n * Handles headings, bold/italic/strikethrough, links, images, blockquotes, code blocks,\n * inline code, tables, and horizontal rules. Inline code content is preserved literally\n * (formatting inside backticks is not stripped).\n *\n * @param markdown - The Markdown source string.\n * @returns A plain-text string with all Markdown syntax removed.\n */\nexport function markdownToPlainText(markdown: string): string {\n if (!markdown) return \"\";\n\n // Protect inline code content from bold/italic stripping by replacing with placeholders\n const inlineCodeSnippets: string[] = [];\n let result = markdown\n // HTML comments (e.g. <!-- table-title: ... -->), including multiline and --!> variants\n .replace(/^<!--[\\s\\S]*?--!?>\\n?/gm, \"\")\n // Code blocks (must come first)\n .replace(/```[\\s\\S]*?```/g, \"\")\n // Inline code → placeholder\n .replace(/`([^`]+)`/g, (_match, code: string) => {\n inlineCodeSnippets.push(code);\n return `${PLACEHOLDER_PREFIX}${inlineCodeSnippets.length - 1}${PLACEHOLDER_SUFFIX}`;\n })\n // Remove headings markers, keep text\n .replace(/^#{1,6}\\s+/gm, \"\")\n // Remove horizontal rules\n .replace(/^(?:---|\\*\\*\\*|___)\\s*$/gm, \"\");\n result = stripInlineMarks(result)\n // Images:  → alt (must come before links)\n .replace(/!\\[([^\\]]*)\\]\\([^)]+\\)/g, \"$1\")\n // Links: [text](url) → text\n .replace(/\\[([^\\]]+)\\]\\([^)]+\\)/g, \"$1\")\n // Blockquote markers\n .replace(/^>\\s?/gm, \"\")\n // Table separator rows (|---|---|)\n .replace(/^\\|[-:\\s|]+\\|\\s*$/gm, \"\")\n // Table rows: | a | b | → a | b\n .replace(/^\\|\\s?/gm, \"\")\n .replace(/\\s?\\|$/gm, \"\")\n // Collapse multiple blank lines\n .replace(/\\n{3,}/g, \"\\n\\n\")\n .trim();\n\n // Restore inline code content\n result = result.replace(PLACEHOLDER_REGEX, (_match, index: string) => inlineCodeSnippets[Number(index)]);\n\n return result;\n}\n","import type { Ref } from \"react\";\n\n/**\n * Combines multiple React refs into a single callback ref.\n * Useful when a component needs to forward its ref while also keeping a local ref.\n *\n * @param refs - Any number of React refs (callback refs, RefObjects, or `undefined`).\n * @returns A callback ref that updates all provided refs when the DOM node changes.\n */\nexport function mergeRefs<T>(...refs: (Ref<T> | undefined)[]): (node: T | null) => void {\n return (node) => {\n for (const ref of refs) {\n if (typeof ref === \"function\") {\n ref(node);\n } else if (ref) {\n (ref as React.MutableRefObject<T | null>).current = node;\n }\n }\n };\n}\n","import { stripInlineMarks } from \"./markdown-to-plain-text\";\n\n/** Structured representation of a parsed markdown table. */\nexport interface MarkdownTable {\n /** Column header labels. */\n columns: string[];\n /** Data rows, each an array of cell values matching the column order. */\n rows: string[][];\n /** Optional title extracted from a `<!-- table-title: ... -->` comment above the table. */\n title?: string;\n}\n\n/**\n * Parses cells from a markdown table row, trimming whitespace and stripping leading/trailing pipes.\n * @param line - A single line of a markdown table.\n * @returns An array of trimmed cell values.\n */\nfunction parseCells(line: string): string[] {\n const trimmed = line.trim();\n const stripped = trimmed.startsWith(\"|\") ? trimmed.slice(1) : trimmed;\n const withoutTrailing = stripped.endsWith(\"|\") ? stripped.slice(0, -1) : stripped;\n return withoutTrailing.split(\"|\").map((cell) => cell.trim());\n}\n\n/**\n * Tests whether a line is a GFM table separator row (e.g. `| --- | --- |`).\n * @param line - A single line to test.\n * @returns `true` if the line matches the separator pattern.\n */\nfunction isSeparatorRow(line: string): boolean {\n const cells = parseCells(line);\n return cells.length > 0 && cells.every((cell) => /^:?-{1,}:?$/.test(cell));\n}\n\n/**\n * Tests whether a line looks like a markdown table row (contains at least one pipe).\n * @param line - A single line to test.\n * @returns `true` if the line contains a pipe character.\n */\nfunction isTableRow(line: string): boolean {\n return line.includes(\"|\");\n}\n\nconst TABLE_TITLE_PREFIX = \"<!-- table-title:\";\nconst TABLE_TITLE_SUFFIX = \"-->\";\n\n/**\n * Extracts a table title from a `<!-- table-title: ... -->` HTML comment.\n * Uses string methods instead of regex to avoid ReDoS on untrusted input.\n * @param line - A single line to test.\n * @returns The title string, or `undefined` if the line is not a title comment.\n */\nfunction extractTableTitle(line: string): string | undefined {\n const trimmed = line.trim();\n if (!trimmed.startsWith(TABLE_TITLE_PREFIX) || !trimmed.endsWith(TABLE_TITLE_SUFFIX)) {\n return undefined;\n }\n return trimmed.slice(TABLE_TITLE_PREFIX.length, -TABLE_TITLE_SUFFIX.length).trim() || undefined;\n}\n\n/**\n * Extracts the Nth GFM markdown table from a string.\n * @param markdown - The markdown content to parse.\n * @param tableIndex - Zero-based index of the table to extract.\n * @returns The parsed table, or `null` if no table exists at the given index.\n */\nexport function parseMarkdownTable(markdown: string, tableIndex = 0): MarkdownTable | null {\n const lines = markdown.split(\"\\n\");\n let tableCount = 0;\n\n for (let i = 0; i < lines.length - 1; i++) {\n const headerLine = lines[i];\n const separatorLine = lines[i + 1];\n\n if (!isTableRow(headerLine) || !isSeparatorRow(separatorLine)) {\n continue;\n }\n\n if (tableCount === tableIndex) {\n const columns = parseCells(headerLine).map(stripInlineMarks);\n const rows: string[][] = [];\n const title = i > 0 ? extractTableTitle(lines[i - 1]) : undefined;\n\n for (let j = i + 2; j < lines.length; j++) {\n if (!isTableRow(lines[j]) || isSeparatorRow(lines[j])) {\n break;\n }\n const trimmedLine = lines[j].trim();\n if (trimmedLine === \"\") {\n break;\n }\n rows.push(parseCells(lines[j]).map(stripInlineMarks));\n }\n\n return { columns, rows, ...(title ? { title } : {}) };\n }\n\n tableCount++;\n }\n\n return null;\n}\n","import type { MarkdownTable } from \"./parse-markdown-table\";\n\n/**\n * Escapes and optionally quotes a CSV field per RFC 4180.\n * Fields containing commas, double quotes, or newlines are wrapped in double quotes.\n * Existing double quotes are escaped by doubling them.\n * @param field - The raw field value.\n * @returns The escaped/quoted field string.\n */\nfunction escapeField(field: string): string {\n if (field.includes(\",\") || field.includes('\"') || field.includes(\"\\n\") || field.includes(\"\\r\")) {\n return `\"${field.replace(/\"/g, '\"\"')}\"`;\n }\n return field;\n}\n\n/**\n * Converts a parsed markdown table to a CSV string (RFC 4180).\n * Fields containing commas, double quotes, or newlines are quoted.\n * @param table - The structured table data to convert.\n * @returns A CSV-formatted string with CRLF line endings.\n */\nexport function convertTableToCSV(table: MarkdownTable): string {\n const headerRow = table.columns.map(escapeField).join(\",\");\n const dataRows = table.rows.map((row) => row.map(escapeField).join(\",\"));\n return [headerRow, ...dataRows].join(\"\\r\\n\");\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/utils/index.ts","../src/utils/markdown-to-plain-text.ts","../src/utils/merge-refs.ts","../src/utils/parse-markdown-table.ts","../src/utils/escape-csv-field.ts","../src/utils/convert-table-to-csv.ts"],"sourcesContent":["export { markdownToPlainText } from \"./markdown-to-plain-text\";\nexport { mergeRefs } from \"./merge-refs\";\nexport { parseMarkdownTable } from \"./parse-markdown-table\";\nexport type { MarkdownTable } from \"./parse-markdown-table\";\nexport { convertTableToCSV } from \"./convert-table-to-csv\";\nexport { escapeCsvField } from \"./escape-csv-field\";\n","const PLACEHOLDER_PREFIX = \"\\u200B\\u200BIC\";\nconst PLACEHOLDER_SUFFIX = \"\\u200B\\u200B\";\nconst PLACEHOLDER_REGEX = new RegExp(`${PLACEHOLDER_PREFIX}(\\\\d+)${PLACEHOLDER_SUFFIX}`, \"g\");\n\n/**\n * Strips only inline markdown marks (bold, italic, strikethrough) from a string.\n * Does not touch block-level syntax (headings, blockquotes, horizontal rules, links, images)\n * so values like `> 100`, `# items`, or `---` are preserved as-is.\n * @param text - The text to strip inline marks from.\n * @returns The text with inline marks removed.\n */\nexport function stripInlineMarks(text: string): string {\n return (\n text\n // Bold + italic\n .replace(/\\*{3}(.+?)\\*{3}/g, \"$1\")\n // Bold\n .replace(/\\*{2}(.+?)\\*{2}/g, \"$1\")\n .replace(/_{2}(.+?)_{2}/g, \"$1\")\n // Italic\n .replace(/\\*(.+?)\\*/g, \"$1\")\n .replace(/_(.+?)_/g, \"$1\")\n // Strikethrough\n .replace(/~~(.+?)~~/g, \"$1\")\n );\n}\n\n/**\n * Converts a Markdown string to plain text by stripping all formatting syntax.\n *\n * Handles headings, bold/italic/strikethrough, links, images, blockquotes, code blocks,\n * inline code, tables, and horizontal rules. Inline code content is preserved literally\n * (formatting inside backticks is not stripped).\n *\n * @param markdown - The Markdown source string.\n * @returns A plain-text string with all Markdown syntax removed.\n */\nexport function markdownToPlainText(markdown: string): string {\n if (!markdown) return \"\";\n\n // Protect inline code content from bold/italic stripping by replacing with placeholders\n const inlineCodeSnippets: string[] = [];\n let result = markdown\n // HTML comments (e.g. <!-- table-title: ... -->), including multiline and --!> variants\n .replace(/^<!--[\\s\\S]*?--!?>\\n?/gm, \"\")\n // Code blocks (must come first)\n .replace(/```[\\s\\S]*?```/g, \"\")\n // Inline code → placeholder\n .replace(/`([^`]+)`/g, (_match, code: string) => {\n inlineCodeSnippets.push(code);\n return `${PLACEHOLDER_PREFIX}${inlineCodeSnippets.length - 1}${PLACEHOLDER_SUFFIX}`;\n })\n // Remove headings markers, keep text\n .replace(/^#{1,6}\\s+/gm, \"\")\n // Remove horizontal rules\n .replace(/^(?:---|\\*\\*\\*|___)\\s*$/gm, \"\");\n result = stripInlineMarks(result)\n // Images:  → alt (must come before links)\n .replace(/!\\[([^\\]]*)\\]\\([^)]+\\)/g, \"$1\")\n // Links: [text](url) → text\n .replace(/\\[([^\\]]+)\\]\\([^)]+\\)/g, \"$1\")\n // Blockquote markers\n .replace(/^>\\s?/gm, \"\")\n // Table separator rows (|---|---|)\n .replace(/^\\|[-:\\s|]+\\|\\s*$/gm, \"\")\n // Table rows: | a | b | → a | b\n .replace(/^\\|\\s?/gm, \"\")\n .replace(/\\s?\\|$/gm, \"\")\n // Collapse multiple blank lines\n .replace(/\\n{3,}/g, \"\\n\\n\")\n .trim();\n\n // Restore inline code content\n result = result.replace(PLACEHOLDER_REGEX, (_match, index: string) => inlineCodeSnippets[Number(index)]);\n\n return result;\n}\n","import type { Ref } from \"react\";\n\n/**\n * Combines multiple React refs into a single callback ref.\n * Useful when a component needs to forward its ref while also keeping a local ref.\n *\n * @param refs - Any number of React refs (callback refs, RefObjects, or `undefined`).\n * @returns A callback ref that updates all provided refs when the DOM node changes.\n */\nexport function mergeRefs<T>(...refs: (Ref<T> | undefined)[]): (node: T | null) => void {\n return (node) => {\n for (const ref of refs) {\n if (typeof ref === \"function\") {\n ref(node);\n } else if (ref) {\n (ref as React.MutableRefObject<T | null>).current = node;\n }\n }\n };\n}\n","import { stripInlineMarks } from \"./markdown-to-plain-text\";\n\n/** Structured representation of a parsed markdown table. */\nexport interface MarkdownTable {\n /** Column header labels. */\n columns: string[];\n /** Data rows, each an array of cell values matching the column order. */\n rows: string[][];\n /** Optional title extracted from a `<!-- table-title: ... -->` comment above the table. */\n title?: string;\n}\n\n/**\n * Parses cells from a markdown table row, trimming whitespace and stripping leading/trailing pipes.\n * @param line - A single line of a markdown table.\n * @returns An array of trimmed cell values.\n */\nfunction parseCells(line: string): string[] {\n const trimmed = line.trim();\n const stripped = trimmed.startsWith(\"|\") ? trimmed.slice(1) : trimmed;\n const withoutTrailing = stripped.endsWith(\"|\") ? stripped.slice(0, -1) : stripped;\n return withoutTrailing.split(\"|\").map((cell) => cell.trim());\n}\n\n/**\n * Tests whether a line is a GFM table separator row (e.g. `| --- | --- |`).\n * @param line - A single line to test.\n * @returns `true` if the line matches the separator pattern.\n */\nfunction isSeparatorRow(line: string): boolean {\n const cells = parseCells(line);\n return cells.length > 0 && cells.every((cell) => /^:?-{1,}:?$/.test(cell));\n}\n\n/**\n * Tests whether a line looks like a markdown table row (contains at least one pipe).\n * @param line - A single line to test.\n * @returns `true` if the line contains a pipe character.\n */\nfunction isTableRow(line: string): boolean {\n return line.includes(\"|\");\n}\n\nconst TABLE_TITLE_PREFIX = \"<!-- table-title:\";\nconst TABLE_TITLE_SUFFIX = \"-->\";\n\n/**\n * Extracts a table title from a `<!-- table-title: ... -->` HTML comment.\n * Uses string methods instead of regex to avoid ReDoS on untrusted input.\n * @param line - A single line to test.\n * @returns The title string, or `undefined` if the line is not a title comment.\n */\nfunction extractTableTitle(line: string): string | undefined {\n const trimmed = line.trim();\n if (!trimmed.startsWith(TABLE_TITLE_PREFIX) || !trimmed.endsWith(TABLE_TITLE_SUFFIX)) {\n return undefined;\n }\n return trimmed.slice(TABLE_TITLE_PREFIX.length, -TABLE_TITLE_SUFFIX.length).trim() || undefined;\n}\n\n/**\n * Extracts the Nth GFM markdown table from a string.\n * @param markdown - The markdown content to parse.\n * @param tableIndex - Zero-based index of the table to extract.\n * @returns The parsed table, or `null` if no table exists at the given index.\n */\nexport function parseMarkdownTable(markdown: string, tableIndex = 0): MarkdownTable | null {\n const lines = markdown.split(\"\\n\");\n let tableCount = 0;\n\n for (let i = 0; i < lines.length - 1; i++) {\n const headerLine = lines[i];\n const separatorLine = lines[i + 1];\n\n if (!isTableRow(headerLine) || !isSeparatorRow(separatorLine)) {\n continue;\n }\n\n if (tableCount === tableIndex) {\n const columns = parseCells(headerLine).map(stripInlineMarks);\n const rows: string[][] = [];\n const title = i > 0 ? extractTableTitle(lines[i - 1]) : undefined;\n\n for (let j = i + 2; j < lines.length; j++) {\n if (!isTableRow(lines[j]) || isSeparatorRow(lines[j])) {\n break;\n }\n const trimmedLine = lines[j].trim();\n if (trimmedLine === \"\") {\n break;\n }\n rows.push(parseCells(lines[j]).map(stripInlineMarks));\n }\n\n return { columns, rows, ...(title ? { title } : {}) };\n }\n\n tableCount++;\n }\n\n return null;\n}\n","/**\n * Characters that trigger spreadsheet-formula evaluation when present at the start of a CSV cell.\n * Excel, Google Sheets, and Numbers all treat a field beginning with one of these as a formula\n * (e.g. `=HYPERLINK(...)`, `+1+1`, `@SUM(...)`), so a field exfiltrated from user-controlled\n * data can run arbitrary expressions in the spreadsheet on open.\n *\n * Note: `-` is intentionally excluded. While `+` and `-` are listed in some CSV injection docs,\n * `-42` is a valid negative number and Excel/Sheets do NOT evaluate it as a formula. Including `-`\n * would corrupt all negative-number cells. The actual risk from `-` is extremely low in practice.\n */\nconst FORMULA_TRIGGERS = new Set([\"=\", \"+\", \"@\", \"\\t\", \"\\r\"]);\n\n/**\n * Escapes and optionally quotes a CSV field per RFC 4180, with OWASP-recommended formula-injection\n * neutralisation for fields beginning with a trigger character.\n *\n * - Fields starting with `=`, `+`, `@`, `\\t`, or `\\r` are prefixed with a tab so the spreadsheet\n * renders them as literal text instead of evaluating them as a formula. The tab itself disappears\n * in the rendered cell.\n * - Fields containing commas, double quotes, newlines, or a leading tab (from formula neutralisation)\n * are wrapped in double quotes per RFC 4180. The double-quote wrapping ensures the tab prefix is\n * preserved in parsers that would otherwise strip leading whitespace.\n * - Existing double quotes are escaped by doubling them.\n *\n * @param field - The raw field value.\n * @returns The escaped/quoted field string.\n */\nexport function escapeCsvField(field: string): string {\n const needsNeutralise = field.length > 0 && FORMULA_TRIGGERS.has(field[0]);\n const safeField = needsNeutralise ? `\\t${field}` : field;\n if (\n needsNeutralise ||\n safeField.includes(\",\") ||\n safeField.includes('\"') ||\n safeField.includes(\"\\n\") ||\n safeField.includes(\"\\r\")\n ) {\n return `\"${safeField.replace(/\"/g, '\"\"')}\"`;\n }\n return safeField;\n}\n","import { escapeCsvField } from \"./escape-csv-field\";\nimport type { MarkdownTable } from \"./parse-markdown-table\";\n\n/**\n * Converts a parsed markdown table to a CSV string (RFC 4180).\n * Fields containing commas, double quotes, or newlines are quoted.\n * @param table - The structured table data to convert.\n * @returns A CSV-formatted string with CRLF line endings.\n */\nexport function convertTableToCSV(table: MarkdownTable): string {\n const headerRow = table.columns.map(escapeCsvField).join(\",\");\n const dataRows = table.rows.map((row) => row.map(escapeCsvField).join(\",\"));\n return [headerRow, ...dataRows].join(\"\\r\\n\");\n}\n"],"mappings":"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","names":[]}
|
package/dist/utils.d.cts
CHANGED
|
@@ -46,4 +46,21 @@ declare function parseMarkdownTable(markdown: string, tableIndex?: number): Mark
|
|
|
46
46
|
*/
|
|
47
47
|
declare function convertTableToCSV(table: MarkdownTable): string;
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
/**
|
|
50
|
+
* Escapes and optionally quotes a CSV field per RFC 4180, with OWASP-recommended formula-injection
|
|
51
|
+
* neutralisation for fields beginning with a trigger character.
|
|
52
|
+
*
|
|
53
|
+
* - Fields starting with `=`, `+`, `@`, `\t`, or `\r` are prefixed with a tab so the spreadsheet
|
|
54
|
+
* renders them as literal text instead of evaluating them as a formula. The tab itself disappears
|
|
55
|
+
* in the rendered cell.
|
|
56
|
+
* - Fields containing commas, double quotes, newlines, or a leading tab (from formula neutralisation)
|
|
57
|
+
* are wrapped in double quotes per RFC 4180. The double-quote wrapping ensures the tab prefix is
|
|
58
|
+
* preserved in parsers that would otherwise strip leading whitespace.
|
|
59
|
+
* - Existing double quotes are escaped by doubling them.
|
|
60
|
+
*
|
|
61
|
+
* @param field - The raw field value.
|
|
62
|
+
* @returns The escaped/quoted field string.
|
|
63
|
+
*/
|
|
64
|
+
declare function escapeCsvField(field: string): string;
|
|
65
|
+
|
|
66
|
+
export { type MarkdownTable, convertTableToCSV, escapeCsvField, markdownToPlainText, mergeRefs, parseMarkdownTable };
|
package/dist/utils.d.ts
CHANGED
|
@@ -46,4 +46,21 @@ declare function parseMarkdownTable(markdown: string, tableIndex?: number): Mark
|
|
|
46
46
|
*/
|
|
47
47
|
declare function convertTableToCSV(table: MarkdownTable): string;
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
/**
|
|
50
|
+
* Escapes and optionally quotes a CSV field per RFC 4180, with OWASP-recommended formula-injection
|
|
51
|
+
* neutralisation for fields beginning with a trigger character.
|
|
52
|
+
*
|
|
53
|
+
* - Fields starting with `=`, `+`, `@`, `\t`, or `\r` are prefixed with a tab so the spreadsheet
|
|
54
|
+
* renders them as literal text instead of evaluating them as a formula. The tab itself disappears
|
|
55
|
+
* in the rendered cell.
|
|
56
|
+
* - Fields containing commas, double quotes, newlines, or a leading tab (from formula neutralisation)
|
|
57
|
+
* are wrapped in double quotes per RFC 4180. The double-quote wrapping ensures the tab prefix is
|
|
58
|
+
* preserved in parsers that would otherwise strip leading whitespace.
|
|
59
|
+
* - Existing double quotes are escaped by doubling them.
|
|
60
|
+
*
|
|
61
|
+
* @param field - The raw field value.
|
|
62
|
+
* @returns The escaped/quoted field string.
|
|
63
|
+
*/
|
|
64
|
+
declare function escapeCsvField(field: string): string;
|
|
65
|
+
|
|
66
|
+
export { type MarkdownTable, convertTableToCSV, escapeCsvField, markdownToPlainText, mergeRefs, parseMarkdownTable };
|
package/dist/utils.js
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import {
|
|
2
2
|
convertTableToCSV,
|
|
3
|
+
escapeCsvField,
|
|
3
4
|
parseMarkdownTable
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import {
|
|
6
|
-
markdownToPlainText
|
|
7
|
-
} from "./chunk-NMKKU2UG.js";
|
|
5
|
+
} from "./chunk-JJUIBBBU.js";
|
|
8
6
|
import {
|
|
9
7
|
mergeRefs
|
|
10
8
|
} from "./chunk-KNYB3RL7.js";
|
|
9
|
+
import {
|
|
10
|
+
markdownToPlainText
|
|
11
|
+
} from "./chunk-NMKKU2UG.js";
|
|
11
12
|
import "./chunk-5WRI5ZAA.js";
|
|
12
13
|
export {
|
|
13
14
|
convertTableToCSV,
|
|
15
|
+
escapeCsvField,
|
|
14
16
|
markdownToPlainText,
|
|
15
17
|
mergeRefs,
|
|
16
18
|
parseMarkdownTable
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shapesos/clay",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"main": "./dist/index.cjs",
|
|
5
5
|
"module": "./dist/index.js",
|
|
6
6
|
"devDependencies": {
|
|
@@ -13,11 +13,15 @@
|
|
|
13
13
|
"@storybook/react": "^10.2.13",
|
|
14
14
|
"@storybook/react-vite": "^10.2.13",
|
|
15
15
|
"@tabler/icons-react": "^3.37.1",
|
|
16
|
+
"@tailwindcss/cli": "^4.2.4",
|
|
17
|
+
"@tailwindcss/postcss": "^4.2.4",
|
|
18
|
+
"@tailwindcss/vite": "^4.2.4",
|
|
16
19
|
"@testing-library/dom": "^10.4.1",
|
|
17
20
|
"@testing-library/jest-dom": "^6.9.1",
|
|
18
21
|
"@testing-library/react": "^16.3.2",
|
|
19
22
|
"@testing-library/user-event": "^14.6.1",
|
|
20
23
|
"@types/node": "^25.3.2",
|
|
24
|
+
"@types/papaparse": "^5.5.2",
|
|
21
25
|
"@types/react": "^19.2.14",
|
|
22
26
|
"@types/react-dom": "^19.2.3",
|
|
23
27
|
"@vitejs/plugin-react": "^5.1.4",
|
|
@@ -31,7 +35,9 @@
|
|
|
31
35
|
"react-dom": "^19.2.4",
|
|
32
36
|
"storybook": "^10.2.13",
|
|
33
37
|
"styled-components": "^6.3.11",
|
|
38
|
+
"tailwindcss": "^4",
|
|
34
39
|
"tsup": "~8.5.1",
|
|
40
|
+
"tw-animate-css": "^1.4.0",
|
|
35
41
|
"typescript": "~5.9.3",
|
|
36
42
|
"typescript-eslint": "^8.56.1",
|
|
37
43
|
"vite": "^7.3.1",
|
|
@@ -63,6 +69,17 @@
|
|
|
63
69
|
"import": "./dist/blocks.js",
|
|
64
70
|
"require": "./dist/blocks.cjs"
|
|
65
71
|
},
|
|
72
|
+
"./blocks/styles.css": "./dist/blocks.css",
|
|
73
|
+
"./artifacts": {
|
|
74
|
+
"types": "./dist/artifacts.d.ts",
|
|
75
|
+
"import": "./dist/artifacts.js",
|
|
76
|
+
"require": "./dist/artifacts.cjs"
|
|
77
|
+
},
|
|
78
|
+
"./table": {
|
|
79
|
+
"types": "./dist/table.d.ts",
|
|
80
|
+
"import": "./dist/table.js",
|
|
81
|
+
"require": "./dist/table.cjs"
|
|
82
|
+
},
|
|
66
83
|
"./icon": {
|
|
67
84
|
"types": "./dist/icon.d.ts",
|
|
68
85
|
"import": "./dist/icon.js",
|
|
@@ -112,7 +129,9 @@
|
|
|
112
129
|
"registry": "https://registry.npmjs.org/"
|
|
113
130
|
},
|
|
114
131
|
"scripts": {
|
|
115
|
-
"build": "tsup",
|
|
132
|
+
"build": "bun run gen:theme && tsup && bun run build:css",
|
|
133
|
+
"build:css": "tailwindcss -i src/styles/tailwind.css -o dist/blocks.css --minify",
|
|
134
|
+
"gen:theme": "bun run scripts/generate-theme-tokens.ts",
|
|
116
135
|
"dev": "tsup --watch",
|
|
117
136
|
"storybook": "storybook dev -p 6006",
|
|
118
137
|
"build-storybook": "storybook build",
|
|
@@ -128,13 +147,19 @@
|
|
|
128
147
|
"check": "eslint . && tsc --noEmit && vitest run",
|
|
129
148
|
"prepare": "lefthook install"
|
|
130
149
|
},
|
|
131
|
-
"sideEffects":
|
|
150
|
+
"sideEffects": [
|
|
151
|
+
"**/*.css"
|
|
152
|
+
],
|
|
132
153
|
"type": "module",
|
|
133
154
|
"types": "./dist/index.d.ts",
|
|
134
155
|
"dependencies": {
|
|
156
|
+
"@radix-ui/react-tooltip": "^1.2.8",
|
|
157
|
+
"clsx": "^2.1.1",
|
|
135
158
|
"lottie-react": "^2.4.1",
|
|
159
|
+
"papaparse": "^5.5.3",
|
|
136
160
|
"react-markdown": "^8.0.7",
|
|
137
161
|
"remark-breaks": "^4.0.0",
|
|
138
|
-
"remark-gfm": "^3.0.1"
|
|
162
|
+
"remark-gfm": "^3.0.1",
|
|
163
|
+
"tailwind-merge": "^3.5.0"
|
|
139
164
|
}
|
|
140
165
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utils/parse-markdown-table.ts","../src/utils/convert-table-to-csv.ts"],"sourcesContent":["import { stripInlineMarks } from \"./markdown-to-plain-text\";\n\n/** Structured representation of a parsed markdown table. */\nexport interface MarkdownTable {\n /** Column header labels. */\n columns: string[];\n /** Data rows, each an array of cell values matching the column order. */\n rows: string[][];\n /** Optional title extracted from a `<!-- table-title: ... -->` comment above the table. */\n title?: string;\n}\n\n/**\n * Parses cells from a markdown table row, trimming whitespace and stripping leading/trailing pipes.\n * @param line - A single line of a markdown table.\n * @returns An array of trimmed cell values.\n */\nfunction parseCells(line: string): string[] {\n const trimmed = line.trim();\n const stripped = trimmed.startsWith(\"|\") ? trimmed.slice(1) : trimmed;\n const withoutTrailing = stripped.endsWith(\"|\") ? stripped.slice(0, -1) : stripped;\n return withoutTrailing.split(\"|\").map((cell) => cell.trim());\n}\n\n/**\n * Tests whether a line is a GFM table separator row (e.g. `| --- | --- |`).\n * @param line - A single line to test.\n * @returns `true` if the line matches the separator pattern.\n */\nfunction isSeparatorRow(line: string): boolean {\n const cells = parseCells(line);\n return cells.length > 0 && cells.every((cell) => /^:?-{1,}:?$/.test(cell));\n}\n\n/**\n * Tests whether a line looks like a markdown table row (contains at least one pipe).\n * @param line - A single line to test.\n * @returns `true` if the line contains a pipe character.\n */\nfunction isTableRow(line: string): boolean {\n return line.includes(\"|\");\n}\n\nconst TABLE_TITLE_PREFIX = \"<!-- table-title:\";\nconst TABLE_TITLE_SUFFIX = \"-->\";\n\n/**\n * Extracts a table title from a `<!-- table-title: ... -->` HTML comment.\n * Uses string methods instead of regex to avoid ReDoS on untrusted input.\n * @param line - A single line to test.\n * @returns The title string, or `undefined` if the line is not a title comment.\n */\nfunction extractTableTitle(line: string): string | undefined {\n const trimmed = line.trim();\n if (!trimmed.startsWith(TABLE_TITLE_PREFIX) || !trimmed.endsWith(TABLE_TITLE_SUFFIX)) {\n return undefined;\n }\n return trimmed.slice(TABLE_TITLE_PREFIX.length, -TABLE_TITLE_SUFFIX.length).trim() || undefined;\n}\n\n/**\n * Extracts the Nth GFM markdown table from a string.\n * @param markdown - The markdown content to parse.\n * @param tableIndex - Zero-based index of the table to extract.\n * @returns The parsed table, or `null` if no table exists at the given index.\n */\nexport function parseMarkdownTable(markdown: string, tableIndex = 0): MarkdownTable | null {\n const lines = markdown.split(\"\\n\");\n let tableCount = 0;\n\n for (let i = 0; i < lines.length - 1; i++) {\n const headerLine = lines[i];\n const separatorLine = lines[i + 1];\n\n if (!isTableRow(headerLine) || !isSeparatorRow(separatorLine)) {\n continue;\n }\n\n if (tableCount === tableIndex) {\n const columns = parseCells(headerLine).map(stripInlineMarks);\n const rows: string[][] = [];\n const title = i > 0 ? extractTableTitle(lines[i - 1]) : undefined;\n\n for (let j = i + 2; j < lines.length; j++) {\n if (!isTableRow(lines[j]) || isSeparatorRow(lines[j])) {\n break;\n }\n const trimmedLine = lines[j].trim();\n if (trimmedLine === \"\") {\n break;\n }\n rows.push(parseCells(lines[j]).map(stripInlineMarks));\n }\n\n return { columns, rows, ...(title ? { title } : {}) };\n }\n\n tableCount++;\n }\n\n return null;\n}\n","import type { MarkdownTable } from \"./parse-markdown-table\";\n\n/**\n * Escapes and optionally quotes a CSV field per RFC 4180.\n * Fields containing commas, double quotes, or newlines are wrapped in double quotes.\n * Existing double quotes are escaped by doubling them.\n * @param field - The raw field value.\n * @returns The escaped/quoted field string.\n */\nfunction escapeField(field: string): string {\n if (field.includes(\",\") || field.includes('\"') || field.includes(\"\\n\") || field.includes(\"\\r\")) {\n return `\"${field.replace(/\"/g, '\"\"')}\"`;\n }\n return field;\n}\n\n/**\n * Converts a parsed markdown table to a CSV string (RFC 4180).\n * Fields containing commas, double quotes, or newlines are quoted.\n * @param table - The structured table data to convert.\n * @returns A CSV-formatted string with CRLF line endings.\n */\nexport function convertTableToCSV(table: MarkdownTable): string {\n const headerRow = table.columns.map(escapeField).join(\",\");\n const dataRows = table.rows.map((row) => row.map(escapeField).join(\",\"));\n return [headerRow, ...dataRows].join(\"\\r\\n\");\n}\n"],"mappings":";;;;;AAiBA,SAAS,WAAW,MAAwB;AAC1C,QAAM,UAAU,KAAK,KAAK;AAC1B,QAAM,WAAW,QAAQ,WAAW,GAAG,IAAI,QAAQ,MAAM,CAAC,IAAI;AAC9D,QAAM,kBAAkB,SAAS,SAAS,GAAG,IAAI,SAAS,MAAM,GAAG,EAAE,IAAI;AACzE,SAAO,gBAAgB,MAAM,GAAG,EAAE,IAAI,CAAC,SAAS,KAAK,KAAK,CAAC;AAC7D;AAOA,SAAS,eAAe,MAAuB;AAC7C,QAAM,QAAQ,WAAW,IAAI;AAC7B,SAAO,MAAM,SAAS,KAAK,MAAM,MAAM,CAAC,SAAS,cAAc,KAAK,IAAI,CAAC;AAC3E;AAOA,SAAS,WAAW,MAAuB;AACzC,SAAO,KAAK,SAAS,GAAG;AAC1B;AAEA,IAAM,qBAAqB;AAC3B,IAAM,qBAAqB;AAQ3B,SAAS,kBAAkB,MAAkC;AAC3D,QAAM,UAAU,KAAK,KAAK;AAC1B,MAAI,CAAC,QAAQ,WAAW,kBAAkB,KAAK,CAAC,QAAQ,SAAS,kBAAkB,GAAG;AACpF,WAAO;AAAA,EACT;AACA,SAAO,QAAQ,MAAM,mBAAmB,QAAQ,CAAC,mBAAmB,MAAM,EAAE,KAAK,KAAK;AACxF;AAQO,SAAS,mBAAmB,UAAkB,aAAa,GAAyB;AACzF,QAAM,QAAQ,SAAS,MAAM,IAAI;AACjC,MAAI,aAAa;AAEjB,WAAS,IAAI,GAAG,IAAI,MAAM,SAAS,GAAG,KAAK;AACzC,UAAM,aAAa,MAAM,CAAC;AAC1B,UAAM,gBAAgB,MAAM,IAAI,CAAC;AAEjC,QAAI,CAAC,WAAW,UAAU,KAAK,CAAC,eAAe,aAAa,GAAG;AAC7D;AAAA,IACF;AAEA,QAAI,eAAe,YAAY;AAC7B,YAAM,UAAU,WAAW,UAAU,EAAE,IAAI,gBAAgB;AAC3D,YAAM,OAAmB,CAAC;AAC1B,YAAM,QAAQ,IAAI,IAAI,kBAAkB,MAAM,IAAI,CAAC,CAAC,IAAI;AAExD,eAAS,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACzC,YAAI,CAAC,WAAW,MAAM,CAAC,CAAC,KAAK,eAAe,MAAM,CAAC,CAAC,GAAG;AACrD;AAAA,QACF;AACA,cAAM,cAAc,MAAM,CAAC,EAAE,KAAK;AAClC,YAAI,gBAAgB,IAAI;AACtB;AAAA,QACF;AACA,aAAK,KAAK,WAAW,MAAM,CAAC,CAAC,EAAE,IAAI,gBAAgB,CAAC;AAAA,MACtD;AAEA,aAAO,EAAE,SAAS,MAAM,GAAI,QAAQ,EAAE,MAAM,IAAI,CAAC,EAAG;AAAA,IACtD;AAEA;AAAA,EACF;AAEA,SAAO;AACT;;;AC5FA,SAAS,YAAY,OAAuB;AAC1C,MAAI,MAAM,SAAS,GAAG,KAAK,MAAM,SAAS,GAAG,KAAK,MAAM,SAAS,IAAI,KAAK,MAAM,SAAS,IAAI,GAAG;AAC9F,WAAO,IAAI,MAAM,QAAQ,MAAM,IAAI,CAAC;AAAA,EACtC;AACA,SAAO;AACT;AAQO,SAAS,kBAAkB,OAA8B;AAC9D,QAAM,YAAY,MAAM,QAAQ,IAAI,WAAW,EAAE,KAAK,GAAG;AACzD,QAAM,WAAW,MAAM,KAAK,IAAI,CAAC,QAAQ,IAAI,IAAI,WAAW,EAAE,KAAK,GAAG,CAAC;AACvE,SAAO,CAAC,WAAW,GAAG,QAAQ,EAAE,KAAK,MAAM;AAC7C;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/icon-button/icon-button-styles.ts","../src/components/icon-button/icon-button.tsx"],"sourcesContent":["import styled from \"styled-components\";\n\nimport { colors } from \"../../tokens/colors\";\nimport type { IconButtonSize, IconButtonVariant } from \"./types\";\n\nconst PADDING: Record<IconButtonVariant, Record<IconButtonSize, string>> = {\n short: {\n small: \"4px\",\n medium: \"6px\",\n },\n long: {\n small: \"4px 6px\",\n medium: \"6px 10px\",\n },\n};\n\nconst BORDER_RADIUS: Record<IconButtonSize, number> = {\n small: 6,\n medium: 10,\n};\n\nexport const Button = styled.button<{\n $size: IconButtonSize;\n $variant: IconButtonVariant;\n $isSelected: boolean;\n}>`\n display: inline-flex;\n align-items: center;\n justify-content: center;\n padding: ${({ $size, $variant }) => PADDING[$variant][$size]};\n border: none;\n border-radius: ${({ $size }) => BORDER_RADIUS[$size]}px;\n background: ${({ $isSelected }) => ($isSelected ? colors[\"brown-40\"] : \"transparent\")};\n color: ${colors[\"brown-100\"]};\n cursor: pointer;\n transition: background-color 100ms ease;\n\n &:hover {\n background: ${({ $isSelected }) => ($isSelected ? colors[\"brown-40\"] : colors[\"brown-20\"])};\n }\n\n &:active {\n background: ${colors[\"brown-40\"]};\n }\n\n &:disabled {\n color: ${colors[\"brown-50\"]};\n background: transparent;\n cursor: not-allowed;\n }\n`;\n","import { Icon } from \"../icon/icon\";\nimport { Button } from \"./icon-button-styles\";\nimport type { IconButtonProps, IconButtonSize } from \"./types\";\n\nconst ICON_SIZE_BY_BUTTON_SIZE: Record<IconButtonSize, number> = {\n small: 16,\n medium: 20,\n};\n\nexport function IconButton({\n icon,\n size = \"small\",\n variant = \"short\",\n isSelected = false,\n disabled = false,\n onClick,\n className,\n id,\n as,\n \"aria-label\": ariaLabel,\n}: IconButtonProps): React.ReactNode {\n return (\n <Button\n className={className}\n id={id}\n as={as}\n $size={size}\n $variant={variant}\n $isSelected={isSelected}\n disabled={disabled}\n onClick={disabled ? undefined : onClick}\n aria-label={ariaLabel}\n >\n <Icon icon={icon} size={ICON_SIZE_BY_BUTTON_SIZE[size]} />\n </Button>\n );\n}\n"],"mappings":";;;;;;;;AAAA,OAAO,YAAY;AAKnB,IAAM,UAAqE;AAAA,EACzE,OAAO;AAAA,IACL,OAAO;AAAA,IACP,QAAQ;AAAA,EACV;AAAA,EACA,MAAM;AAAA,IACJ,OAAO;AAAA,IACP,QAAQ;AAAA,EACV;AACF;AAEA,IAAM,gBAAgD;AAAA,EACpD,OAAO;AAAA,EACP,QAAQ;AACV;AAEO,IAAM,SAAS,OAAO;AAAA;AAAA;AAAA;AAAA,aAQhB,CAAC,EAAE,OAAO,SAAS,MAAM,QAAQ,QAAQ,EAAE,KAAK,CAAC;AAAA;AAAA,mBAE3C,CAAC,EAAE,MAAM,MAAM,cAAc,KAAK,CAAC;AAAA,gBACtC,CAAC,EAAE,YAAY,MAAO,cAAc,OAAO,UAAU,IAAI,aAAc;AAAA,WAC5E,OAAO,WAAW,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,kBAKZ,CAAC,EAAE,YAAY,MAAO,cAAc,OAAO,UAAU,IAAI,OAAO,UAAU,CAAE;AAAA;AAAA;AAAA;AAAA,kBAI5E,OAAO,UAAU,CAAC;AAAA;AAAA;AAAA;AAAA,aAIvB,OAAO,UAAU,CAAC;AAAA;AAAA;AAAA;AAAA;;;ACbzB;AA7BN,IAAM,2BAA2D;AAAA,EAC/D,OAAO;AAAA,EACP,QAAQ;AACV;AAEO,SAAS,WAAW;AAAA,EACzB;AAAA,EACA,OAAO;AAAA,EACP,UAAU;AAAA,EACV,aAAa;AAAA,EACb,WAAW;AAAA,EACX;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,cAAc;AAChB,GAAqC;AACnC,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA;AAAA,MACA;AAAA,MACA,OAAO;AAAA,MACP,UAAU;AAAA,MACV,aAAa;AAAA,MACb;AAAA,MACA,SAAS,WAAW,SAAY;AAAA,MAChC,cAAY;AAAA,MAEZ,8BAAC,QAAK,MAAY,MAAM,yBAAyB,IAAI,GAAG;AAAA;AAAA,EAC1D;AAEJ;","names":[]}
|
package/dist/chunk-OKPNST44.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
//# sourceMappingURL=chunk-OKPNST44.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/blocks/block-context/block-context.tsx","../src/components/blocks/types.ts","../src/components/blocks/text-block/text-block.tsx","../src/components/blocks/scrollable-table/scrollable-table-styles.ts","../src/components/blocks/scrollable-table/scrollable-table.tsx","../src/components/blocks/text-block/sanitize.ts","../src/components/blocks/text-block/text-block-styles.ts","../src/components/blocks/block-services/text-block-service.ts","../src/components/blocks/block-services/index.ts","../src/components/blocks/block/block.tsx"],"sourcesContent":["import { createContext, useContext, type ComponentType } from \"react\";\n\n/** Renderer-level concerns provided to descendants of any `Block`-rendering tree. */\nexport interface BlockContextValue {\n /** Optional component rendered next to each table in a TEXT block. Receives the zero-based table index. */\n TableActions?: ComponentType<{ tableIndex: number }>;\n}\n\nexport const BlockContext = createContext<BlockContextValue | null>(null);\n\n/** Returns the ambient block-rendering context. Returns an empty object outside a `<BlockContext.Provider>`. */\nexport function useBlockContext(): BlockContextValue {\n return useContext(BlockContext) ?? {};\n}\n","import type { ComponentType } from \"react\";\n\n/** Block-type discriminator constants. Mirrors the wire shape from io-server / shapes-agent. */\nexport const blockTypes = {\n TEXT: \"TEXT\",\n} as const;\n\n/** Discriminator for content blocks. */\nexport type BlockType = (typeof blockTypes)[keyof typeof blockTypes];\n\n/** Plain-text content block — rendered as Markdown. */\nexport interface TextBlockRecord {\n /** Block-type discriminator. */\n type: typeof blockTypes.TEXT;\n /** Type-specific data; kept as `payload` to match the wire shape 1:1. */\n payload: {\n /** The Markdown source for this text segment. */\n text: string;\n };\n}\n\n/** Discriminated union of all supported content blocks. Additive — new types extend this union. */\nexport type BlockRecord = TextBlockRecord;\n\n/** Props every concrete block component receives from the dispatcher. */\nexport interface BlockComponentProps<TBlock extends BlockRecord = BlockRecord> {\n /** The block to render. */\n block: TBlock;\n}\n\n/** Per-block-type service — registry entry that pairs a discriminator with its concrete renderer. */\nexport interface BlockService<TBlock extends BlockRecord = BlockRecord> {\n /** Discriminator value this service handles. */\n type: BlockType;\n /** React component that renders a block of this type. */\n Component: ComponentType<BlockComponentProps<TBlock>>;\n}\n","import type { ReactNode } from \"react\";\nimport ReactMarkdown from \"react-markdown\";\nimport remarkBreaks from \"remark-breaks\";\nimport remarkGfm from \"remark-gfm\";\n\nimport { ScrollableTable, type TableCounter } from \"../scrollable-table/scrollable-table\";\nimport type { BlockComponentProps, TextBlockRecord } from \"../types\";\nimport { sanitize } from \"./sanitize\";\nimport { TextBlockWrapper } from \"./text-block-styles\";\n\n/** Renders a TEXT block as Markdown with GFM (tables, strikethrough) and line breaks. */\nexport function TextBlock({ block }: BlockComponentProps<TextBlockRecord>): ReactNode {\n const counter: TableCounter = { value: 0 };\n\n const markdownComponents = {\n table: ((tableProps: { node?: unknown; children?: ReactNode }) => (\n <ScrollableTable {...tableProps} counter={counter} />\n )) as React.ComponentType<unknown>,\n };\n\n return (\n <TextBlockWrapper>\n <ReactMarkdown remarkPlugins={[remarkGfm, remarkBreaks]} components={markdownComponents}>\n {sanitize(block.payload.text)}\n </ReactMarkdown>\n </TextBlockWrapper>\n );\n}\n","import styled from \"styled-components\";\n\nimport { colors } from \"@/tokens/colors\";\n\nexport const TableContainer = styled.div`\n position: relative;\n margin-block: 8px 16px;\n`;\n\nexport const TableScroll = styled.div`\n width: 100%;\n overflow-x: auto;\n overflow-y: hidden;\n`;\n\nexport const TableActionsWrapper = styled.div`\n display: flex;\n justify-content: flex-start;\n margin-block: 8px 0;\n`;\n\nexport const TableActionsDivider = styled.div`\n height: 1px;\n background: ${colors[\"brown-40\"]};\n margin-block: 8px 0;\n`;\n","import { type ReactNode } from \"react\";\n\nimport { useBlockContext } from \"../block-context/block-context\";\nimport { TableActionsWrapper, TableActionsDivider, TableContainer, TableScroll } from \"./scrollable-table-styles\";\n\n/** Mutable counter used to assign sequential table indices during a single render pass. */\nexport interface TableCounter {\n /** The current value; incremented each time a table component is rendered. */\n value: number;\n}\n\n/** Props for the ScrollableTable component. */\ninterface ScrollableTableProps {\n /** Unused node prop from react-markdown. */\n node?: unknown;\n /** Table children elements. */\n children?: ReactNode;\n /** Mutable counter for tracking table indices. */\n counter?: TableCounter;\n}\n\n/** Renders a table inside a horizontally scrollable wrapper with optional actions below. */\nexport function ScrollableTable({ node: _node, counter, ...props }: ScrollableTableProps): ReactNode {\n const currentIndex = counter ? counter.value : 0;\n if (counter) {\n // eslint-disable-next-line react-hooks/immutability -- Counter is an intentionally mutable shared object for sequential table indexing\n counter.value += 1;\n }\n\n const { TableActions } = useBlockContext();\n\n return (\n <TableContainer>\n <TableScroll>\n <table {...props} />\n </TableScroll>\n {TableActions && (\n <>\n <TableActionsWrapper>\n <TableActions tableIndex={currentIndex} />\n </TableActionsWrapper>\n <TableActionsDivider />\n </>\n )}\n </TableContainer>\n );\n}\n","// Strips io-server's `<!-- table-title: ... -->` HTML-comment metadata before rendering.\n// The marker is emitted by the server's CSV-export pipeline so the export button can label\n// the file; it has no place in the visual output. If the server-side format changes\n// (prefix, spacing, closing-tag style), this filter silently stops matching and the\n// raw HTML comment leaks into rendered Markdown — keep this contract aligned with io-server.\nexport function sanitize(text: string): string {\n return text\n .split(\"\\n\")\n .filter((line) => {\n const t = line.trim();\n return !(t.startsWith(\"<!-- table-title:\") && t.endsWith(\"-->\"));\n })\n .join(\"\\n\");\n}\n","import styled from \"styled-components\";\n\nimport { colors } from \"@/tokens/colors\";\nimport { typographyMixin, typographyTypes } from \"@/tokens/typography\";\n\nexport const TextBlockWrapper = styled.div`\n ${typographyMixin(typographyTypes.GEIST_BODY_S_REGULAR)};\n color: ${colors[\"brown-100\"]};\n word-break: break-word;\n\n & h1 {\n ${typographyMixin(typographyTypes.GEIST_HEADING_S_BOLD)};\n margin-block: 20px;\n }\n\n & h2 {\n ${typographyMixin(typographyTypes.GEIST_BODY_L_SEMI_BOLD)};\n margin-block: 12px;\n }\n\n & h3,\n & h4,\n & h5,\n & h6 {\n ${typographyMixin(typographyTypes.GEIST_BODY_M_SEMI_BOLD)};\n margin-block: 8px;\n }\n\n & strong {\n ${typographyMixin(typographyTypes.GEIST_BODY_S_SEMI_BOLD)};\n }\n\n & p {\n margin-block: 0px;\n }\n\n & p + p {\n margin-block: 8px 0px;\n }\n\n & ul,\n & ol {\n margin-block: 16px;\n padding-left: 20px;\n }\n\n & li + li {\n margin-block: 4px;\n }\n\n & ol {\n list-style-type: decimal;\n }\n\n & ol ol {\n list-style-type: lower-alpha;\n }\n\n & ol ol ol {\n list-style-type: lower-roman;\n }\n\n & a {\n color: inherit;\n text-decoration-line: underline;\n text-decoration-style: dotted;\n text-decoration-thickness: auto;\n text-underline-offset: auto;\n text-underline-position: from-font;\n transition: color 75ms ease-in-out;\n\n &:hover {\n color: ${colors[\"blue-600\"]};\n }\n }\n\n & hr {\n border: none;\n border-top: 1px solid ${colors[\"brown-40\"]};\n margin-block: 20px;\n }\n\n & table {\n margin: 0;\n width: 100%;\n border-collapse: collapse;\n }\n\n & th,\n & td {\n border: none;\n border-bottom: 1px solid ${colors[\"brown-40\"]};\n text-align: left;\n }\n\n & th {\n ${typographyMixin(typographyTypes.GEIST_LABEL_CAPTION_MEDIUM)};\n padding: 8px;\n }\n\n & td {\n ${typographyMixin(typographyTypes.GEIST_BODY_XS_MEDIUM)};\n padding: 16px 8px;\n min-width: 150px;\n width: 1%;\n }\n\n & td strong {\n font-weight: inherit;\n font-size: inherit;\n line-height: inherit;\n }\n\n & blockquote {\n margin-block: 4px;\n margin-inline: 0px;\n padding-left: 16px;\n border-left: 4px solid ${colors[\"brown-40\"]};\n color: ${colors[\"brown-80\"]};\n }\n`;\n","import { TextBlock } from \"../text-block/text-block\";\nimport { blockTypes } from \"../types\";\nimport type { BlockService, TextBlockRecord } from \"../types\";\n\nexport const TextBlockService: BlockService<TextBlockRecord> = {\n type: blockTypes.TEXT,\n Component: TextBlock,\n};\n","import type { BlockService, BlockType } from \"../types\";\nimport { TextBlockService } from \"./text-block-service\";\n\nexport const BlockServices: Record<BlockType, BlockService> = {\n [TextBlockService.type]: TextBlockService,\n};\n","import type { ReactNode } from \"react\";\n\nimport { BlockServices } from \"../block-services\";\nimport type { BlockRecord } from \"../types\";\n\n/** Props for the Block dispatcher. */\ninterface BlockProps {\n /** The block to render. Dispatched to the registered concrete component for its `type`. */\n block: BlockRecord;\n}\n\n/** Dispatches a single block to its registered concrete component. Unknown types render as a no-op. */\nexport function Block({ block }: BlockProps): ReactNode {\n const service = BlockServices[block.type];\n if (!service) return null;\n const { Component } = service;\n return <Component block={block} />;\n}\n"],"mappings":"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","names":["styled","styled","jsx","jsx"]}
|