c063 1.4.9 → 1.6.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.
@@ -11,6 +11,6 @@ import { CodeBlockProps } from "../types/index";
11
11
  * @returns JSX 元素,呈現語法高亮的程式碼區塊
12
12
  */
13
13
  export declare const CodeBlock: {
14
- <T extends React.ElementType = "span">({ tokenLines, showLineNumbers, lineNumberStyle, theme, ...rest }: CodeBlockProps<T>): import("react/jsx-runtime").JSX.Element;
14
+ <T extends React.ElementType = "span">({ tokenLines, showLineNumbers, lineNumberStyle, autoWrap, theme, ...rest }: CodeBlockProps<T>): import("react/jsx-runtime").JSX.Element;
15
15
  displayName: string;
16
16
  };
@@ -11,14 +11,15 @@ import { CodeLine } from "./CodeLine";
11
11
  * @param rest 其他傳遞給 <pre> 的屬性
12
12
  * @returns JSX 元素,呈現語法高亮的程式碼區塊
13
13
  */
14
- export const CodeBlock = ({ tokenLines, showLineNumbers = true, lineNumberStyle, theme, ...rest }) => {
15
- return (_jsx("pre", { ...rest, children: tokenLines.map((line, index) => (
16
- // eslint-disable-next-line react/react-in-jsx-scope
17
- _jsxs("div", { style: {
18
- display: "flex",
19
- flexWrap: "nowrap",
20
- width: "100%",
21
- gap: "0.5rem",
22
- }, children: [showLineNumbers && (_jsx("span", { style: { color: "#888", userSelect: "none", ...lineNumberStyle }, children: index + 1 })), _jsx(CodeLine, { theme: theme, tokens: line })] }, index))) }));
14
+ export const CodeBlock = ({ tokenLines, showLineNumbers = true, lineNumberStyle, autoWrap, theme, ...rest }) => {
15
+ return (_jsx("pre", { ...rest, style: { margin: 0, padding: 0, overflowX: "auto" }, children: _jsx("table", { style: { borderCollapse: "collapse", width: "100%" }, children: _jsx("tbody", { children: tokenLines.map((line, index) => (_jsxs("tr", { children: [showLineNumbers && (_jsx("td", { style: {
16
+ paddingInline: "0.5rem",
17
+ textAlign: "right",
18
+ whiteSpace: "pre",
19
+ fontVariantNumeric: "tabular-nums",
20
+ color: "#888",
21
+ userSelect: "none",
22
+ ...lineNumberStyle,
23
+ }, children: index + 1 })), _jsx("td", { style: { width: "100%" }, children: _jsx(CodeLine, { theme: theme, tokens: line, autoWrap: autoWrap }) })] }, index))) }) }) }));
23
24
  };
24
25
  CodeBlock.displayName = "CodeBlock";
@@ -6,10 +6,11 @@ import { CodeLineProps } from "../types/index";
6
6
  * @param props.tokens 該行所包含的語法 token 陣列
7
7
  * @param props.style 自訂樣式,會與 whiteSpace: pre-wrap 合併
8
8
  * @param props.theme 主題
9
+ * @param prop.autoWrap 是否自動換行
9
10
  * @param rest 其他 HTMLAttributes
10
11
  * @returns JSX 元素,呈現語法 token 的單行程式碼
11
12
  */
12
13
  export declare const CodeLine: {
13
- <T extends React.ElementType = "span">({ style, tokens, theme, ...rest }: CodeLineProps<T>): import("react/jsx-runtime").JSX.Element;
14
+ <T extends React.ElementType = "span">({ style, tokens, theme, autoWrap, ...rest }: CodeLineProps<T>): import("react/jsx-runtime").JSX.Element;
14
15
  displayName: string;
15
16
  };
@@ -7,12 +7,13 @@ import { CodeToken } from "./CodeToken";
7
7
  * @param props.tokens 該行所包含的語法 token 陣列
8
8
  * @param props.style 自訂樣式,會與 whiteSpace: pre-wrap 合併
9
9
  * @param props.theme 主題
10
+ * @param prop.autoWrap 是否自動換行
10
11
  * @param rest 其他 HTMLAttributes
11
12
  * @returns JSX 元素,呈現語法 token 的單行程式碼
12
13
  */
13
- export const CodeLine = ({ style, tokens, theme, ...rest }) => {
14
+ export const CodeLine = ({ style, tokens, theme, autoWrap = true, ...rest }) => {
14
15
  return (_jsx("code", { ...rest, style: {
15
- whiteSpace: "pre-wrap",
16
+ whiteSpace: autoWrap ? "pre-wrap" : "nowrap",
16
17
  ...style,
17
18
  }, children: tokens.map((token, index) => (_jsx(CodeToken, { theme: theme, ...token }, index))) }));
18
19
  };
@@ -7,7 +7,7 @@ import { AsComponentProps, OverrideProps } from "./common";
7
7
  *
8
8
  * 類型分為以下幾大類:
9
9
  *
10
- * - `keyword1` / `keyword2`: 關鍵字,如 `const`、`return`、`import` 等,分顏色類別。
10
+ * - `keyword1` / `keyword2`: 關鍵字,如 `const`/`return`、`import` 等,分顏色類別。
11
11
  * - `string`: 字串常值,如 `'text'`、`"value"`。
12
12
  * - `number`: 數字常值,如 `123`、`3.14`。
13
13
  * - `comment`: 註解內容,如 `//`。
@@ -71,6 +71,11 @@ export type CodeLineProps<T extends React.ElementType> = OverrideProps<React.HTM
71
71
  * @default "vscode-dark"
72
72
  */
73
73
  theme?: CodeTheme;
74
+ /**
75
+ * 是否自動換行
76
+ * @default true
77
+ */
78
+ autoWrap?: boolean;
74
79
  }>;
75
80
  export type CodeBlockProps<T extends React.ElementType> = OverrideProps<React.HTMLAttributes<HTMLPreElement>, {
76
81
  /**
@@ -80,13 +85,13 @@ export type CodeBlockProps<T extends React.ElementType> = OverrideProps<React.HT
80
85
  * ```tsx
81
86
  * <CodeBlock tokenLines={[
82
87
  * [
83
- * { type: "keyword-blue", children: "const" },
88
+ * { type: "keyword1", children: "const" },
84
89
  * { type: "variable", children: "x" },
85
90
  * { type: "operator", children: "=" },
86
91
  * { type: "number", children: "42" },
87
92
  * ],
88
93
  * [
89
- * { type: "keyword-purple", children: "return" },
94
+ * { type: "keyword2", children: "return" },
90
95
  * { type: "variable", children: "x" },
91
96
  * ],
92
97
  * ]} />
@@ -109,8 +114,13 @@ export type CodeBlockProps<T extends React.ElementType> = OverrideProps<React.HT
109
114
  lineNumberStyle?: React.CSSProperties;
110
115
  /**
111
116
  * 語法主題名稱。
112
- * @default "vscode-dark"
117
+ * @default "default-dark-modern"
113
118
  */
114
119
  theme?: CodeTheme;
120
+ /**
121
+ * 是否自動換行
122
+ * @default true
123
+ */
124
+ autoWrap?: boolean;
115
125
  }>;
116
126
  export type ParsableLanguage = (typeof parsableLanguages)[number];
@@ -1,31 +1,56 @@
1
1
  import React from "react";
2
2
  import { CodeTokenBuilder, CodeTokenProps, CodeTokenType } from "../types";
3
3
  /**
4
- * 語法 token 的建構器集合,每個 key 對應一種語法類型(如 `keyword-blue`, `string`, `comment` 等),
5
- * 透過 Proxy 生成對應的 `CodeTokenBuilder`。
4
+ * `c063` 是一組語法高亮 token 建構器集合。
5
+ * 每個 key 對應一種語法分類(如 `keyword1`, `string`, `comment` 等),
6
+ * 回傳對應的 `CodeTokenProps` 物件。
6
7
  *
7
- * 使用方式:
8
+ * @example
8
9
  * ```tsx
9
- * c063.keyword1("const") // -> { type: "keyword1", children: "const" }
10
- * c063.string("'hello'", { as: "code" }) // 可自訂 as 或其他 props
10
+ * const keyword = c063.keyword1("const");
11
+ * const str = c063.string("'Hello'", { as: "code" });
11
12
  * ```
12
13
  *
13
- * @example
14
- * tokens.push(c063.keyword1("const"));
15
- * tokens.push(c063.string("'Hello'"));
16
- *
17
- * @returns 一個以 `CodeTokenType` 為 key 的建構器函式集合
14
+ * @returns 以 `CodeTokenType` 為 key 的建構器函式集合。
18
15
  */
19
16
  declare const c063: Record<CodeTokenType, CodeTokenBuilder>;
17
+ export default c063;
20
18
  /**
21
- * 產生指定空白數量的 CodeToken,用於程式碼中的縮排或空格。
19
+ * 產生指定空白數量的 CodeToken,用於縮排、空格等用途。
22
20
  *
23
21
  * @param count 空白字元數,預設為 1
24
- * @returns `CodeTokenProps` 物件,type 為 "default",children 為空白字串
22
+ * @returns type 為 `"default"`、內容為空格的 `CodeTokenProps`
25
23
  *
26
24
  * @example
25
+ * ```tsx
27
26
  * tokens.push(whiteSpace(2)); // -> { type: "default", children: " " }
27
+ * ```
28
28
  */
29
29
  export declare const whiteSpace: (count?: number) => CodeTokenProps<"span">;
30
+ /**
31
+ * 抽取單個 `CodeTokenProps` 的純文字內容。
32
+ *
33
+ * @param token 要處理的 token
34
+ * @returns 對應的文字內容
35
+ *
36
+ * @example
37
+ * ```tsx
38
+ * extractTokenContent(c063.keyword1("return")); // => "return"
39
+ * ```
40
+ */
30
41
  export declare const extractTokenContent: <T extends React.ElementType>(token: CodeTokenProps<T>) => string;
31
- export default c063;
42
+ /**
43
+ * 判斷兩個 token 是否相等(type 與內容皆相同)。
44
+ *
45
+ * @param a 第一個 token
46
+ * @param b 第二個 token
47
+ * @returns 是否相等
48
+ */
49
+ export declare const isTokenEqual: <T extends React.ElementType>(a: CodeTokenProps<T>, b: CodeTokenProps<T>) => boolean;
50
+ /**
51
+ * 將 token 列表按語法類型分類。
52
+ *
53
+ * @param lines 二維陣列,每行為一組 token
54
+ * @returns 分組後的 token 映射,key 為 `CodeTokenType`
55
+ */
56
+ export declare const groupTokensByType: <T extends React.ElementType>(lines: CodeTokenProps<T>[][]) => Record<CodeTokenType, CodeTokenProps<T>[]>;
@@ -1,22 +1,26 @@
1
1
  import React from "react";
2
2
  /**
3
- * 語法 token 的建構器集合,每個 key 對應一種語法類型(如 `keyword-blue`, `string`, `comment` 等),
4
- * 透過 Proxy 生成對應的 `CodeTokenBuilder`。
3
+ * `c063` 是一組語法高亮 token 建構器集合。
4
+ * 每個 key 對應一種語法分類(如 `keyword1`, `string`, `comment` 等),
5
+ * 回傳對應的 `CodeTokenProps` 物件。
5
6
  *
6
- * 使用方式:
7
+ * @example
7
8
  * ```tsx
8
- * c063.keyword1("const") // -> { type: "keyword1", children: "const" }
9
- * c063.string("'hello'", { as: "code" }) // 可自訂 as 或其他 props
9
+ * const keyword = c063.keyword1("const");
10
+ * const str = c063.string("'Hello'", { as: "code" });
10
11
  * ```
11
12
  *
12
- * @example
13
- * tokens.push(c063.keyword1("const"));
14
- * tokens.push(c063.string("'Hello'"));
15
- *
16
- * @returns 一個以 `CodeTokenType` 為 key 的建構器函式集合
13
+ * @returns 以 `CodeTokenType` 為 key 的建構器函式集合。
17
14
  */
18
15
  const c063 = new Proxy({}, {
19
16
  get: (_, prop) => {
17
+ /**
18
+ * 建立指定語法類型的 CodeToken。
19
+ *
20
+ * @param children 要包裹的 React 內容或字串
21
+ * @param props 可選的額外屬性,如 `as` 或 `className`
22
+ * @returns 一個 CodeToken 物件
23
+ */
20
24
  const builder = (children, props) => {
21
25
  return {
22
26
  children,
@@ -27,39 +31,113 @@ const c063 = new Proxy({}, {
27
31
  return builder;
28
32
  },
29
33
  });
34
+ export default c063;
30
35
  /**
31
- * 產生指定空白數量的 CodeToken,用於程式碼中的縮排或空格。
36
+ * 產生指定空白數量的 CodeToken,用於縮排、空格等用途。
32
37
  *
33
38
  * @param count 空白字元數,預設為 1
34
- * @returns `CodeTokenProps` 物件,type 為 "default",children 為空白字串
39
+ * @returns type 為 `"default"`、內容為空格的 `CodeTokenProps`
35
40
  *
36
41
  * @example
42
+ * ```tsx
37
43
  * tokens.push(whiteSpace(2)); // -> { type: "default", children: " " }
44
+ * ```
38
45
  */
39
46
  export const whiteSpace = (count = 1) => c063.default(" ".repeat(count));
47
+ /**
48
+ * 遞迴抽取 ReactNode 中的純文字內容。
49
+ *
50
+ * @param children ReactNode,可以是字串、數字、JSX 元素、陣列等
51
+ * @returns 純文字內容字串
52
+ */
53
+ const _extractReactNode = (children) => {
54
+ if (typeof children === "string")
55
+ return children;
56
+ if (typeof children === "number")
57
+ return children.toString();
58
+ if (Array.isArray(children))
59
+ return children.map(_extractReactNode).join("");
60
+ if (React.isValidElement(children)) {
61
+ return _extractReactNode(children.props.children);
62
+ }
63
+ if (typeof children === "object" && children !== null) {
64
+ return React.Children.toArray(children).map(_extractReactNode).join("");
65
+ }
66
+ return ""; // 如果 children 是 null 或 undefined,則返回空字串
67
+ };
68
+ /**
69
+ * 抽取單個 `CodeTokenProps` 的純文字內容。
70
+ *
71
+ * @param token 要處理的 token
72
+ * @returns 對應的文字內容
73
+ *
74
+ * @example
75
+ * ```tsx
76
+ * extractTokenContent(c063.keyword1("return")); // => "return"
77
+ * ```
78
+ */
40
79
  export const extractTokenContent = (token) => {
41
- const _extract = (children) => {
42
- if (typeof children === "string")
43
- return children;
44
- if (typeof children === "number")
45
- return children.toString();
46
- if (Array.isArray(children))
47
- return children.map(_extract).join("");
48
- if (React.isValidElement(children)) {
49
- return _extract(children.props.children);
50
- }
51
- return ""; // 如果 children null 或 undefined,則返回空字串
80
+ return _extractReactNode(token.children);
81
+ };
82
+ /**
83
+ * 判斷兩個 token 是否相等(type 與內容皆相同)。
84
+ *
85
+ * @param a 第一個 token
86
+ * @param b 第二個 token
87
+ * @returns 是否相等
88
+ */
89
+ export const isTokenEqual = (a, b) => {
90
+ return a.type === b.type && extractTokenContent(a) === extractTokenContent(b);
91
+ };
92
+ /**
93
+ * 將 token 列表按語法類型分類。
94
+ *
95
+ * @param lines 二維陣列,每行為一組 token
96
+ * @returns 分組後的 token 映射,key 為 `CodeTokenType`
97
+ */
98
+ export const groupTokensByType = (lines) => {
99
+ var _a;
100
+ const grouped = {
101
+ keyword1: [],
102
+ keyword2: [],
103
+ function: [],
104
+ string: [],
105
+ number: [],
106
+ comment: [],
107
+ type: [],
108
+ variable: [],
109
+ constant: [],
110
+ brackets1: [],
111
+ brackets2: [],
112
+ brackets3: [],
113
+ operator: [],
114
+ default: [],
52
115
  };
53
- return _extract(token.children);
116
+ for (const token of lines.flat()) {
117
+ grouped[(_a = token.type) !== null && _a !== void 0 ? _a : "default"].push(token);
118
+ }
119
+ return grouped;
54
120
  };
55
- export default c063;
56
121
  /**
57
- * 待實現
122
+ * 待實作
123
+ * `parseTokens` 是語法解析器的代理集合,用來解析特定語言的程式碼字串。
124
+ *
125
+ * 每個 key 對應一種可解析語言(如 `"javascript"`、`"python"` 等),
126
+ * 傳入原始程式碼字串後,回傳解析後的 token 二維陣列(每行一組 token)。
127
+ *
128
+ *
129
+ * @example
130
+ * ```ts
131
+ * const tokens = parseTokens.javascript("const x = 1;");
132
+ * ```
133
+ *
134
+ * @returns 語法高亮用的 `CodeTokenProps` 二維陣列
58
135
  */
59
- const _parseTokens = new Proxy({}, {
136
+ const parseTokens = new Proxy({}, {
60
137
  get: (_, prop) => {
61
138
  const parser = (content) => {
62
- return [];
139
+ const result = [];
140
+ return result;
63
141
  };
64
142
  return parser;
65
143
  },
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "c063",
4
- "version": "1.4.9",
4
+ "version": "1.6.0",
5
5
  "description": "A React component for displaying code snippets with syntax highlighting.",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
@@ -1,5 +1,6 @@
1
1
  import { CodeBlockProps } from "../types/index";
2
2
  import { CodeLine } from "./CodeLine";
3
+
3
4
  /**
4
5
  * 顯示完整程式碼區塊,支援多行語法 token 與行號顯示。
5
6
  *
@@ -15,32 +16,38 @@ export const CodeBlock = <T extends React.ElementType = "span">({
15
16
  tokenLines,
16
17
  showLineNumbers = true,
17
18
  lineNumberStyle,
19
+ autoWrap,
18
20
  theme,
19
21
  ...rest
20
22
  }: CodeBlockProps<T>) => {
21
23
  return (
22
- <pre {...rest}>
23
- {tokenLines.map((line, index) => (
24
- // eslint-disable-next-line react/react-in-jsx-scope
25
- <div
26
- key={index}
27
- style={{
28
- display: "flex",
29
- flexWrap: "nowrap",
30
- width: "100%",
31
- gap: "0.5rem",
32
- }}
33
- >
34
- {showLineNumbers && (
35
- <span
36
- style={{ color: "#888", userSelect: "none", ...lineNumberStyle }}
37
- >
38
- {index + 1}
39
- </span>
40
- )}
41
- <CodeLine theme={theme} tokens={line} />
42
- </div>
43
- ))}
24
+ <pre {...rest} style={{ margin: 0, padding: 0, overflowX: "auto" }}>
25
+ <table style={{ borderCollapse: "collapse", width: "100%" }}>
26
+ <tbody>
27
+ {tokenLines.map((line, index) => (
28
+ <tr key={index}>
29
+ {showLineNumbers && (
30
+ <td
31
+ style={{
32
+ paddingInline: "0.5rem",
33
+ textAlign: "right",
34
+ whiteSpace: "pre",
35
+ fontVariantNumeric: "tabular-nums",
36
+ color: "#888",
37
+ userSelect: "none",
38
+ ...lineNumberStyle,
39
+ }}
40
+ >
41
+ {index + 1}
42
+ </td>
43
+ )}
44
+ <td style={{ width: "100%" }}>
45
+ <CodeLine theme={theme} tokens={line} autoWrap={autoWrap} />
46
+ </td>
47
+ </tr>
48
+ ))}
49
+ </tbody>
50
+ </table>
44
51
  </pre>
45
52
  );
46
53
  };
@@ -6,7 +6,8 @@ import { CodeToken } from "./CodeToken";
6
6
  * @template T 元件渲染類型,例如 <code>、<span> 等
7
7
  * @param props.tokens 該行所包含的語法 token 陣列
8
8
  * @param props.style 自訂樣式,會與 whiteSpace: pre-wrap 合併
9
- * @param props.theme 主題
9
+ * @param props.theme 主題
10
+ * @param prop.autoWrap 是否自動換行
10
11
  * @param rest 其他 HTMLAttributes
11
12
  * @returns JSX 元素,呈現語法 token 的單行程式碼
12
13
  */
@@ -14,13 +15,14 @@ export const CodeLine = <T extends React.ElementType = "span">({
14
15
  style,
15
16
  tokens,
16
17
  theme,
18
+ autoWrap = true,
17
19
  ...rest
18
20
  }: CodeLineProps<T>) => {
19
21
  return (
20
22
  <code
21
23
  {...rest}
22
24
  style={{
23
- whiteSpace: "pre-wrap",
25
+ whiteSpace: autoWrap ? "pre-wrap" : "nowrap",
24
26
  ...style,
25
27
  }}
26
28
  >
@@ -7,7 +7,7 @@ import { AsComponentProps, OverrideProps } from "./common";
7
7
  *
8
8
  * 類型分為以下幾大類:
9
9
  *
10
- * - `keyword1` / `keyword2`: 關鍵字,如 `const`、`return`、`import` 等,分顏色類別。
10
+ * - `keyword1` / `keyword2`: 關鍵字,如 `const`/`return`、`import` 等,分顏色類別。
11
11
  * - `string`: 字串常值,如 `'text'`、`"value"`。
12
12
  * - `number`: 數字常值,如 `123`、`3.14`。
13
13
  * - `comment`: 註解內容,如 `//`。
@@ -94,6 +94,12 @@ export type CodeLineProps<T extends React.ElementType> = OverrideProps<
94
94
  * @default "vscode-dark"
95
95
  */
96
96
  theme?: CodeTheme;
97
+
98
+ /**
99
+ * 是否自動換行
100
+ * @default true
101
+ */
102
+ autoWrap?: boolean;
97
103
  }
98
104
  >;
99
105
 
@@ -107,13 +113,13 @@ export type CodeBlockProps<T extends React.ElementType> = OverrideProps<
107
113
  * ```tsx
108
114
  * <CodeBlock tokenLines={[
109
115
  * [
110
- * { type: "keyword-blue", children: "const" },
116
+ * { type: "keyword1", children: "const" },
111
117
  * { type: "variable", children: "x" },
112
118
  * { type: "operator", children: "=" },
113
119
  * { type: "number", children: "42" },
114
120
  * ],
115
121
  * [
116
- * { type: "keyword-purple", children: "return" },
122
+ * { type: "keyword2", children: "return" },
117
123
  * { type: "variable", children: "x" },
118
124
  * ],
119
125
  * ]} />
@@ -137,9 +143,15 @@ export type CodeBlockProps<T extends React.ElementType> = OverrideProps<
137
143
  lineNumberStyle?: React.CSSProperties;
138
144
  /**
139
145
  * 語法主題名稱。
140
- * @default "vscode-dark"
146
+ * @default "default-dark-modern"
141
147
  */
142
148
  theme?: CodeTheme;
149
+
150
+ /**
151
+ * 是否自動換行
152
+ * @default true
153
+ */
154
+ autoWrap?: boolean;
143
155
  }
144
156
  >;
145
157
 
@@ -7,28 +7,32 @@ import {
7
7
  } from "../types";
8
8
 
9
9
  /**
10
- * 語法 token 的建構器集合,每個 key 對應一種語法類型(如 `keyword-blue`, `string`, `comment` 等),
11
- * 透過 Proxy 生成對應的 `CodeTokenBuilder`。
10
+ * `c063` 是一組語法高亮 token 建構器集合。
11
+ * 每個 key 對應一種語法分類(如 `keyword1`, `string`, `comment` 等),
12
+ * 回傳對應的 `CodeTokenProps` 物件。
12
13
  *
13
- * 使用方式:
14
+ * @example
14
15
  * ```tsx
15
- * c063.keyword1("const") // -> { type: "keyword1", children: "const" }
16
- * c063.string("'hello'", { as: "code" }) // 可自訂 as 或其他 props
16
+ * const keyword = c063.keyword1("const");
17
+ * const str = c063.string("'Hello'", { as: "code" });
17
18
  * ```
18
19
  *
19
- * @example
20
- * tokens.push(c063.keyword1("const"));
21
- * tokens.push(c063.string("'Hello'"));
22
- *
23
- * @returns 一個以 `CodeTokenType` 為 key 的建構器函式集合
20
+ * @returns 以 `CodeTokenType` 為 key 的建構器函式集合。
24
21
  */
25
22
  const c063 = new Proxy(
26
23
  {},
27
24
  {
28
25
  get: (_, prop: CodeTokenType) => {
26
+ /**
27
+ * 建立指定語法類型的 CodeToken。
28
+ *
29
+ * @param children 要包裹的 React 內容或字串
30
+ * @param props 可選的額外屬性,如 `as` 或 `className`
31
+ * @returns 一個 CodeToken 物件
32
+ */
29
33
  const builder = <T extends React.ElementType = "span">(
30
34
  children: React.ReactNode,
31
- props: CodeTokenProps<T>
35
+ props?: CodeTokenProps<T>
32
36
  ) => {
33
37
  return {
34
38
  children,
@@ -40,45 +44,129 @@ const c063 = new Proxy(
40
44
  },
41
45
  }
42
46
  ) as Record<CodeTokenType, CodeTokenBuilder>;
47
+ export default c063;
43
48
 
44
49
  /**
45
- * 產生指定空白數量的 CodeToken,用於程式碼中的縮排或空格。
50
+ * 產生指定空白數量的 CodeToken,用於縮排、空格等用途。
46
51
  *
47
52
  * @param count 空白字元數,預設為 1
48
- * @returns `CodeTokenProps` 物件,type 為 "default",children 為空白字串
53
+ * @returns type 為 `"default"`、內容為空格的 `CodeTokenProps`
49
54
  *
50
55
  * @example
56
+ * ```tsx
51
57
  * tokens.push(whiteSpace(2)); // -> { type: "default", children: " " }
58
+ * ```
52
59
  */
53
60
  export const whiteSpace = (count: number = 1): CodeTokenProps<"span"> =>
54
61
  c063.default(" ".repeat(count));
55
62
 
63
+ /**
64
+ * 遞迴抽取 ReactNode 中的純文字內容。
65
+ *
66
+ * @param children ReactNode,可以是字串、數字、JSX 元素、陣列等
67
+ * @returns 純文字內容字串
68
+ */
69
+ const _extractReactNode = (children: React.ReactNode): string => {
70
+ if (typeof children === "string") return children;
71
+ if (typeof children === "number") return children.toString();
72
+ if (Array.isArray(children)) return children.map(_extractReactNode).join("");
73
+ if (React.isValidElement(children)) {
74
+ return _extractReactNode((children as React.JSX.Element).props.children);
75
+ }
76
+ if (typeof children === "object" && children !== null) {
77
+ return React.Children.toArray(children).map(_extractReactNode).join("");
78
+ }
79
+
80
+ return ""; // 如果 children 是 null 或 undefined,則返回空字串
81
+ };
82
+
83
+ /**
84
+ * 抽取單個 `CodeTokenProps` 的純文字內容。
85
+ *
86
+ * @param token 要處理的 token
87
+ * @returns 對應的文字內容
88
+ *
89
+ * @example
90
+ * ```tsx
91
+ * extractTokenContent(c063.keyword1("return")); // => "return"
92
+ * ```
93
+ */
56
94
  export const extractTokenContent = <T extends React.ElementType>(
57
95
  token: CodeTokenProps<T>
58
96
  ): string => {
59
- const _extract = (children: React.ReactNode): string => {
60
- if (typeof children === "string") return children;
61
- if (typeof children === "number") return children.toString();
62
- if (Array.isArray(children)) return children.map(_extract).join("");
63
- if (React.isValidElement(children)) {
64
- return _extract((children as React.JSX.Element).props.children);
65
- }
66
- return ""; // 如果 children 是 null 或 undefined,則返回空字串
67
- };
68
- return _extract(token.children);
97
+ return _extractReactNode(token.children);
69
98
  };
70
99
 
71
- export default c063;
100
+ /**
101
+ * 判斷兩個 token 是否相等(type 與內容皆相同)。
102
+ *
103
+ * @param a 第一個 token
104
+ * @param b 第二個 token
105
+ * @returns 是否相等
106
+ */
107
+ export const isTokenEqual = <T extends React.ElementType>(
108
+ a: CodeTokenProps<T>,
109
+ b: CodeTokenProps<T>
110
+ ): boolean => {
111
+ return a.type === b.type && extractTokenContent(a) === extractTokenContent(b);
112
+ };
72
113
 
73
114
  /**
74
- * 待實現
115
+ * 將 token 列表按語法類型分類。
116
+ *
117
+ * @param lines 二維陣列,每行為一組 token
118
+ * @returns 分組後的 token 映射,key 為 `CodeTokenType`
75
119
  */
76
- const _parseTokens = new Proxy(
120
+ export const groupTokensByType = <T extends React.ElementType>(
121
+ lines: CodeTokenProps<T>[][]
122
+ ): Record<CodeTokenType, CodeTokenProps<T>[]> => {
123
+ const grouped: Record<CodeTokenType, CodeTokenProps<T>[]> = {
124
+ keyword1: [],
125
+ keyword2: [],
126
+ function: [],
127
+ string: [],
128
+ number: [],
129
+ comment: [],
130
+ type: [],
131
+ variable: [],
132
+ constant: [],
133
+ brackets1: [],
134
+ brackets2: [],
135
+ brackets3: [],
136
+ operator: [],
137
+ default: [],
138
+ };
139
+
140
+ for (const token of lines.flat()) {
141
+ grouped[token.type ?? "default"].push(token);
142
+ }
143
+
144
+ return grouped;
145
+ };
146
+
147
+ /**
148
+ * 待實作
149
+ * `parseTokens` 是語法解析器的代理集合,用來解析特定語言的程式碼字串。
150
+ *
151
+ * 每個 key 對應一種可解析語言(如 `"javascript"`、`"python"` 等),
152
+ * 傳入原始程式碼字串後,回傳解析後的 token 二維陣列(每行一組 token)。
153
+ *
154
+ *
155
+ * @example
156
+ * ```ts
157
+ * const tokens = parseTokens.javascript("const x = 1;");
158
+ * ```
159
+ *
160
+ * @returns 語法高亮用的 `CodeTokenProps` 二維陣列
161
+ */
162
+ const parseTokens = new Proxy(
77
163
  {},
78
164
  {
79
165
  get: (_, prop: ParsableLanguage) => {
80
166
  const parser = (content: string): CodeTokenProps<"span">[][] => {
81
- return [];
167
+ const result: CodeTokenProps<"span">[][] = [];
168
+
169
+ return result;
82
170
  };
83
171
  return parser;
84
172
  },