@hackersheet/next-document-content-components 0.1.0-alpha.2 → 0.1.0-alpha.21
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/cjs/components/code-block/code-block-copy-button.d.ts +13 -2
- package/dist/cjs/components/code-block/code-block-copy-button.js +2 -2
- package/dist/cjs/components/code-block/code-block-icon.d.ts +38 -4
- package/dist/cjs/components/code-block/code-block-icon.js +12 -0
- package/dist/cjs/components/code-block/code-block.d.ts +1 -1
- package/dist/cjs/components/code-block/code-block.js +3 -9
- package/dist/cjs/components/code-block/directory-tree-item.d.ts +15 -0
- package/dist/cjs/components/code-block/directory-tree-item.js +38 -0
- package/dist/cjs/components/code-block/directory-tree.d.ts +13 -0
- package/dist/cjs/components/code-block/{code-block-kifu.js → directory-tree.js} +12 -35
- package/dist/cjs/components/code-block/parse-tree-output.d.ts +26 -0
- package/dist/cjs/components/code-block/parse-tree-output.js +111 -0
- package/dist/cjs/components/code-block/shiki.js +25 -7
- package/dist/cjs/components/gist/gist.d.ts +6 -0
- package/dist/cjs/components/{index.js → gist/gist.js} +26 -27
- package/dist/cjs/components/heading/heading.d.ts +6 -0
- package/dist/cjs/components/{kifu-to/kifu-to.js → heading/heading.js} +7 -9
- package/dist/cjs/components/image/image.js +1 -2
- package/dist/cjs/components/link/link.js +1 -1
- package/dist/cjs/components/link-card/link-card.js +1 -2
- package/dist/cjs/components/mermaid/mermaid.d.ts +6 -0
- package/dist/cjs/components/{code-block/code-block-mermaid.js → mermaid/mermaid.js} +9 -8
- package/dist/cjs/components/youtube/youtube.js +1 -1
- package/dist/cjs/index.d.ts +4 -1
- package/dist/cjs/index.js +48 -5
- package/dist/esm/components/code-block/code-block-copy-button.d.mts +13 -2
- package/dist/esm/components/code-block/code-block-copy-button.mjs +2 -2
- package/dist/esm/components/code-block/code-block-icon.d.mts +38 -4
- package/dist/esm/components/code-block/code-block-icon.mjs +25 -1
- package/dist/esm/components/code-block/code-block.d.mts +1 -1
- package/dist/esm/components/code-block/code-block.mjs +3 -9
- package/dist/esm/components/code-block/directory-tree-item.d.mts +15 -0
- package/dist/esm/components/code-block/directory-tree-item.mjs +8 -0
- package/dist/esm/components/code-block/directory-tree.d.mts +13 -0
- package/dist/esm/components/code-block/directory-tree.mjs +13 -0
- package/dist/esm/components/code-block/parse-tree-output.d.mts +26 -0
- package/dist/esm/components/code-block/parse-tree-output.mjs +87 -0
- package/dist/esm/components/code-block/shiki.mjs +21 -8
- package/dist/esm/components/gist/gist.d.mts +6 -0
- package/dist/esm/components/gist/gist.mjs +25 -0
- package/dist/esm/components/heading/heading.d.mts +6 -0
- package/dist/esm/components/heading/heading.mjs +9 -0
- package/dist/esm/components/image/image.mjs +1 -2
- package/dist/esm/components/link/link.mjs +1 -1
- package/dist/esm/components/link-card/link-card.mjs +1 -2
- package/dist/esm/components/mermaid/mermaid.d.mts +6 -0
- package/dist/esm/components/{code-block/code-block-mermaid.mjs → mermaid/mermaid.mjs} +6 -5
- package/dist/esm/components/youtube/youtube.mjs +1 -1
- package/dist/esm/index.d.mts +4 -1
- package/dist/esm/index.mjs +22 -1
- package/package.json +23 -36
- package/dist/cjs/components/code-block/code-block-kifu.d.ts +0 -9
- package/dist/cjs/components/code-block/code-block-mermaid.d.ts +0 -8
- package/dist/cjs/components/index.d.ts +0 -9
- package/dist/cjs/components/kifu-to/kifu-to.d.ts +0 -6
- package/dist/esm/components/code-block/code-block-kifu.d.mts +0 -9
- package/dist/esm/components/code-block/code-block-kifu.mjs +0 -36
- package/dist/esm/components/code-block/code-block-mermaid.d.mts +0 -8
- package/dist/esm/components/index.d.mts +0 -9
- package/dist/esm/components/index.mjs +0 -16
- package/dist/esm/components/kifu-to/kifu-to.d.mts +0 -6
- package/dist/esm/components/kifu-to/kifu-to.mjs +0 -11
- package/dist/style.module.css +0 -280
- package/dist/style.module.scss.d.ts +0 -9
|
@@ -27,11 +27,11 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
27
27
|
mod
|
|
28
28
|
));
|
|
29
29
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
30
|
-
var
|
|
31
|
-
__export(
|
|
32
|
-
default: () =>
|
|
30
|
+
var mermaid_exports = {};
|
|
31
|
+
__export(mermaid_exports, {
|
|
32
|
+
default: () => Mermaid
|
|
33
33
|
});
|
|
34
|
-
module.exports = __toCommonJS(
|
|
34
|
+
module.exports = __toCommonJS(mermaid_exports);
|
|
35
35
|
var import_crypto = require("crypto");
|
|
36
36
|
var import_mermaid = __toESM(require("mermaid"));
|
|
37
37
|
var import_next_themes = require("next-themes");
|
|
@@ -41,7 +41,7 @@ function createId(code) {
|
|
|
41
41
|
hash.update(code);
|
|
42
42
|
return "id-" + hash.digest("hex");
|
|
43
43
|
}
|
|
44
|
-
function
|
|
44
|
+
function Mermaid({ code }) {
|
|
45
45
|
const ref = (0, import_react.useRef)(null);
|
|
46
46
|
const [mounted, setMounted] = (0, import_react.useState)(false);
|
|
47
47
|
const [svg, setSvg] = (0, import_react.useState)("");
|
|
@@ -65,7 +65,8 @@ function CodeBlockMermaid({ code }) {
|
|
|
65
65
|
};
|
|
66
66
|
renderMermaid();
|
|
67
67
|
}, [mounted, code, id, theme, systemTheme, setSvg, setMounted]);
|
|
68
|
-
if (!mounted)
|
|
69
|
-
return /* @__PURE__ */ import_react.default.createElement("div",
|
|
70
|
-
|
|
68
|
+
if (!mounted) {
|
|
69
|
+
return /* @__PURE__ */ import_react.default.createElement("div", { className: "mermaid-block mermaid-loading" }, /* @__PURE__ */ import_react.default.createElement("div", null, "Loading..."));
|
|
70
|
+
}
|
|
71
|
+
return /* @__PURE__ */ import_react.default.createElement("div", { className: "mermaid-block", dangerouslySetInnerHTML: { __html: svg }, ref });
|
|
71
72
|
}
|
|
@@ -35,5 +35,5 @@ var import_google = require("@next/third-parties/google");
|
|
|
35
35
|
var import_react = __toESM(require("react"));
|
|
36
36
|
async function Youtube({ videoId, params, playLabel, ...props }) {
|
|
37
37
|
const paramsString = params ? Object.entries(params).filter(([, value]) => value !== void 0).map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`).join("&") : void 0;
|
|
38
|
-
return /* @__PURE__ */ import_react.default.createElement(import_google.YouTubeEmbed, { ...props, videoid: videoId, params: paramsString, playlabel: playLabel });
|
|
38
|
+
return /* @__PURE__ */ import_react.default.createElement("div", { className: "youtube-block" }, /* @__PURE__ */ import_react.default.createElement(import_google.YouTubeEmbed, { ...props, videoid: videoId, params: paramsString, playlabel: playLabel }));
|
|
39
39
|
}
|
package/dist/cjs/index.d.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
export { default as CodeBlock } from './components/code-block/code-block.js';
|
|
2
|
+
export { default as DirectoryTree } from './components/code-block/directory-tree.js';
|
|
3
|
+
export { default as Gist } from './components/gist/gist.js';
|
|
4
|
+
export { default as Heading } from './components/heading/heading.js';
|
|
2
5
|
export { default as Image } from './components/image/image.js';
|
|
3
|
-
export { default as KifuTo } from './components/kifu-to/kifu-to.js';
|
|
4
6
|
export { default as Link } from './components/link/link.js';
|
|
5
7
|
export { default as LinkCard } from './components/link-card/link-card.js';
|
|
8
|
+
export { default as Mermaid } from './components/mermaid/mermaid.js';
|
|
6
9
|
export { default as XPost } from './components/x-post/x-post.js';
|
|
7
10
|
export { default as Youtube } from './components/youtube/youtube.js';
|
|
8
11
|
import 'react';
|
package/dist/cjs/index.js
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
6
12
|
var __copyProps = (to, from, except, desc) => {
|
|
7
13
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
8
14
|
for (let key of __getOwnPropNames(from))
|
|
@@ -11,12 +17,49 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
11
17
|
}
|
|
12
18
|
return to;
|
|
13
19
|
};
|
|
14
|
-
var
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
15
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
16
|
-
var
|
|
17
|
-
|
|
18
|
-
|
|
29
|
+
var index_exports = {};
|
|
30
|
+
__export(index_exports, {
|
|
31
|
+
CodeBlock: () => import_code_block.default,
|
|
32
|
+
DirectoryTree: () => import_directory_tree.default,
|
|
33
|
+
Gist: () => import_gist.default,
|
|
34
|
+
Heading: () => import_heading.default,
|
|
35
|
+
Image: () => import_image.default,
|
|
36
|
+
Link: () => import_link.default,
|
|
37
|
+
LinkCard: () => import_link_card.default,
|
|
38
|
+
Mermaid: () => import_mermaid.default,
|
|
39
|
+
XPost: () => import_x_post.default,
|
|
40
|
+
Youtube: () => import_youtube.default
|
|
41
|
+
});
|
|
42
|
+
module.exports = __toCommonJS(index_exports);
|
|
43
|
+
var import_code_block = __toESM(require("./components/code-block/code-block"));
|
|
44
|
+
var import_directory_tree = __toESM(require("./components/code-block/directory-tree"));
|
|
45
|
+
var import_gist = __toESM(require("./components/gist/gist"));
|
|
46
|
+
var import_heading = __toESM(require("./components/heading/heading"));
|
|
47
|
+
var import_image = __toESM(require("./components/image/image"));
|
|
48
|
+
var import_link = __toESM(require("./components/link/link"));
|
|
49
|
+
var import_link_card = __toESM(require("./components/link-card/link-card"));
|
|
50
|
+
var import_mermaid = __toESM(require("./components/mermaid/mermaid"));
|
|
51
|
+
var import_x_post = __toESM(require("./components/x-post/x-post"));
|
|
52
|
+
var import_youtube = __toESM(require("./components/youtube/youtube"));
|
|
19
53
|
// Annotate the CommonJS export names for ESM import in node:
|
|
20
54
|
0 && (module.exports = {
|
|
21
|
-
|
|
55
|
+
CodeBlock,
|
|
56
|
+
DirectoryTree,
|
|
57
|
+
Gist,
|
|
58
|
+
Heading,
|
|
59
|
+
Image,
|
|
60
|
+
Link,
|
|
61
|
+
LinkCard,
|
|
62
|
+
Mermaid,
|
|
63
|
+
XPost,
|
|
64
|
+
Youtube
|
|
22
65
|
});
|
|
@@ -1,8 +1,19 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
type CodeBlockCopyButtonProps = {
|
|
4
4
|
code: string;
|
|
5
|
-
}
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* コードブロック用のコピー ボタンコンポーネント。
|
|
8
|
+
*
|
|
9
|
+
* 与えられた code をクリップボードに書き込み、コピー完了時にアイコンをチェックに切り替えます。
|
|
10
|
+
*
|
|
11
|
+
* @param props.code コピー対象のコード文字列(Shiki 注釈が含まれている可能性あり)。
|
|
12
|
+
* @returns コピー用ボタンの React 要素。
|
|
13
|
+
*
|
|
14
|
+
* @remarks
|
|
15
|
+
* ブラウザの navigator.clipboard を使用します。ユーザーの環境によっては権限や HTTPS が必要です。
|
|
16
|
+
*/
|
|
6
17
|
declare function CodeBlockCopyButton({ code }: CodeBlockCopyButtonProps): React.JSX.Element;
|
|
7
18
|
|
|
8
19
|
export { type CodeBlockCopyButtonProps, CodeBlockCopyButton as default };
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import React, { useState } from "react";
|
|
3
3
|
import { HiOutlineClipboardDocumentList, HiCheck } from "react-icons/hi2";
|
|
4
|
+
const removeShikiCode = (code) => code.replace(/ *\/\/.*\[!code[^\]]+\]/gm, "").trim();
|
|
4
5
|
function CodeBlockCopyButton({ code }) {
|
|
5
6
|
const [copied, setCopied] = useState(false);
|
|
6
|
-
const removeShikiCode = (code2) => code2.replace(/ *\/\/.*\[!code[^\]]+\]/gm, "").trim();
|
|
7
7
|
const handleClick = () => {
|
|
8
8
|
navigator.clipboard.writeText(removeShikiCode(code)).then(() => {
|
|
9
9
|
setCopied(true);
|
|
10
10
|
setTimeout(() => setCopied(false), 1e3);
|
|
11
11
|
});
|
|
12
12
|
};
|
|
13
|
-
return /* @__PURE__ */ React.createElement("button", { onClick: handleClick }, copied ? /* @__PURE__ */ React.createElement(HiCheck, { size: 18 }) : /* @__PURE__ */ React.createElement(HiOutlineClipboardDocumentList, { size: 18 }));
|
|
13
|
+
return /* @__PURE__ */ React.createElement("button", { onClick: handleClick, "aria-label": "Copy code to clipboard" }, copied ? /* @__PURE__ */ React.createElement(HiCheck, { size: 18 }) : /* @__PURE__ */ React.createElement(HiOutlineClipboardDocumentList, { size: 18 }));
|
|
14
14
|
}
|
|
15
15
|
export {
|
|
16
16
|
CodeBlockCopyButton as default
|
|
@@ -1,8 +1,42 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { JSX } from 'react';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* CodeBlockIcon コンポーネントの props 型定義。
|
|
5
|
+
*
|
|
6
|
+
* `language` プロパティに基づいて、コードブロックに表示する代表的なアイコンを返します。
|
|
7
|
+
* 小文字の言語識別子(例: `"typescript"`, `"bash"`, `"json"`, `"markdown"`)を想定しています。
|
|
8
|
+
* 未知の値やサポート外の値は汎用のコードアイコンにフォールバックします。
|
|
9
|
+
*/
|
|
10
|
+
type CodeBlockIconProps = {
|
|
11
|
+
/**
|
|
12
|
+
* アイコン選択に用いる言語識別子(小文字推奨)。例: 'typescript', 'bash', 'json'
|
|
13
|
+
*/
|
|
4
14
|
language: string;
|
|
5
|
-
}
|
|
6
|
-
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* 指定された言語に対応するアイコンを返すコンポーネント。
|
|
18
|
+
*
|
|
19
|
+
* 共通の言語識別子を `react-icons` のアイコンにマッピングします。
|
|
20
|
+
* 認識できない言語は汎用の括弧(コード)アイコンに置き換わります。
|
|
21
|
+
*
|
|
22
|
+
* 使用例:
|
|
23
|
+
*
|
|
24
|
+
* ```tsx
|
|
25
|
+
* <CodeBlockIcon language="typescript" />
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* サポート例(非網羅):
|
|
29
|
+
* - 'bash' | 'sh' -> ターミナルアイコン
|
|
30
|
+
* - 'typescript' -> TypeScript アイコン
|
|
31
|
+
* - 'tsx' -> React アイコン
|
|
32
|
+
* - 'json' -> JSON アイコン
|
|
33
|
+
* - 'markdown' -> Markdown アイコン
|
|
34
|
+
* - 'hcl' -> Terraform アイコン
|
|
35
|
+
* - 'php', 'ruby', 'yaml', 'text' など
|
|
36
|
+
*
|
|
37
|
+
* @param props.language アイコン選択に用いる小文字の言語名
|
|
38
|
+
* @returns 選択されたアイコンを含む JSX 要素
|
|
39
|
+
*/
|
|
40
|
+
declare function CodeBlockIcon({ language }: CodeBlockIconProps): JSX.Element;
|
|
7
41
|
|
|
8
42
|
export { type CodeBlockIconProps, CodeBlockIcon as default };
|
|
@@ -1,7 +1,19 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { FaTerminal, FaReact } from "react-icons/fa6";
|
|
3
3
|
import { HiCodeBracket } from "react-icons/hi2";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
SiTypescript,
|
|
6
|
+
SiTerraform,
|
|
7
|
+
SiMarkdown,
|
|
8
|
+
SiPhp,
|
|
9
|
+
SiRuby,
|
|
10
|
+
SiYaml,
|
|
11
|
+
SiJavascript,
|
|
12
|
+
SiPython,
|
|
13
|
+
SiKotlin,
|
|
14
|
+
SiGo,
|
|
15
|
+
SiRust
|
|
16
|
+
} from "react-icons/si";
|
|
5
17
|
import { TbTxt } from "react-icons/tb";
|
|
6
18
|
import { VscJson } from "react-icons/vsc";
|
|
7
19
|
function CodeBlockIcon({ language }) {
|
|
@@ -9,6 +21,18 @@ function CodeBlockIcon({ language }) {
|
|
|
9
21
|
case "sh":
|
|
10
22
|
case "bash":
|
|
11
23
|
return /* @__PURE__ */ React.createElement(FaTerminal, null);
|
|
24
|
+
case "js":
|
|
25
|
+
case "javascript":
|
|
26
|
+
return /* @__PURE__ */ React.createElement(SiJavascript, null);
|
|
27
|
+
case "py":
|
|
28
|
+
case "python":
|
|
29
|
+
return /* @__PURE__ */ React.createElement(SiPython, null);
|
|
30
|
+
case "kotlin":
|
|
31
|
+
return /* @__PURE__ */ React.createElement(SiKotlin, null);
|
|
32
|
+
case "go":
|
|
33
|
+
return /* @__PURE__ */ React.createElement(SiGo, null);
|
|
34
|
+
case "rust":
|
|
35
|
+
return /* @__PURE__ */ React.createElement(SiRust, null);
|
|
12
36
|
case "hcl":
|
|
13
37
|
return /* @__PURE__ */ React.createElement(SiTerraform, null);
|
|
14
38
|
case "typescript":
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { CodeBlockComponentProps } from '@hackersheet/react-document-content';
|
|
3
3
|
|
|
4
|
-
declare function CodeBlock({ code,
|
|
4
|
+
declare function CodeBlock({ code, ...props }: CodeBlockComponentProps): Promise<React.JSX.Element>;
|
|
5
5
|
|
|
6
6
|
export { CodeBlock as default };
|
|
@@ -1,16 +1,10 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import CodeBlockCopyButton from "./code-block-copy-button";
|
|
3
3
|
import CodeBlockIcon from "./code-block-icon";
|
|
4
|
-
import CodeBlockKifu from "./code-block-kifu";
|
|
5
|
-
import CodeBlockMermaid from "./code-block-mermaid";
|
|
6
4
|
import { highlighteCode } from "./shiki";
|
|
7
|
-
async function CodeBlock({ code,
|
|
8
|
-
const [
|
|
9
|
-
const
|
|
10
|
-
const isKifu = lang === "kifu";
|
|
11
|
-
if (isMermaid) return /* @__PURE__ */ React.createElement(CodeBlockMermaid, { code });
|
|
12
|
-
if (isKifu) return /* @__PURE__ */ React.createElement(CodeBlockKifu, { kifu: code, filename });
|
|
13
|
-
const html = await highlighteCode(code, lang);
|
|
5
|
+
async function CodeBlock({ code, ...props }) {
|
|
6
|
+
const [language, filename] = props.language.split(":");
|
|
7
|
+
const html = await highlighteCode(code, language);
|
|
14
8
|
return /* @__PURE__ */ React.createElement("div", { className: "code-block" }, /* @__PURE__ */ React.createElement("div", { className: "code-block-header" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(CodeBlockIcon, { language })), /* @__PURE__ */ React.createElement("div", { className: "code-block-filename" }, filename), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(CodeBlockCopyButton, { code }))), html && /* @__PURE__ */ React.createElement("div", { dangerouslySetInnerHTML: { __html: html } }), !html && /* @__PURE__ */ React.createElement("pre", null, code));
|
|
15
9
|
}
|
|
16
10
|
export {
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { TreeNode } from './parse-tree-output.mjs';
|
|
3
|
+
|
|
4
|
+
type DirectoryTreeItemProps = {
|
|
5
|
+
node: TreeNode;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* 単一のツリーノード(ディレクトリまたはファイル)をレンダリングするコンポーネント。
|
|
9
|
+
* 再帰的に自身を呼び出して子ノードを描画します。
|
|
10
|
+
*
|
|
11
|
+
* @param props.node - 表示対象の TreeNode
|
|
12
|
+
*/
|
|
13
|
+
declare function DirectoryTreeItem({ node }: DirectoryTreeItemProps): React.JSX.Element;
|
|
14
|
+
|
|
15
|
+
export { DirectoryTreeItem as default };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { LuFolder, LuFile } from "react-icons/lu";
|
|
3
|
+
function DirectoryTreeItem({ node }) {
|
|
4
|
+
return /* @__PURE__ */ React.createElement("li", { key: node.id, className: `${node.type === "directory" ? "directory-tree-directory" : "directory-tree-file"}` }, /* @__PURE__ */ React.createElement("div", { className: "directory-tree-node-content" }, /* @__PURE__ */ React.createElement("div", { className: "directory-tree-icon" }, node.type === "directory" && /* @__PURE__ */ React.createElement(LuFolder, null), node.type === "file" && /* @__PURE__ */ React.createElement(LuFile, null)), /* @__PURE__ */ React.createElement("div", null, node.name)), node.children && /* @__PURE__ */ React.createElement("ul", null, node.children.map((child) => /* @__PURE__ */ React.createElement(DirectoryTreeItem, { key: child.id, node: child }))));
|
|
5
|
+
}
|
|
6
|
+
export {
|
|
7
|
+
DirectoryTreeItem as default
|
|
8
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { DirectoryTreeComponentProps } from '@hackersheet/react-document-content';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* code ブロック内の `tree` コマンド出力をパースしてツリーをレンダリングするコンポーネント。
|
|
6
|
+
* ヘッダにはツリーアイコン、ファイル名(language のコロン以降)、コピー用ボタンを表示します。
|
|
7
|
+
*
|
|
8
|
+
* @param props.code - `tree` コマンドの出力テキスト
|
|
9
|
+
* @param props.language - 言語/ファイル名情報(形式: "xxx:filename")
|
|
10
|
+
*/
|
|
11
|
+
declare function DirectoryTree({ code, ...props }: DirectoryTreeComponentProps): Promise<React.JSX.Element>;
|
|
12
|
+
|
|
13
|
+
export { DirectoryTree as default };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { LuFolderTree } from "react-icons/lu";
|
|
3
|
+
import CodeBlockCopyButton from "./code-block-copy-button";
|
|
4
|
+
import DirectoryTreeItem from "./directory-tree-item";
|
|
5
|
+
import parseTreeOutput from "./parse-tree-output";
|
|
6
|
+
async function DirectoryTree({ code, ...props }) {
|
|
7
|
+
const [, filename] = props.language.split(":");
|
|
8
|
+
const treeNode = parseTreeOutput(code);
|
|
9
|
+
return /* @__PURE__ */ React.createElement("div", { className: "code-block" }, /* @__PURE__ */ React.createElement("div", { className: "code-block-header" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(LuFolderTree, null)), /* @__PURE__ */ React.createElement("div", { className: "code-block-filename" }, filename), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(CodeBlockCopyButton, { code }))), /* @__PURE__ */ React.createElement("div", null, treeNode ? /* @__PURE__ */ React.createElement("ul", { className: "directory-tree" }, /* @__PURE__ */ React.createElement(DirectoryTreeItem, { node: treeNode })) : /* @__PURE__ */ React.createElement("pre", null, code)));
|
|
10
|
+
}
|
|
11
|
+
export {
|
|
12
|
+
DirectoryTree as default
|
|
13
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
type TreeNode = {
|
|
2
|
+
id: string;
|
|
3
|
+
name: string;
|
|
4
|
+
type: 'file' | 'directory';
|
|
5
|
+
extension?: string;
|
|
6
|
+
level: number;
|
|
7
|
+
children?: TreeNode[];
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* `tree` コマンドの出力(box-drawing を含むテキスト)をパースして TreeNode のツリー構造を返します。
|
|
11
|
+
*
|
|
12
|
+
* @remarks
|
|
13
|
+
* - 入力は Linux/macOS の `tree` コマンドの標準出力を想定しています。
|
|
14
|
+
* - 行先頭の箱線文字(`│`, `├`, `└`, `─`)や 4 スペースインデントを解析して階層を決定します。
|
|
15
|
+
* - 最後に出力されるサマリ行(例: `3 directories, 5 files`)は自動的に除外します。
|
|
16
|
+
* - 子要素が存在する行のみをディレクトリと見なすルールを採用しています(子要素が無ければファイル)。
|
|
17
|
+
* - 解析中に例外が発生した場合は `null` を返します(呼び出し元でフォールバック処理を行ってください)。
|
|
18
|
+
*
|
|
19
|
+
* @param treeOutput - `tree` コマンドで得られたテキスト全体
|
|
20
|
+
* @returns 解析に成功した場合はルート `TreeNode`、失敗または空入力の場合は `null`
|
|
21
|
+
*
|
|
22
|
+
* @complexity O(n) - 入力行数 n に対して一度の走査で処理します(追加で逆順集計を行うためメモリは O(n))。
|
|
23
|
+
*/
|
|
24
|
+
declare function parseTreeOutput(treeOutput: string): TreeNode | null;
|
|
25
|
+
|
|
26
|
+
export { type TreeNode, parseTreeOutput as default, parseTreeOutput };
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { createHash } from "crypto";
|
|
2
|
+
function encryptSha256(str) {
|
|
3
|
+
const hash = createHash("sha256");
|
|
4
|
+
hash.update(str);
|
|
5
|
+
return hash.digest("hex");
|
|
6
|
+
}
|
|
7
|
+
function parseTreeOutput(treeOutput) {
|
|
8
|
+
const raw = treeOutput.replace(/\r/g, "").trim();
|
|
9
|
+
if (!raw) return null;
|
|
10
|
+
const lines = raw.split("\n");
|
|
11
|
+
const filteredLines = lines.filter(
|
|
12
|
+
(l) => !/^\s*(?:\d+\s+directories?,\s*\d+\s+files?|\d+\s+directories?|\d+\s+files?)\s*$/i.test(l)
|
|
13
|
+
);
|
|
14
|
+
const idPrefix = "tree-" + encryptSha256(raw) + "-";
|
|
15
|
+
const rootNode = {
|
|
16
|
+
id: idPrefix + "root",
|
|
17
|
+
name: "",
|
|
18
|
+
type: "directory",
|
|
19
|
+
level: 0,
|
|
20
|
+
children: []
|
|
21
|
+
};
|
|
22
|
+
const stack = [{ node: rootNode, level: 0 }];
|
|
23
|
+
const getExtension = (filename) => filename.match(/\.(\w+)$/)?.[1];
|
|
24
|
+
const cleanNodeName = (name) => name.replace(/^[\s│├└─]+/, "").trim();
|
|
25
|
+
try {
|
|
26
|
+
const treeLineRegex = /^(?<prefix>(?:│\s{3}|\s{4})*)(?<connector>├── |└── )?(?<name>.*)$/;
|
|
27
|
+
const lineInfos = filteredLines.map((ln) => {
|
|
28
|
+
const normalized = ln.replace(/\t/g, " ");
|
|
29
|
+
const m = normalized.match(treeLineRegex);
|
|
30
|
+
if (m && m.groups) {
|
|
31
|
+
const prefix = m.groups["prefix"] || "";
|
|
32
|
+
const connector = m.groups["connector"] || "";
|
|
33
|
+
const name = (m.groups["name"] || "").trim();
|
|
34
|
+
const groupMatches = prefix.match(/(?:│\s{3}|\s{4})/g);
|
|
35
|
+
const level = (groupMatches ? groupMatches.length : 0) + (connector ? 1 : 0);
|
|
36
|
+
return { rawLine: normalized, level, cleanedName: cleanNodeName(name) };
|
|
37
|
+
}
|
|
38
|
+
return { rawLine: normalized, level: 0, cleanedName: cleanNodeName(normalized.trim()) };
|
|
39
|
+
});
|
|
40
|
+
const reduceResult = lineInfos.reduceRight(
|
|
41
|
+
(acc, info) => ({
|
|
42
|
+
lastLevel: info.rawLine.trim() ? info.level : acc.lastLevel,
|
|
43
|
+
nextLevels: [acc.lastLevel, ...acc.nextLevels]
|
|
44
|
+
}),
|
|
45
|
+
{ lastLevel: null, nextLevels: [] }
|
|
46
|
+
);
|
|
47
|
+
const nextNonEmptyLevel = reduceResult.nextLevels;
|
|
48
|
+
lineInfos.forEach((info, index) => {
|
|
49
|
+
const level = info.level;
|
|
50
|
+
const cleanedLine = info.cleanedName;
|
|
51
|
+
if (level === 0 && rootNode.name === "") {
|
|
52
|
+
rootNode.name = cleanedLine;
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const nextLevelForLine = nextNonEmptyLevel[index];
|
|
56
|
+
const isDirectory = nextLevelForLine != null && nextLevelForLine > level;
|
|
57
|
+
const newNode = {
|
|
58
|
+
id: idPrefix + "node-" + index,
|
|
59
|
+
name: cleanedLine,
|
|
60
|
+
type: isDirectory ? "directory" : "file",
|
|
61
|
+
extension: isDirectory ? void 0 : getExtension(cleanedLine),
|
|
62
|
+
level,
|
|
63
|
+
children: isDirectory ? [] : void 0
|
|
64
|
+
};
|
|
65
|
+
while (stack.length > 0 && stack[stack.length - 1].level >= level) {
|
|
66
|
+
stack.pop();
|
|
67
|
+
}
|
|
68
|
+
if (stack[stack.length - 1] === void 0) {
|
|
69
|
+
throw new Error("Tree parsing failed");
|
|
70
|
+
}
|
|
71
|
+
const parentNode = stack[stack.length - 1].node;
|
|
72
|
+
parentNode.children = parentNode.children || [];
|
|
73
|
+
parentNode.children.push(newNode);
|
|
74
|
+
if (newNode.type === "directory") {
|
|
75
|
+
stack.push({ node: newNode, level });
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
return rootNode;
|
|
79
|
+
} catch {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
var parse_tree_output_default = parseTreeOutput;
|
|
84
|
+
export {
|
|
85
|
+
parse_tree_output_default as default,
|
|
86
|
+
parseTreeOutput
|
|
87
|
+
};
|
|
@@ -1,26 +1,39 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
transformerNotationDiff,
|
|
3
|
+
transformerNotationWordHighlight,
|
|
4
|
+
transformerRemoveNotationEscape,
|
|
5
|
+
transformerNotationHighlight
|
|
6
|
+
} from "@shikijs/transformers";
|
|
2
7
|
import { cache } from "react";
|
|
3
|
-
import { bundledLanguages,
|
|
8
|
+
import { bundledLanguages, getSingletonHighlighterCore } from "shiki/bundle/web";
|
|
9
|
+
import { createJavaScriptRegexEngine } from "shiki/engine/javascript";
|
|
4
10
|
async function highlighteCode(code, language) {
|
|
5
11
|
const highlighter = await getShikiHighlighter();
|
|
6
|
-
const
|
|
12
|
+
const shikiLangs = highlighter.getLoadedLanguages();
|
|
13
|
+
const shikiLang = shikiLangs.find((lang) => lang === language);
|
|
7
14
|
if (shikiLang === void 0 && language !== "text") {
|
|
8
15
|
return null;
|
|
9
16
|
}
|
|
10
|
-
const html = highlighter.codeToHtml(code, {
|
|
17
|
+
const html = highlighter.codeToHtml(code.trim(), {
|
|
11
18
|
lang: shikiLang || "text",
|
|
12
19
|
themes: {
|
|
13
20
|
light: "github-light",
|
|
14
21
|
dark: "github-dark-dimmed"
|
|
15
22
|
},
|
|
16
|
-
transformers: [
|
|
23
|
+
transformers: [
|
|
24
|
+
transformerNotationDiff(),
|
|
25
|
+
transformerNotationHighlight(),
|
|
26
|
+
transformerNotationWordHighlight(),
|
|
27
|
+
transformerRemoveNotationEscape()
|
|
28
|
+
]
|
|
17
29
|
});
|
|
18
30
|
return html;
|
|
19
31
|
}
|
|
20
32
|
const getShikiHighlighter = cache(async () => {
|
|
21
|
-
return
|
|
22
|
-
themes:
|
|
23
|
-
langs: Object.
|
|
33
|
+
return getSingletonHighlighterCore({
|
|
34
|
+
themes: [import("@shikijs/themes/github-light"), import("@shikijs/themes/github-dark-dimmed")],
|
|
35
|
+
langs: Object.values(bundledLanguages),
|
|
36
|
+
engine: createJavaScriptRegexEngine()
|
|
24
37
|
});
|
|
25
38
|
});
|
|
26
39
|
export {
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import Script from "next/script";
|
|
2
|
+
import React from "react";
|
|
3
|
+
async function Gist({ gistId, username, filename }) {
|
|
4
|
+
const gistUrl = (() => {
|
|
5
|
+
const base = `https://gist.github.com/${username}/${gistId}.json`;
|
|
6
|
+
if (filename) {
|
|
7
|
+
return base + `?file=${filename}`;
|
|
8
|
+
}
|
|
9
|
+
return base;
|
|
10
|
+
})();
|
|
11
|
+
const result = await fetch(gistUrl);
|
|
12
|
+
const json = await result.json();
|
|
13
|
+
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
|
|
14
|
+
"div",
|
|
15
|
+
{
|
|
16
|
+
className: "gist-block",
|
|
17
|
+
dangerouslySetInnerHTML: {
|
|
18
|
+
__html: json.div
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
), /* @__PURE__ */ React.createElement(Script, { stylesheets: [json.stylesheet] }));
|
|
22
|
+
}
|
|
23
|
+
export {
|
|
24
|
+
Gist as default
|
|
25
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import Link from "next/link";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { MdLink } from "react-icons/md";
|
|
4
|
+
function Heading({ HeadingTag, id, children }) {
|
|
5
|
+
return /* @__PURE__ */ React.createElement(HeadingTag, { id, className: "heading" }, /* @__PURE__ */ React.createElement(Link, { href: `#${id}`, prefetch: false }, /* @__PURE__ */ React.createElement("span", { className: "heading-label" }, children), /* @__PURE__ */ React.createElement("span", { className: "heading-link-icon" }, /* @__PURE__ */ React.createElement(MdLink, null))));
|
|
6
|
+
}
|
|
7
|
+
export {
|
|
8
|
+
Heading as default
|
|
9
|
+
};
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import NextImage from "next/image";
|
|
2
1
|
import React from "react";
|
|
3
2
|
function Image({ src, width, height, alt }) {
|
|
4
|
-
return /* @__PURE__ */ React.createElement(
|
|
3
|
+
return /* @__PURE__ */ React.createElement("picture", null, /* @__PURE__ */ React.createElement("img", { src, width, height, alt }));
|
|
5
4
|
}
|
|
6
5
|
export {
|
|
7
6
|
Image as default
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import NextLink from "next/link";
|
|
2
2
|
import React from "react";
|
|
3
3
|
function Link({ href, id, children }) {
|
|
4
|
-
return /* @__PURE__ */ React.createElement(NextLink, { href, id }, children);
|
|
4
|
+
return /* @__PURE__ */ React.createElement(NextLink, { href, id, prefetch: false }, children);
|
|
5
5
|
}
|
|
6
6
|
export {
|
|
7
7
|
Link as default
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import Image from "next/image";
|
|
2
1
|
import React from "react";
|
|
3
2
|
function LinkCard({
|
|
4
3
|
domain,
|
|
@@ -10,7 +9,7 @@ function LinkCard({
|
|
|
10
9
|
imageWidth
|
|
11
10
|
}) {
|
|
12
11
|
const faviconUrl = `https://t1.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=https://${domain}&size=16`;
|
|
13
|
-
return /* @__PURE__ */ React.createElement("a", { href: url, className: "link-card" }, /* @__PURE__ */ React.createElement("div", { className: "link-card-main" }, /* @__PURE__ */ React.createElement("div", { className: "link-card-title" }, title), /* @__PURE__ */ React.createElement("div", { className: "link-card-description" }, description), /* @__PURE__ */ React.createElement("div", { className: "link-card-domain" }, /* @__PURE__ */ React.createElement("picture", null, /* @__PURE__ */ React.createElement("img", { src: faviconUrl, alt: `${domain} favicon`, width: 16, height: 16 })), /* @__PURE__ */ React.createElement("div", null, domain))), imageUrl && /* @__PURE__ */ React.createElement("
|
|
12
|
+
return /* @__PURE__ */ React.createElement("a", { href: url, className: "link-card" }, /* @__PURE__ */ React.createElement("div", { className: "link-card-main" }, /* @__PURE__ */ React.createElement("div", { className: "link-card-title-container" }, /* @__PURE__ */ React.createElement("div", { className: "link-card-title" }, title)), /* @__PURE__ */ React.createElement("div", { className: "link-card-description" }, description), /* @__PURE__ */ React.createElement("div", { className: "link-card-domain" }, /* @__PURE__ */ React.createElement("picture", null, /* @__PURE__ */ React.createElement("img", { src: faviconUrl, alt: `${domain} favicon`, width: 16, height: 16 })), /* @__PURE__ */ React.createElement("div", null, domain))), imageUrl && /* @__PURE__ */ React.createElement("picture", { className: "link-card-image" }, /* @__PURE__ */ React.createElement("img", { alt: title, src: imageUrl, height: imageHeight, width: imageWidth })));
|
|
14
13
|
}
|
|
15
14
|
export {
|
|
16
15
|
LinkCard as default
|
|
@@ -8,7 +8,7 @@ function createId(code) {
|
|
|
8
8
|
hash.update(code);
|
|
9
9
|
return "id-" + hash.digest("hex");
|
|
10
10
|
}
|
|
11
|
-
function
|
|
11
|
+
function Mermaid({ code }) {
|
|
12
12
|
const ref = useRef(null);
|
|
13
13
|
const [mounted, setMounted] = useState(false);
|
|
14
14
|
const [svg, setSvg] = useState("");
|
|
@@ -32,10 +32,11 @@ function CodeBlockMermaid({ code }) {
|
|
|
32
32
|
};
|
|
33
33
|
renderMermaid();
|
|
34
34
|
}, [mounted, code, id, theme, systemTheme, setSvg, setMounted]);
|
|
35
|
-
if (!mounted)
|
|
36
|
-
return /* @__PURE__ */ React.createElement("div",
|
|
37
|
-
|
|
35
|
+
if (!mounted) {
|
|
36
|
+
return /* @__PURE__ */ React.createElement("div", { className: "mermaid-block mermaid-loading" }, /* @__PURE__ */ React.createElement("div", null, "Loading..."));
|
|
37
|
+
}
|
|
38
|
+
return /* @__PURE__ */ React.createElement("div", { className: "mermaid-block", dangerouslySetInnerHTML: { __html: svg }, ref });
|
|
38
39
|
}
|
|
39
40
|
export {
|
|
40
|
-
|
|
41
|
+
Mermaid as default
|
|
41
42
|
};
|