@interactive-inc/docs-client 0.0.1 → 0.0.2

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.
Files changed (87) hide show
  1. package/build/index.html +13 -0
  2. package/package.json +5 -2
  3. package/.tanstack/tmp/router-generator-Eq20Yx/50b11a0f488e61cf92364a7e585c1448 +0 -123
  4. package/.tanstack/tmp/router-generator-KQIYSa/aecb094e4891f9475b91de590abc8809 +0 -117
  5. package/.tanstack/tmp/router-generator-ekW2pI/aecb094e4891f9475b91de590abc8809 +0 -117
  6. package/app/components/archived-file-list-view.tsx +0 -99
  7. package/app/components/directory-file-list-view.tsx +0 -32
  8. package/app/components/directory-file-tree-node.tsx +0 -96
  9. package/app/components/directory-layout-sidebar.tsx +0 -181
  10. package/app/components/directory-page-view.tsx +0 -208
  11. package/app/components/directory-table-view.tsx +0 -383
  12. package/app/components/editable-table-cell/boolean-editable-cell.tsx +0 -44
  13. package/app/components/editable-table-cell/multi-boolean-editable-cell.tsx +0 -59
  14. package/app/components/editable-table-cell/multi-number-editable-cell.tsx +0 -62
  15. package/app/components/editable-table-cell/multi-relation-editable-cell.tsx +0 -19
  16. package/app/components/editable-table-cell/multi-select-number-editable-cell.tsx +0 -82
  17. package/app/components/editable-table-cell/multi-select-text-editable-cell.tsx +0 -82
  18. package/app/components/editable-table-cell/multi-text-editable-cell.tsx +0 -62
  19. package/app/components/editable-table-cell/number-editable-cell.tsx +0 -54
  20. package/app/components/editable-table-cell/relation-editable-cell.tsx +0 -20
  21. package/app/components/editable-table-cell/select-number-editable-cell.tsx +0 -43
  22. package/app/components/editable-table-cell/select-text-editable-cell.tsx +0 -33
  23. package/app/components/editable-table-cell/string-editable-cell.tsx +0 -42
  24. package/app/components/editable-table-cell.tsx +0 -129
  25. package/app/components/file-card.tsx +0 -120
  26. package/app/components/file-tree-icon.tsx +0 -26
  27. package/app/components/file-view/archive-restore-banner.tsx +0 -86
  28. package/app/components/file-view/csv-file-view.tsx +0 -50
  29. package/app/components/file-view/default-file-view.tsx +0 -11
  30. package/app/components/file-view/editable-csv-cell.tsx +0 -37
  31. package/app/components/file-view/editable-front-matter-view.tsx +0 -105
  32. package/app/components/file-view/file-csv-columns-view.tsx +0 -181
  33. package/app/components/file-view/file-csv-table.tsx +0 -142
  34. package/app/components/file-view/file-header.tsx +0 -115
  35. package/app/components/file-view/file-page-view.tsx +0 -165
  36. package/app/components/file-view/front-matter-input-field.tsx +0 -157
  37. package/app/components/file-view/front-matter-view.tsx +0 -30
  38. package/app/components/file-view/json-file-editor.tsx +0 -38
  39. package/app/components/file-view/markdown-file-view.tsx +0 -71
  40. package/app/components/file-view/multi-relation-select.tsx +0 -129
  41. package/app/components/file-view/render-front-matter-value.tsx +0 -57
  42. package/app/components/file-view/single-relation-select.tsx +0 -41
  43. package/app/components/file-view/sortable-csv-row.tsx +0 -57
  44. package/app/components/file-view/sortable-header.tsx +0 -30
  45. package/app/components/file-view/utils.ts +0 -8
  46. package/app/components/milestones-editor.tsx +0 -95
  47. package/app/components/page-header.tsx +0 -20
  48. package/app/components/project-view/feature-item.tsx +0 -139
  49. package/app/components/project-view/project-page-group.tsx +0 -125
  50. package/app/components/project-view/project-view.tsx +0 -230
  51. package/app/components/project-view/unlinked-features-section.tsx +0 -73
  52. package/app/components/providers.tsx +0 -21
  53. package/app/components/reload-button.tsx +0 -25
  54. package/app/components/sidebar-button.tsx +0 -13
  55. package/app/components/ui/alert.tsx +0 -66
  56. package/app/components/ui/badge.tsx +0 -46
  57. package/app/components/ui/button.tsx +0 -59
  58. package/app/components/ui/card.tsx +0 -92
  59. package/app/components/ui/dialog.tsx +0 -133
  60. package/app/components/ui/emoji-picker.tsx +0 -133
  61. package/app/components/ui/icons.tsx +0 -3
  62. package/app/components/ui/input.tsx +0 -21
  63. package/app/components/ui/select.tsx +0 -183
  64. package/app/components/ui/separator.tsx +0 -26
  65. package/app/components/ui/sheet.tsx +0 -137
  66. package/app/components/ui/sidebar.tsx +0 -724
  67. package/app/components/ui/simple-sidebar.tsx +0 -50
  68. package/app/components/ui/skeleton.tsx +0 -13
  69. package/app/components/ui/sonner.tsx +0 -23
  70. package/app/components/ui/table.tsx +0 -114
  71. package/app/components/ui/tabs.tsx +0 -53
  72. package/app/components/ui/textarea.tsx +0 -18
  73. package/app/components/ui/tooltip.tsx +0 -59
  74. package/app/components/vscode-button.tsx +0 -23
  75. package/app/hooks/use-directory-query.ts +0 -22
  76. package/app/hooks/use-file-properties-mutation.ts +0 -38
  77. package/app/hooks/use-file-query.ts +0 -21
  78. package/app/hooks/use-mobile.ts +0 -19
  79. package/app/main.css +0 -122
  80. package/app/main.tsx +0 -38
  81. package/app/routes/$.index.tsx +0 -25
  82. package/app/routes/$.tsx +0 -20
  83. package/app/routes/__root.tsx +0 -20
  84. package/app/routes/_index.tsx +0 -17
  85. package/app/routes/apps.client.$.tsx +0 -21
  86. package/components.json +0 -21
  87. package/tsconfig.index.json +0 -17
@@ -1,115 +0,0 @@
1
- import { useMutation } from "@tanstack/react-query"
2
- import { useNavigate } from "@tanstack/react-router"
3
- import { ArrowLeft } from "lucide-react"
4
- import { useState, useEffect } from "react"
5
- import { ReloadButton } from "@/components/reload-button"
6
- import { SidebarButton } from "@/components/sidebar-button"
7
- import { Button } from "@/components/ui/button"
8
- import { Input } from "@/components/ui/input"
9
- import { VscodeButton } from "@/components/vscode-button"
10
- import { apiClient } from "@/lib/api-client"
11
- import { normalizePath } from "@/lib/path-utils"
12
-
13
- type Props = {
14
- filePath: string
15
- fileData: {
16
- path: string
17
- title: string | null
18
- }
19
- cwd: string
20
- onReload: () => void
21
- isLoading: boolean
22
- children?: React.ReactNode
23
- }
24
-
25
- export function FileHeader(props: Props) {
26
- const navigate = useNavigate()
27
-
28
- const getInitialTitle = () => {
29
- if (props.fileData.title) return props.fileData.title
30
- return props.filePath.split("/").pop()?.replace(/\.md$/, "") || ""
31
- }
32
-
33
- const [title, setTitle] = useState(getInitialTitle())
34
-
35
- // propsが変更されたときにタイトルを更新
36
- useEffect(() => {
37
- setTitle(getInitialTitle())
38
- }, [props.fileData.title, props.filePath])
39
-
40
- const updateTitleMutation = useMutation({
41
- async mutationFn(newTitle: string) {
42
- const normalizedPath = normalizePath(props.filePath)
43
-
44
- await apiClient.api.files[":path{.+}"].$put({
45
- param: {
46
- path: normalizedPath,
47
- },
48
- json: {
49
- title: newTitle,
50
- properties: null,
51
- content: null,
52
- description: null,
53
- isArchived: null,
54
- },
55
- })
56
- },
57
- onSuccess() {
58
- props.onReload()
59
- },
60
- })
61
-
62
- const handleTitleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
63
- const newTitle = e.target.value
64
- setTitle(newTitle)
65
- }
66
-
67
- const handleTitleBlur = async () => {
68
- if (props.fileData.path.endsWith(".md")) {
69
- updateTitleMutation.mutate(title)
70
- }
71
- }
72
-
73
- const handleBackClick = () => {
74
- const pathSegments = props.filePath.split("/")
75
- pathSegments.pop()
76
-
77
- // アーカイブディレクトリ(_)を削除
78
- const filteredSegments = pathSegments.filter((segment) => segment !== "_")
79
- const directoryPath = filteredSegments.join("/")
80
-
81
- navigate({ to: `/${directoryPath}` })
82
- }
83
-
84
- return (
85
- <>
86
- <div className="flex items-center gap-2">
87
- <SidebarButton />
88
- <VscodeButton
89
- cwd={props.cwd}
90
- filePath={props.fileData.path}
91
- size="icon"
92
- variant="outline"
93
- />
94
- <Button onClick={handleBackClick} size="icon" variant="outline">
95
- <ArrowLeft className="h-4 w-4" />
96
- </Button>
97
-
98
- <Input
99
- value={title}
100
- onChange={handleTitleChange}
101
- onBlur={handleTitleBlur}
102
- placeholder="タイトルを入力"
103
- className="flex-1"
104
- />
105
- <ReloadButton
106
- onReload={props.onReload}
107
- size="icon"
108
- variant="outline"
109
- disabled={props.isLoading}
110
- />
111
- {props.children}
112
- </div>
113
- </>
114
- )
115
- }
@@ -1,165 +0,0 @@
1
- import { useState } from "react"
2
- import { CsvFileView } from "@/components/file-view/csv-file-view"
3
- import { DefaultFileViewer } from "@/components/file-view/default-file-view"
4
- import { JsonFileEditor } from "@/components/file-view/json-file-editor"
5
- import { MarkdownFileView } from "@/components/file-view/markdown-file-view"
6
- import { useDirectoryQuery } from "@/hooks/use-directory-query"
7
- import { useFilePropertiesMutation } from "@/hooks/use-file-properties-mutation"
8
- import { useFileQuery } from "@/hooks/use-file-query"
9
- import { apiClient } from "@/lib/api-client"
10
- import { normalizePath } from "@/lib/path-utils"
11
-
12
- type Props = {
13
- filePath: string
14
- }
15
-
16
- export function FilePageView(props: Props) {
17
- const fileQuery = useFileQuery(props.filePath)
18
-
19
- const directoryQuery = useDirectoryQuery(props.filePath)
20
-
21
- const fileData = fileQuery.data
22
-
23
- const directorySchema =
24
- directoryQuery.data?.indexFile?.content?.frontMatter?.schema || {}
25
-
26
- const relations = directoryQuery.data?.relations || []
27
-
28
- // contentがオブジェクトの場合はbodyプロパティを使用
29
- const initialContent = typeof fileData?.content === 'string'
30
- ? fileData.content
31
- : fileData?.content?.body || ""
32
- const [currentContent, setCurrentContent] = useState(initialContent)
33
-
34
- const updateProperties = useFilePropertiesMutation()
35
-
36
- const onChange = async (newContent: string) => {
37
- const normalizedPath = normalizePath(props.filePath)
38
-
39
- const result = await apiClient.api.files[":path{.+}"].$put({
40
- param: { path: normalizedPath },
41
- json: {
42
- title: null,
43
- properties: null,
44
- content: newContent,
45
- description: null,
46
- isArchived: null,
47
- },
48
- })
49
-
50
- const data = await result.json()
51
- if ("content" in data && typeof data.content === "string") {
52
- setCurrentContent(data.content)
53
- }
54
- }
55
-
56
- const handleReload = async () => {
57
- const result = await fileQuery.refetch()
58
- if (result.data === undefined) return
59
- const content = typeof result.data.content === 'string'
60
- ? result.data.content
61
- : result.data.content?.body || ""
62
- setCurrentContent(content)
63
- }
64
-
65
- const handleFrontMatterUpdate = async (key: string, value: unknown) => {
66
- await updateProperties.mutateAsync({
67
- path: props.filePath,
68
- field: key,
69
- value: value,
70
- })
71
- // フロントマター更新後にファイル内容を再取得
72
- handleReload()
73
- }
74
-
75
- if (!fileData) {
76
- return <div>ファイルが見つかりません</div>
77
- }
78
-
79
- // pathオブジェクトから実際のパス文字列を取得
80
- const filePath = typeof fileData.path === 'string' ? fileData.path : fileData.path?.path
81
-
82
- if (filePath?.endsWith(".md")) {
83
- return (
84
- <main className="p-2">
85
- <MarkdownFileView
86
- filePath={props.filePath}
87
- fileData={{ path: filePath, title: fileData.content.title || null }}
88
- cwd={directoryQuery.data.cwd}
89
- content={currentContent}
90
- onChange={onChange}
91
- frontMatter={(() => {
92
- // frontMatterはcontent内にある
93
- const fm = fileData.content.frontMatter || {}
94
- // スキーマやアイコンなどの複雑なオブジェクトを除外し、プリミティブ値のみを返す
95
- const filtered: Record<
96
- string,
97
- string | number | boolean | string[] | number[] | boolean[] | null
98
- > = {}
99
- for (const [key, value] of Object.entries(fm)) {
100
- if (key === "schema" || key === "icon") continue
101
- if (
102
- typeof value === "string" ||
103
- typeof value === "number" ||
104
- typeof value === "boolean" ||
105
- value === null ||
106
- (Array.isArray(value) &&
107
- value.every(
108
- (v) =>
109
- typeof v === "string" ||
110
- typeof v === "number" ||
111
- typeof v === "boolean",
112
- ))
113
- ) {
114
- filtered[key] = value as
115
- | string
116
- | number
117
- | boolean
118
- | string[]
119
- | number[]
120
- | boolean[]
121
- | null
122
- }
123
- }
124
- return filtered
125
- })()}
126
- onFrontMatterUpdate={handleFrontMatterUpdate}
127
- onReload={handleReload}
128
- isLoading={fileQuery.isLoading}
129
- schema={directorySchema}
130
- relations={relations}
131
- />
132
- </main>
133
- )
134
- }
135
-
136
- if (filePath?.endsWith(".csv")) {
137
- return (
138
- <main className="p-2">
139
- <CsvFileView
140
- filePath={props.filePath}
141
- fileData={{ path: filePath, title: fileData.content.title || null }}
142
- cwd={directoryQuery.data.cwd}
143
- content={currentContent}
144
- onChange={onChange}
145
- onReload={handleReload}
146
- isLoading={fileQuery.isLoading}
147
- />
148
- </main>
149
- )
150
- }
151
-
152
- if (filePath?.endsWith(".json")) {
153
- return (
154
- <main className="p-2">
155
- <JsonFileEditor content={currentContent} />
156
- </main>
157
- )
158
- }
159
-
160
- return (
161
- <main className="p-2">
162
- <DefaultFileViewer content={currentContent} />
163
- </main>
164
- )
165
- }
@@ -1,157 +0,0 @@
1
- import { useEffect, useState } from "react"
2
- import { Input } from "@/components/ui/input"
3
- import {
4
- Select,
5
- SelectContent,
6
- SelectItem,
7
- SelectTrigger,
8
- SelectValue,
9
- } from "@/components/ui/select"
10
- import type { DocRelationFile, DocSchemaField } from "@/lib/types"
11
- import { MultiRelationSelect } from "./multi-relation-select"
12
- import { SingleRelationSelect } from "./single-relation-select"
13
-
14
- type Props = {
15
- fieldKey: string
16
- value: unknown
17
- originalValue: unknown
18
- onValueChange: (key: string, value: string) => void
19
- onBlur: (key: string, value: string) => void
20
- schemaField?: DocSchemaField
21
- relationOptions?: DocRelationFile[]
22
- }
23
-
24
- /**
25
- * Front Matter用の入力フィールドを表示するコンポーネント
26
- */
27
- export function FrontMatterInputField(props: Props) {
28
- const [localValue, setLocalValue] = useState(() => {
29
- if (Array.isArray(props.value)) {
30
- return props.value.join(", ")
31
- }
32
- return String(props.value)
33
- })
34
-
35
- useEffect(() => {
36
- if (Array.isArray(props.value)) {
37
- setLocalValue(props.value.join(", "))
38
- } else {
39
- setLocalValue(String(props.value))
40
- }
41
- }, [props.value])
42
-
43
- const _getDisplayValue = (value: unknown): string => {
44
- if (Array.isArray(value)) {
45
- return value.join(", ")
46
- }
47
- return String(value)
48
- }
49
-
50
- // リレーション型の場合
51
- if (
52
- props.schemaField &&
53
- props.schemaField.type === "relation" &&
54
- props.schemaField.path
55
- ) {
56
- return (
57
- <SingleRelationSelect
58
- value={(props.value as string) || ""}
59
- relationOptions={props.relationOptions}
60
- onValueChange={(value) => props.onValueChange(props.fieldKey, value)}
61
- />
62
- )
63
- }
64
-
65
- // 配列リレーション型の場合
66
- if (
67
- props.schemaField &&
68
- props.schemaField.type === "multi-relation" &&
69
- props.schemaField.path
70
- ) {
71
- // 値を配列に正規化
72
- let normalizedValue: string[] = []
73
- if (Array.isArray(props.value)) {
74
- normalizedValue = props.value.filter(
75
- (v) => typeof v === "string" && v.trim() !== "",
76
- )
77
- } else if (typeof props.value === "string" && props.value.trim() !== "") {
78
- normalizedValue = props.value
79
- .split(",")
80
- .map((v) => v.trim())
81
- .filter((v) => v !== "")
82
- }
83
-
84
- return (
85
- <MultiRelationSelect
86
- value={normalizedValue}
87
- relationOptions={props.relationOptions}
88
- onValueChange={(value) =>
89
- props.onValueChange(props.fieldKey, value.join(", "))
90
- }
91
- wrap={true}
92
- />
93
- )
94
- }
95
-
96
- if (typeof props.originalValue === "boolean") {
97
- return (
98
- <Select
99
- value={String(props.value)}
100
- onValueChange={(value) => props.onValueChange(props.fieldKey, value)}
101
- >
102
- <SelectTrigger className="w-full">
103
- <SelectValue />
104
- </SelectTrigger>
105
- <SelectContent>
106
- <SelectItem value="true">true</SelectItem>
107
- <SelectItem value="false">false</SelectItem>
108
- </SelectContent>
109
- </Select>
110
- )
111
- }
112
-
113
- if (typeof props.originalValue === "number") {
114
- return (
115
- <Input
116
- type="number"
117
- value={localValue}
118
- onChange={(e) => setLocalValue(e.target.value)}
119
- onBlur={(e) => props.onBlur(props.fieldKey, e.target.value)}
120
- className="flex-1"
121
- placeholder={`${props.fieldKey}の値を入力`}
122
- />
123
- )
124
- }
125
-
126
- if (Array.isArray(props.originalValue)) {
127
- const placeholder =
128
- props.originalValue.length > 0 &&
129
- typeof props.originalValue[0] === "number"
130
- ? "カンマ区切りで数値を入力 (例: 1, 2, 3)"
131
- : props.originalValue.length > 0 &&
132
- typeof props.originalValue[0] === "boolean"
133
- ? "カンマ区切りでtrue/falseを入力 (例: true, false, true)"
134
- : "カンマ区切りで入力 (例: item1, item2, item3)"
135
-
136
- return (
137
- <Input
138
- value={localValue}
139
- onChange={(e) => setLocalValue(e.target.value)}
140
- onBlur={(e) => props.onBlur(props.fieldKey, e.target.value)}
141
- className="flex-1"
142
- placeholder={placeholder}
143
- />
144
- )
145
- }
146
-
147
- // string型の場合
148
- return (
149
- <Input
150
- value={localValue}
151
- onChange={(e) => setLocalValue(e.target.value)}
152
- onBlur={(e) => props.onBlur(props.fieldKey, e.target.value)}
153
- className="flex-1"
154
- placeholder={`${props.fieldKey}の値を入力`}
155
- />
156
- )
157
- }
@@ -1,30 +0,0 @@
1
- import { Card } from "@/components/ui/card"
2
- import { RenderFrontMatterValue } from "./render-front-matter-value"
3
-
4
- type Props = {
5
- frontMatter: Record<string, string | string[]> | null
6
- }
7
-
8
- /**
9
- * Front-matterをより見やすく表示するコンポーネント(表示のみ)
10
- */
11
- export function FrontMatterView(props: Props) {
12
- const frontMatter = props.frontMatter
13
- // front-matterが空の場合は表示しない
14
- if (!frontMatter || Object.keys(frontMatter).length === 0) {
15
- return null
16
- }
17
-
18
- return (
19
- <Card className="gap-0 p-2">
20
- {Object.entries(frontMatter).map(([key, value]) => (
21
- <div key={key}>
22
- <div className="flex gap-x-2">
23
- <span className="font-bold">{key}</span>
24
- <RenderFrontMatterValue value={value} />
25
- </div>
26
- </div>
27
- ))}
28
- </Card>
29
- )
30
- }
@@ -1,38 +0,0 @@
1
- import { useEffect, useState } from "react"
2
-
3
- type Props = {
4
- content: string
5
- }
6
-
7
- export function JsonFileEditor(props: Props) {
8
- const [formattedJson, setFormattedJson] = useState<string>("")
9
- const [error, setError] = useState<string | null>(null)
10
-
11
- useEffect(() => {
12
- try {
13
- const jsonData = JSON.parse(props.content)
14
- setFormattedJson(JSON.stringify(jsonData, null, 2))
15
- setError(null)
16
- } catch (_e) {
17
- setError(
18
- "JSONのパースに失敗しました。有効なJSONではない可能性があります。",
19
- )
20
- setFormattedJson(props.content) // JSONのパースに失敗した場合はそのまま表示
21
- }
22
- }, [props.content])
23
-
24
- return (
25
- <div>
26
- {error && (
27
- <div className="mb-4 rounded border border-red-700 bg-red-900/30 p-2 text-red-300 text-sm">
28
- {error}
29
- </div>
30
- )}
31
- <div className="overflow-x-auto rounded bg-gray-800 p-4 text-white">
32
- <pre className="whitespace-pre-wrap font-mono text-green-300">
33
- {formattedJson}
34
- </pre>
35
- </div>
36
- </div>
37
- )
38
- }
@@ -1,71 +0,0 @@
1
- import { marked } from "marked"
2
- import type { ReactNode } from "react"
3
- import { ArchiveRestoreBanner } from "@/components/file-view/archive-restore-banner"
4
- import { EditableFrontMatterView } from "@/components/file-view/editable-front-matter-view"
5
- import { FileHeader } from "@/components/file-view/file-header"
6
- import { Card } from "@/components/ui/card"
7
- import type { DocRelation, DocSchemaRecord, DocFileMdFrontMatter } from "@/lib/types"
8
-
9
- import "github-markdown-css"
10
-
11
- type Props = {
12
- filePath: string
13
- fileData: {
14
- path: string
15
- title: string | null
16
- }
17
- cwd: string
18
- content: string
19
- onChange(content: string): void
20
- frontMatter: DocFileMdFrontMatter
21
- onFrontMatterUpdate: (key: string, value: unknown) => void
22
- onReload: () => void
23
- isLoading: boolean
24
- schema?: DocSchemaRecord
25
- relations?: DocRelation[]
26
- }
27
-
28
- export function MarkdownFileView(props: Props): ReactNode {
29
- const frontMatter = props.frontMatter
30
-
31
- const hasFrontMatter =
32
- frontMatter !== null && Object.keys(frontMatter || {}).length > 0
33
-
34
- const html = marked.parse(props.content)
35
-
36
- // アーカイブファイルかどうかを判定
37
- const isArchived = props.filePath.includes("/_/")
38
-
39
- return (
40
- <div className="h-full space-y-2">
41
- {isArchived && (
42
- <ArchiveRestoreBanner
43
- filePath={props.filePath}
44
- onRestore={props.onReload}
45
- />
46
- )}
47
- <FileHeader
48
- filePath={props.filePath}
49
- fileData={props.fileData}
50
- cwd={props.cwd}
51
- onReload={props.onReload}
52
- isLoading={props.isLoading}
53
- />
54
- {hasFrontMatter && (
55
- <EditableFrontMatterView
56
- frontMatter={frontMatter}
57
- onUpdate={props.onFrontMatterUpdate}
58
- schema={props.schema}
59
- relations={props.relations}
60
- />
61
- )}
62
- <Card className="overflow-hidden rounded-md p-0">
63
- <div
64
- className="markdown-body p-4"
65
- // biome-ignore lint/security/noDangerouslySetInnerHtml: <explanation>
66
- dangerouslySetInnerHTML={{ __html: html }}
67
- />
68
- </Card>
69
- </div>
70
- )
71
- }