@suzumiyaaoba/mdxr 0.1.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.
Files changed (236) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +56 -0
  3. package/dist/cli.d.mts +1 -0
  4. package/dist/cli.mjs +2902 -0
  5. package/dist/components.d.mts +791 -0
  6. package/dist/components.mjs +2 -0
  7. package/dist/config-SI9IyFiC.mjs +184 -0
  8. package/dist/doc-context-CEqzMYKv.d.mts +568 -0
  9. package/dist/index.d.mts +20 -0
  10. package/dist/index.mjs +4 -0
  11. package/dist/ui-GKFD0mx5.mjs +15986 -0
  12. package/package.json +131 -0
  13. package/skill/SKILL.md +49 -0
  14. package/skill/references/components/charts.md +172 -0
  15. package/skill/references/components/document.md +98 -0
  16. package/skill/references/components/forms.md +35 -0
  17. package/skill/references/components/investigation.md +198 -0
  18. package/skill/references/components/layout.md +68 -0
  19. package/skill/references/components/output.md +158 -0
  20. package/skill/references/components/planning.md +155 -0
  21. package/skill/references/components/reports.md +252 -0
  22. package/skill/references/components/shadcn.md +19 -0
  23. package/skill/references/components.md +186 -0
  24. package/skill/references/extending.md +48 -0
  25. package/src/ask-sheet.ts +40 -0
  26. package/src/assets/css.ts +392 -0
  27. package/src/assets/scripts.ts +78 -0
  28. package/src/catalog.ts +287 -0
  29. package/src/cli.ts +178 -0
  30. package/src/client/doc-events.ts +1108 -0
  31. package/src/client/entry.ts +27 -0
  32. package/src/client-js.ts +47 -0
  33. package/src/component-map.ts +47 -0
  34. package/src/components/ui/accordion.tsx +77 -0
  35. package/src/components/ui/alert-dialog.tsx +185 -0
  36. package/src/components/ui/alert.tsx +76 -0
  37. package/src/components/ui/aspect-ratio.tsx +22 -0
  38. package/src/components/ui/attachment.tsx +208 -0
  39. package/src/components/ui/avatar.tsx +106 -0
  40. package/src/components/ui/badge.tsx +52 -0
  41. package/src/components/ui/breadcrumb.tsx +121 -0
  42. package/src/components/ui/bubble.tsx +128 -0
  43. package/src/components/ui/button-group.tsx +88 -0
  44. package/src/components/ui/button.tsx +58 -0
  45. package/src/components/ui/calendar.tsx +226 -0
  46. package/src/components/ui/card.tsx +102 -0
  47. package/src/components/ui/carousel.tsx +246 -0
  48. package/src/components/ui/chart.tsx +379 -0
  49. package/src/components/ui/checkbox.tsx +27 -0
  50. package/src/components/ui/collapsible.tsx +19 -0
  51. package/src/components/ui/combobox.tsx +298 -0
  52. package/src/components/ui/command.tsx +193 -0
  53. package/src/components/ui/context-menu.tsx +271 -0
  54. package/src/components/ui/dialog.tsx +159 -0
  55. package/src/components/ui/direction.tsx +4 -0
  56. package/src/components/ui/drawer.tsx +227 -0
  57. package/src/components/ui/dropdown-menu.tsx +269 -0
  58. package/src/components/ui/empty.tsx +104 -0
  59. package/src/components/ui/field.tsx +237 -0
  60. package/src/components/ui/fieldset.tsx +32 -0
  61. package/src/components/ui/frame.tsx +87 -0
  62. package/src/components/ui/hover-card.tsx +50 -0
  63. package/src/components/ui/input-group.tsx +159 -0
  64. package/src/components/ui/input-otp.tsx +83 -0
  65. package/src/components/ui/input.tsx +19 -0
  66. package/src/components/ui/item.tsx +202 -0
  67. package/src/components/ui/kbd.tsx +26 -0
  68. package/src/components/ui/label.tsx +19 -0
  69. package/src/components/ui/marker.tsx +71 -0
  70. package/src/components/ui/menubar.tsx +284 -0
  71. package/src/components/ui/message-scroller.tsx +128 -0
  72. package/src/components/ui/message.tsx +91 -0
  73. package/src/components/ui/meter.tsx +80 -0
  74. package/src/components/ui/native-select.tsx +64 -0
  75. package/src/components/ui/navigation-menu.tsx +170 -0
  76. package/src/components/ui/pagination.tsx +133 -0
  77. package/src/components/ui/popover.tsx +87 -0
  78. package/src/components/ui/progress.tsx +82 -0
  79. package/src/components/ui/questionnaire.tsx +328 -0
  80. package/src/components/ui/radio-group.tsx +35 -0
  81. package/src/components/ui/resizable.tsx +49 -0
  82. package/src/components/ui/scroll-area.tsx +50 -0
  83. package/src/components/ui/select.tsx +201 -0
  84. package/src/components/ui/separator.tsx +22 -0
  85. package/src/components/ui/sheet.tsx +135 -0
  86. package/src/components/ui/sidebar.tsx +730 -0
  87. package/src/components/ui/skeleton.tsx +13 -0
  88. package/src/components/ui/slider.tsx +51 -0
  89. package/src/components/ui/spinner.tsx +16 -0
  90. package/src/components/ui/switch.tsx +31 -0
  91. package/src/components/ui/table.tsx +113 -0
  92. package/src/components/ui/tabs.tsx +82 -0
  93. package/src/components/ui/textarea.tsx +17 -0
  94. package/src/components/ui/toast.tsx +229 -0
  95. package/src/components/ui/toggle-group.tsx +87 -0
  96. package/src/components/ui/toggle.tsx +43 -0
  97. package/src/components/ui/tooltip.tsx +65 -0
  98. package/src/components.ts +78 -0
  99. package/src/config.ts +56 -0
  100. package/src/define.ts +122 -0
  101. package/src/doc-context.ts +20 -0
  102. package/src/editor.ts +94 -0
  103. package/src/format-error.ts +78 -0
  104. package/src/guards.ts +84 -0
  105. package/src/hooks/use-mobile.ts +21 -0
  106. package/src/html.ts +91 -0
  107. package/src/hydrate/export-index.ts +232 -0
  108. package/src/hydrate/import-scan.ts +169 -0
  109. package/src/hydrate/plugins.ts +118 -0
  110. package/src/hydrate/runtime-module.ts +145 -0
  111. package/src/hydrate-runtime.ts +67 -0
  112. package/src/hydrate.ts +147 -0
  113. package/src/index.ts +16 -0
  114. package/src/init.ts +57 -0
  115. package/src/langs.ts +148 -0
  116. package/src/lines.ts +53 -0
  117. package/src/load-user-module.ts +191 -0
  118. package/src/mdx.ts +248 -0
  119. package/src/paths.ts +36 -0
  120. package/src/rehype/shiki.ts +533 -0
  121. package/src/remark/alerts.ts +53 -0
  122. package/src/remark/ast.ts +96 -0
  123. package/src/remark/callouts.ts +25 -0
  124. package/src/remark/code-file.ts +85 -0
  125. package/src/remark/code-meta.ts +21 -0
  126. package/src/remark/directives.ts +170 -0
  127. package/src/remark/file-paths.ts +64 -0
  128. package/src/remark/headings.ts +131 -0
  129. package/src/remark/no-js.ts +40 -0
  130. package/src/render.ts +337 -0
  131. package/src/serve.ts +322 -0
  132. package/src/styles/globals.css +134 -0
  133. package/src/styles/shadcn.css +641 -0
  134. package/src/tailwind.ts +119 -0
  135. package/src/ui/approvals.tsx +76 -0
  136. package/src/ui/ask-question.tsx +386 -0
  137. package/src/ui/ask.tsx +206 -0
  138. package/src/ui/attrs.ts +51 -0
  139. package/src/ui/audit.tsx +139 -0
  140. package/src/ui/bar-chart.tsx +334 -0
  141. package/src/ui/benchmarks.tsx +143 -0
  142. package/src/ui/bits.tsx +537 -0
  143. package/src/ui/board.tsx +173 -0
  144. package/src/ui/bridge.tsx +207 -0
  145. package/src/ui/bumps.tsx +178 -0
  146. package/src/ui/callout.tsx +106 -0
  147. package/src/ui/changes.tsx +89 -0
  148. package/src/ui/chart-bits.tsx +52 -0
  149. package/src/ui/chart.ts +577 -0
  150. package/src/ui/checks.tsx +203 -0
  151. package/src/ui/child-index.tsx +44 -0
  152. package/src/ui/children.ts +43 -0
  153. package/src/ui/chips.tsx +49 -0
  154. package/src/ui/cmd.tsx +27 -0
  155. package/src/ui/columns.tsx +31 -0
  156. package/src/ui/comments.tsx +544 -0
  157. package/src/ui/compare.tsx +67 -0
  158. package/src/ui/decision.tsx +79 -0
  159. package/src/ui/deps.tsx +78 -0
  160. package/src/ui/details.tsx +43 -0
  161. package/src/ui/diff-parse.ts +307 -0
  162. package/src/ui/diff.tsx +458 -0
  163. package/src/ui/diffstat.tsx +58 -0
  164. package/src/ui/due.tsx +65 -0
  165. package/src/ui/effort.tsx +29 -0
  166. package/src/ui/endpoints.tsx +118 -0
  167. package/src/ui/envvars.tsx +96 -0
  168. package/src/ui/figure.tsx +37 -0
  169. package/src/ui/file-icon.ts +1020 -0
  170. package/src/ui/file-link.ts +32 -0
  171. package/src/ui/file-ref.tsx +39 -0
  172. package/src/ui/files.tsx +112 -0
  173. package/src/ui/findings.tsx +85 -0
  174. package/src/ui/flow.tsx +73 -0
  175. package/src/ui/funnel.tsx +121 -0
  176. package/src/ui/gantt.tsx +443 -0
  177. package/src/ui/gauges.tsx +135 -0
  178. package/src/ui/glossary.tsx +29 -0
  179. package/src/ui/graph-layout.ts +149 -0
  180. package/src/ui/graph-specs.tsx +102 -0
  181. package/src/ui/graph.tsx +278 -0
  182. package/src/ui/grid.tsx +119 -0
  183. package/src/ui/hypothesis.tsx +94 -0
  184. package/src/ui/icon.tsx +80 -0
  185. package/src/ui/incident.tsx +130 -0
  186. package/src/ui/index.ts +342 -0
  187. package/src/ui/ins-del.tsx +41 -0
  188. package/src/ui/json.tsx +190 -0
  189. package/src/ui/layout.ts +9 -0
  190. package/src/ui/line-chart.tsx +251 -0
  191. package/src/ui/matrix.tsx +208 -0
  192. package/src/ui/meta.tsx +73 -0
  193. package/src/ui/option.tsx +64 -0
  194. package/src/ui/owner.tsx +40 -0
  195. package/src/ui/packages.tsx +104 -0
  196. package/src/ui/pathway.tsx +96 -0
  197. package/src/ui/phase.tsx +41 -0
  198. package/src/ui/pie-chart.tsx +170 -0
  199. package/src/ui/plan.tsx +77 -0
  200. package/src/ui/pre.tsx +175 -0
  201. package/src/ui/priority.tsx +51 -0
  202. package/src/ui/props.tsx +77 -0
  203. package/src/ui/quadrant.tsx +172 -0
  204. package/src/ui/radar.tsx +200 -0
  205. package/src/ui/ref.tsx +160 -0
  206. package/src/ui/release.tsx +178 -0
  207. package/src/ui/req.tsx +42 -0
  208. package/src/ui/review.tsx +133 -0
  209. package/src/ui/risk.tsx +67 -0
  210. package/src/ui/sankey.tsx +287 -0
  211. package/src/ui/scatter.tsx +237 -0
  212. package/src/ui/schema.tsx +113 -0
  213. package/src/ui/score.tsx +105 -0
  214. package/src/ui/search.tsx +120 -0
  215. package/src/ui/series.tsx +38 -0
  216. package/src/ui/severity.tsx +69 -0
  217. package/src/ui/shadcn.tsx +216 -0
  218. package/src/ui/spark.tsx +86 -0
  219. package/src/ui/stack.tsx +57 -0
  220. package/src/ui/stats.tsx +63 -0
  221. package/src/ui/status-badge.tsx +66 -0
  222. package/src/ui/statuspage.tsx +238 -0
  223. package/src/ui/steps.tsx +74 -0
  224. package/src/ui/summary.tsx +41 -0
  225. package/src/ui/symbol-ref.tsx +73 -0
  226. package/src/ui/terminal.tsx +117 -0
  227. package/src/ui/tests.tsx +218 -0
  228. package/src/ui/timeline.tsx +63 -0
  229. package/src/ui/toc.tsx +56 -0
  230. package/src/ui/tones.ts +187 -0
  231. package/src/ui/trace.tsx +69 -0
  232. package/src/ui/tree.tsx +281 -0
  233. package/src/ui/treemap.tsx +128 -0
  234. package/src/ui/venn.tsx +258 -0
  235. package/src/ui/verdict.tsx +78 -0
  236. package/src/ui/waterfall.tsx +142 -0
@@ -0,0 +1,105 @@
1
+ import * as v from "valibot";
2
+
3
+ import { defineComponent } from "../define.js";
4
+ import { nonEmpty } from "../guards.js";
5
+ import { numOf, NUMISH } from "./attrs.js";
6
+ import { BORDER_CLS, TEXT, TEXT_SUB } from "./tones.js";
7
+
8
+ type Band = "amber" | "emerald" | "red";
9
+
10
+ const STROKE_CLS: Record<Band, string> = {
11
+ amber: "stroke-amber-500",
12
+ emerald: "stroke-emerald-500",
13
+ red: "stroke-red-500",
14
+ };
15
+
16
+ const TEXT_BAND: Record<Band, string> = {
17
+ amber: "text-amber-600 dark:text-amber-400",
18
+ emerald: "text-emerald-600 dark:text-emerald-400",
19
+ red: "text-red-600 dark:text-red-400",
20
+ };
21
+
22
+ const bandOf = (pct: number): Band => {
23
+ if (pct >= 80) {
24
+ return "emerald";
25
+ }
26
+ if (pct >= 50) {
27
+ return "amber";
28
+ }
29
+ return "red";
30
+ };
31
+
32
+ /** `0..1` 以外の数値は 0-100 スケールとして扱う。 */
33
+ const toPct = (value: number, max: number | undefined): number => {
34
+ if (max !== undefined && max > 0) {
35
+ return Math.min(100, Math.max(0, (value / max) * 100));
36
+ }
37
+ return Math.min(100, Math.max(0, value));
38
+ };
39
+
40
+ export const Score = defineComponent(
41
+ {
42
+ description:
43
+ "0-100 スコアのリングゲージ。value は必須 (max で分母変更可)。label で指標名、detail で補足行。80+ 緑・50+ 黄・それ以下赤。ヘルススキャン・品質スコアの表示向け。Row/Grid に並べられる",
44
+ schema: v.looseObject({
45
+ detail: v.optional(v.string()),
46
+ label: v.optional(v.string()),
47
+ max: v.optional(NUMISH),
48
+ value: NUMISH,
49
+ }),
50
+ },
51
+ ({ value, max, label, detail }) => {
52
+ const vNum = numOf(value) ?? 0;
53
+ const pct = toPct(vNum, numOf(max));
54
+ const band = bandOf(pct);
55
+ const shown = Number.isInteger(vNum) ? String(vNum) : vNum.toFixed(1);
56
+ return (
57
+ <div
58
+ className={`not-prose my-6 inline-flex items-center gap-3 rounded-lg border px-4 py-3 ${BORDER_CLS}`}
59
+ >
60
+ <svg className="h-14 w-14" viewBox="0 0 36 36">
61
+ <circle
62
+ className="stroke-neutral-200 dark:stroke-neutral-700"
63
+ cx="18"
64
+ cy="18"
65
+ fill="none"
66
+ r="15.9155"
67
+ strokeWidth="3"
68
+ />
69
+ <circle
70
+ className={STROKE_CLS[band]}
71
+ cx="18"
72
+ cy="18"
73
+ fill="none"
74
+ pathLength={100}
75
+ r="15.9155"
76
+ strokeDasharray={`${pct} 100`}
77
+ strokeDashoffset="25"
78
+ strokeLinecap="round"
79
+ strokeWidth="3"
80
+ />
81
+ <text
82
+ className={`fill-current font-semibold ${TEXT_BAND[band]}`}
83
+ dominantBaseline="central"
84
+ fontSize="9"
85
+ textAnchor="middle"
86
+ x="18"
87
+ y="18"
88
+ >
89
+ {shown}
90
+ </text>
91
+ </svg>
92
+ <div className="min-w-0">
93
+ {nonEmpty(label) ? (
94
+ <div className={`truncate text-sm font-medium ${TEXT.strong}`}>
95
+ {label}
96
+ </div>
97
+ ) : null}
98
+ <div className={`${TEXT_SUB} ${TEXT.muted}`}>
99
+ {nonEmpty(detail) ? detail : "score"}
100
+ </div>
101
+ </div>
102
+ </div>
103
+ );
104
+ }
105
+ );
@@ -0,0 +1,120 @@
1
+ import type { ReactElement, ReactNode } from "react";
2
+ import * as v from "valibot";
3
+
4
+ import { defineComponent, flattenChildren } from "../define.js";
5
+ import { nonEmpty } from "../guards.js";
6
+ import { NUMISH, TITLE_PROP } from "./attrs.js";
7
+ import { ListPanel, ListRow, Pill, RowIcon, RowNote } from "./bits.js";
8
+ import { isEl, propOf } from "./children.js";
9
+ import {
10
+ LOC_CLS,
11
+ MONO_CLS,
12
+ MONO_TAG_CLS,
13
+ SURFACE_CLS,
14
+ TEXT,
15
+ TONE,
16
+ } from "./tones.js";
17
+
18
+ interface HitsBadge {
19
+ label: string;
20
+ n: number | undefined;
21
+ }
22
+
23
+ /** "0" → "no hits"; "12" → "12 hits"; non-numeric text passes through verbatim. */
24
+ const hitsBadge = (
25
+ hits: string | number | undefined
26
+ ): HitsBadge | undefined => {
27
+ if (hits === undefined) {
28
+ return undefined;
29
+ }
30
+ const text = String(hits);
31
+ const n = typeof hits === "number" ? hits : Math.trunc(Number(text));
32
+ if (Number.isNaN(n)) {
33
+ return { label: text, n: undefined };
34
+ }
35
+ return { label: n === 0 ? "no hits" : `${text} hit${n === 1 ? "" : "s"}`, n };
36
+ };
37
+
38
+ export const Search = defineComponent(
39
+ {
40
+ description:
41
+ "検索クエリ1行。pattern=検索パターン(必須)、path=対象スコープ、tool=ツール名 (rg/grep 等)、hits=ヒット数(0 は no hits、省略可)。children は注記",
42
+ schema: v.looseObject({
43
+ hits: v.optional(NUMISH),
44
+ path: v.optional(v.string()),
45
+ pattern: v.string(),
46
+ tool: v.optional(v.string()),
47
+ }),
48
+ },
49
+ ({ pattern, path, tool, hits, children }) => {
50
+ const badge = hitsBadge(hits);
51
+ return (
52
+ <ListRow>
53
+ <RowIcon name="lucide:search" />
54
+ <code className={MONO_CLS}>{pattern}</code>
55
+ {nonEmpty(path) ? <span className={LOC_CLS}>in {path}</span> : null}
56
+ {nonEmpty(tool) ? <span className={MONO_TAG_CLS}>{tool}</span> : null}
57
+ {badge === undefined ? null : (
58
+ <Pill
59
+ className={`shrink-0 ${
60
+ badge.n !== undefined && badge.n > 0 ? TONE.emerald : TONE.neutral
61
+ }`}
62
+ >
63
+ {badge.label}
64
+ </Pill>
65
+ )}
66
+ <RowNote>{children}</RowNote>
67
+ </ListRow>
68
+ );
69
+ }
70
+ );
71
+
72
+ const isSearchEl = (node: ReactNode): node is ReactElement =>
73
+ isEl(node, Search);
74
+
75
+ /** A <Search> child's parsed hit count (0 when the badge is absent). */
76
+ const searchHits = (el: ReactElement): number => {
77
+ const h = propOf(el, "hits");
78
+ return typeof h === "string" || typeof h === "number"
79
+ ? (hitsBadge(h)?.n ?? 0)
80
+ : 0;
81
+ };
82
+
83
+ const summarizeSearches = (
84
+ children: ReactNode
85
+ ): { count: number; hits: number } => {
86
+ let count = 0;
87
+ let hits = 0;
88
+ for (const node of flattenChildren(children)) {
89
+ if (!isSearchEl(node)) {
90
+ continue;
91
+ }
92
+ count += 1;
93
+ hits += searchHits(node);
94
+ }
95
+ return { count, hits };
96
+ };
97
+
98
+ const searchSummary = (count: number, hits: number): string =>
99
+ `${count} search${count === 1 ? "" : "es"}${hits > 0 ? ` · ${hits} hits` : ""}`;
100
+
101
+ export const Searches = defineComponent(
102
+ {
103
+ description:
104
+ "検索クエリログのコンテナ。<Search> を並べる。title でキャプションバー、件数とヒット合計を上部に表示",
105
+ schema: v.looseObject(TITLE_PROP),
106
+ },
107
+ ({ title, children }) => {
108
+ const { count, hits } = summarizeSearches(children);
109
+ return (
110
+ <ListPanel title={title}>
111
+ {count > 0 ? (
112
+ <div className={`${SURFACE_CLS} px-4 py-1.5 text-xs ${TEXT.muted}`}>
113
+ {searchSummary(count, hits)}
114
+ </div>
115
+ ) : null}
116
+ {children}
117
+ </ListPanel>
118
+ );
119
+ }
120
+ );
@@ -0,0 +1,38 @@
1
+ import type { ReactElement } from "react";
2
+ import * as v from "valibot";
3
+
4
+ import { defineComponent } from "../define.js";
5
+ import { nonEmpty } from "../guards.js";
6
+ import { BOOLISH_PROP } from "./attrs.js";
7
+ import { CODE_CHIP_CLS } from "./bits.js";
8
+ import { CHART_TONES } from "./chart.js";
9
+
10
+ /**
11
+ * Named data series shared by <LineChart> and <Radar>: `values` is a
12
+ * comma-separated number list aligned with the chart's `labels`/`axes`.
13
+ */
14
+
15
+ export const SERIES_SCHEMA = v.looseObject({
16
+ dash: BOOLISH_PROP,
17
+ name: v.string(),
18
+ tone: v.optional(v.picklist(CHART_TONES)),
19
+ values: v.string(),
20
+ });
21
+
22
+ export type SeriesSpec = v.InferOutput<typeof SERIES_SCHEMA>;
23
+
24
+ export const Series = defineComponent(
25
+ {
26
+ description:
27
+ 'データ系列。<LineChart>/<Radar> の子。name/values は必須 (values="12,8,4" のカンマ区切り)、tone で色、dash で破線',
28
+ schema: SERIES_SCHEMA,
29
+ },
30
+ // Standalone use: a `name values` chip so misplaced series still render.
31
+ ({ name, values, dash }): ReactElement => (
32
+ <span className={`font-mono ${CODE_CHIP_CLS}`}>
33
+ {name}
34
+ <span className="opacity-70">{values}</span>
35
+ {nonEmpty(dash) ? <span className="opacity-70">dash</span> : null}
36
+ </span>
37
+ )
38
+ );
@@ -0,0 +1,69 @@
1
+ import * as v from "valibot";
2
+
3
+ import { defineComponent } from "../define.js";
4
+ import { isOneOf } from "../guards.js";
5
+ import { Pill } from "./bits.js";
6
+ import { TONE } from "./tones.js";
7
+
8
+ export const SEVERITY_LEVELS = [
9
+ "critical",
10
+ "high",
11
+ "medium",
12
+ "low",
13
+ "info",
14
+ ] as const;
15
+ export type SeverityLevel = (typeof SEVERITY_LEVELS)[number];
16
+
17
+ export const isSeverity = isOneOf(SEVERITY_LEVELS);
18
+
19
+ /** Optional `severity` schema entry — shared by Comment, Vuln and Incident. */
20
+ export const SEVERITY_PROP = v.optional(v.picklist(SEVERITY_LEVELS), "medium");
21
+
22
+ export const SEVERITY_STYLES: Record<
23
+ SeverityLevel,
24
+ { chip: string; icon: string; label: string }
25
+ > = {
26
+ critical: {
27
+ chip: TONE.red,
28
+ icon: "lucide:siren",
29
+ label: "Critical",
30
+ },
31
+ high: {
32
+ chip: TONE.orange,
33
+ icon: "lucide:flame",
34
+ label: "High",
35
+ },
36
+ info: {
37
+ chip: TONE.neutral,
38
+ icon: "lucide:info",
39
+ label: "Info",
40
+ },
41
+ low: {
42
+ chip: TONE.sky,
43
+ icon: "lucide:circle-minus",
44
+ label: "Low",
45
+ },
46
+ medium: {
47
+ chip: TONE.amber,
48
+ icon: "lucide:triangle-alert",
49
+ label: "Medium",
50
+ },
51
+ };
52
+
53
+ export const Severity = defineComponent(
54
+ {
55
+ description:
56
+ "重要度ピル。level は critical|high|medium|low|info。レビュー指摘・脆弱性・インシデントの深刻度を示すインラインチップ",
57
+ schema: v.looseObject({
58
+ level: v.optional(v.picklist(SEVERITY_LEVELS), "medium"),
59
+ }),
60
+ },
61
+ ({ level, children }) => {
62
+ const s = SEVERITY_STYLES[level];
63
+ return (
64
+ <Pill className={s.chip} icon={s.icon}>
65
+ {children ?? s.label}
66
+ </Pill>
67
+ );
68
+ }
69
+ );
@@ -0,0 +1,216 @@
1
+ import * as v from "valibot";
2
+
3
+ import * as accordion from "../components/ui/accordion.js";
4
+ import * as alertDialog from "../components/ui/alert-dialog.js";
5
+ import * as alert from "../components/ui/alert.js";
6
+ import * as aspectRatio from "../components/ui/aspect-ratio.js";
7
+ import * as attachment from "../components/ui/attachment.js";
8
+ import * as avatar from "../components/ui/avatar.js";
9
+ import * as badge from "../components/ui/badge.js";
10
+ import * as breadcrumb from "../components/ui/breadcrumb.js";
11
+ import * as bubble from "../components/ui/bubble.js";
12
+ import * as buttonGroup from "../components/ui/button-group.js";
13
+ import * as button from "../components/ui/button.js";
14
+ import * as calendar from "../components/ui/calendar.js";
15
+ import * as card from "../components/ui/card.js";
16
+ import * as carousel from "../components/ui/carousel.js";
17
+ import * as chart from "../components/ui/chart.js";
18
+ import * as checkbox from "../components/ui/checkbox.js";
19
+ import * as collapsible from "../components/ui/collapsible.js";
20
+ import * as combobox from "../components/ui/combobox.js";
21
+ import * as command from "../components/ui/command.js";
22
+ import * as contextMenu from "../components/ui/context-menu.js";
23
+ import * as dialog from "../components/ui/dialog.js";
24
+ import * as direction from "../components/ui/direction.js";
25
+ import * as drawer from "../components/ui/drawer.js";
26
+ import * as dropdownMenu from "../components/ui/dropdown-menu.js";
27
+ import * as empty from "../components/ui/empty.js";
28
+ import * as field from "../components/ui/field.js";
29
+ import * as fieldset from "../components/ui/fieldset.js";
30
+ import * as frame from "../components/ui/frame.js";
31
+ import * as hoverCard from "../components/ui/hover-card.js";
32
+ import * as inputGroup from "../components/ui/input-group.js";
33
+ import * as inputOtp from "../components/ui/input-otp.js";
34
+ import * as input from "../components/ui/input.js";
35
+ import * as item from "../components/ui/item.js";
36
+ import * as kbd from "../components/ui/kbd.js";
37
+ import * as label from "../components/ui/label.js";
38
+ import * as marker from "../components/ui/marker.js";
39
+ import * as menubar from "../components/ui/menubar.js";
40
+ import * as messageScroller from "../components/ui/message-scroller.js";
41
+ import * as message from "../components/ui/message.js";
42
+ import * as meter from "../components/ui/meter.js";
43
+ import * as nativeSelect from "../components/ui/native-select.js";
44
+ import * as navigationMenu from "../components/ui/navigation-menu.js";
45
+ import * as pagination from "../components/ui/pagination.js";
46
+ import * as popover from "../components/ui/popover.js";
47
+ import * as progress from "../components/ui/progress.js";
48
+ import * as questionnaire from "../components/ui/questionnaire.js";
49
+ import * as radioGroup from "../components/ui/radio-group.js";
50
+ import * as resizable from "../components/ui/resizable.js";
51
+ import * as scrollArea from "../components/ui/scroll-area.js";
52
+ import * as select from "../components/ui/select.js";
53
+ import * as separator from "../components/ui/separator.js";
54
+ import * as sheet from "../components/ui/sheet.js";
55
+ import * as sidebar from "../components/ui/sidebar.js";
56
+ import * as skeleton from "../components/ui/skeleton.js";
57
+ import * as slider from "../components/ui/slider.js";
58
+ import * as spinner from "../components/ui/spinner.js";
59
+ import * as switch_ from "../components/ui/switch.js";
60
+ import * as table from "../components/ui/table.js";
61
+ import * as tabs from "../components/ui/tabs.js";
62
+ import * as textarea from "../components/ui/textarea.js";
63
+ import * as toast from "../components/ui/toast.js";
64
+ import * as toggleGroup from "../components/ui/toggle-group.js";
65
+ import * as toggle from "../components/ui/toggle.js";
66
+ import * as tooltip from "../components/ui/tooltip.js";
67
+ import type { AnyComponent, ComponentMap, DocProps } from "../define.js";
68
+ import { defineComponent } from "../define.js";
69
+ import { isComponent, safeHref } from "../guards.js";
70
+
71
+ /**
72
+ * URL-bearing props get a scriptable-scheme check before reaching the DOM —
73
+ * `looseObject` props mean `<BreadcrumbLink href="javascript:…">` would
74
+ * otherwise land on `<a href>` unchecked.
75
+ */
76
+ const URL_PROPS = new Set([
77
+ "action",
78
+ "background",
79
+ "data",
80
+ "formaction",
81
+ "href",
82
+ "poster",
83
+ "src",
84
+ "xlinkhref",
85
+ ]);
86
+
87
+ const sanitizeProps = (props: Record<string, unknown>): DocProps => {
88
+ const out: Record<string, unknown> = { ...props };
89
+ for (const k of Object.keys(out)) {
90
+ if (URL_PROPS.has(k.toLowerCase()) && typeof out[k] === "string") {
91
+ out[k] = safeHref(out[k]);
92
+ }
93
+ }
94
+ return out;
95
+ };
96
+
97
+ const MODULES: Record<string, Record<string, unknown>> = {
98
+ accordion,
99
+ alert,
100
+ "alert-dialog": alertDialog,
101
+ "aspect-ratio": aspectRatio,
102
+ attachment,
103
+ avatar,
104
+ badge,
105
+ breadcrumb,
106
+ bubble,
107
+ button,
108
+ "button-group": buttonGroup,
109
+ calendar,
110
+ card,
111
+ carousel,
112
+ chart,
113
+ checkbox,
114
+ collapsible,
115
+ combobox,
116
+ command,
117
+ "context-menu": contextMenu,
118
+ dialog,
119
+ direction,
120
+ drawer,
121
+ "dropdown-menu": dropdownMenu,
122
+ empty,
123
+ field,
124
+ fieldset,
125
+ frame,
126
+ "hover-card": hoverCard,
127
+ input,
128
+ "input-group": inputGroup,
129
+ "input-otp": inputOtp,
130
+ item,
131
+ kbd,
132
+ label,
133
+ marker,
134
+ menubar,
135
+ message,
136
+ "message-scroller": messageScroller,
137
+ meter,
138
+ "native-select": nativeSelect,
139
+ "navigation-menu": navigationMenu,
140
+ pagination,
141
+ popover,
142
+ progress,
143
+ questionnaire,
144
+ "radio-group": radioGroup,
145
+ resizable,
146
+ "scroll-area": scrollArea,
147
+ select,
148
+ separator,
149
+ sheet,
150
+ sidebar,
151
+ skeleton,
152
+ slider,
153
+ spinner,
154
+ switch: switch_,
155
+ table,
156
+ tabs,
157
+ textarea,
158
+ toast,
159
+ toggle,
160
+ "toggle-group": toggleGroup,
161
+ tooltip,
162
+ };
163
+
164
+ /**
165
+ * Wrap a shadcn/ui (Base UI) component for the document catalog. Props stay
166
+ * loose — MDX attributes arrive as strings — and each entry gets a catalog
167
+ * description noting that interactive parts come alive via hydration.
168
+ */
169
+ const wrap = (
170
+ moduleName: string,
171
+ exportName: string,
172
+ Comp: AnyComponent
173
+ ): AnyComponent =>
174
+ defineComponent(
175
+ {
176
+ description: `shadcn/ui ${exportName} (${moduleName}) — Base UI; interactive after hydration.`,
177
+ schema: v.looseObject({}),
178
+ },
179
+ (props) => <Comp {...sanitizeProps(props)} />
180
+ );
181
+
182
+ /**
183
+ * `[moduleName, exportName, component]` for every PascalCase function export
184
+ * of `src/components/ui/*`. Lowercase helpers (cva variants, hooks) and
185
+ * non-component constants are skipped by the `isComponent` + name filter.
186
+ */
187
+ const componentEntries = (): [string, string, AnyComponent][] =>
188
+ Object.entries(MODULES).flatMap(([moduleName, mod]) =>
189
+ Object.entries(mod).flatMap(
190
+ ([name, comp]): [string, string, AnyComponent][] =>
191
+ /^[A-Z]/u.test(name) && isComponent(comp)
192
+ ? [[moduleName, name, comp]]
193
+ : []
194
+ )
195
+ );
196
+
197
+ /** Component catalog entries, keyed by export name. */
198
+ export const shadcnComponents: ComponentMap = Object.fromEntries(
199
+ componentEntries().map(([moduleName, name, comp]) => [
200
+ name,
201
+ wrap(moduleName, name, comp),
202
+ ])
203
+ );
204
+
205
+ /**
206
+ * shadcn export name → module specifier relative to this directory
207
+ * (`../components/ui/<name>.js`). The hydration bundle resolves each used
208
+ * component straight to its leaf module — the barrel would defeat
209
+ * tree-shaking.
210
+ */
211
+ export const shadcnModules: Record<string, string> = Object.fromEntries(
212
+ componentEntries().map(([moduleName, name]) => [
213
+ name,
214
+ `../components/ui/${moduleName}.js`,
215
+ ])
216
+ );
@@ -0,0 +1,86 @@
1
+ import * as v from "valibot";
2
+
3
+ import { defineComponent } from "../define.js";
4
+ import { nonEmpty } from "../guards.js";
5
+ import { numOf } from "./attrs.js";
6
+ import { TONE_TEXT } from "./tones.js";
7
+
8
+ export const SPARK_TONES = [
9
+ "neutral",
10
+ "emerald",
11
+ "amber",
12
+ "red",
13
+ "sky",
14
+ "violet",
15
+ ] as const;
16
+ export type SparkTone = (typeof SPARK_TONES)[number];
17
+
18
+ const PAD = 1.5;
19
+ const W = 100;
20
+ const H = 24;
21
+
22
+ /** `"3, 8, 2, 12"` / `"3 8 2 12"` → finite numbers. */
23
+ const parseValues = (x: unknown): number[] => {
24
+ if (typeof x !== "string") {
25
+ return [];
26
+ }
27
+ return x
28
+ .split(/[\s,]+/u)
29
+ .map((t) => numOf(t))
30
+ .filter((n): n is number => n !== undefined);
31
+ };
32
+
33
+ export const Spark = defineComponent(
34
+ {
35
+ description:
36
+ 'インラインスパークライン (小さな折れ線 SVG)。values に数値列 ("3,8,2,12,5")、tone で色、label で aria ラベル。本文中のトレンド表示向け',
37
+ schema: v.looseObject({
38
+ label: v.optional(v.string()),
39
+ tone: v.optional(v.picklist(SPARK_TONES), "neutral"),
40
+ values: v.string(),
41
+ }),
42
+ },
43
+ ({ values, tone, label }) => {
44
+ const pts = parseValues(values);
45
+ if (pts.length < 2) {
46
+ return null;
47
+ }
48
+ const min = Math.min(...pts);
49
+ const max = Math.max(...pts);
50
+ const span = max - min || 1;
51
+ const stepX = (W - PAD * 2) / (pts.length - 1);
52
+ const points = pts
53
+ .map(
54
+ (p, i) =>
55
+ `${(PAD + i * stepX).toFixed(2)},${(
56
+ PAD +
57
+ (1 - (p - min) / span) * (H - PAD * 2)
58
+ ).toFixed(2)}`
59
+ )
60
+ .join(" ");
61
+ return (
62
+ <span
63
+ className={`not-prose mx-0.5 inline-flex align-middle ${TONE_TEXT[tone]}`}
64
+ >
65
+ <svg
66
+ aria-hidden={!nonEmpty(label)}
67
+ aria-label={nonEmpty(label) ? label : undefined}
68
+ className="h-4 w-16"
69
+ preserveAspectRatio="none"
70
+ role={nonEmpty(label) ? "img" : undefined}
71
+ viewBox={`0 0 ${W} ${H}`}
72
+ >
73
+ <polyline
74
+ fill="none"
75
+ points={points}
76
+ stroke="currentColor"
77
+ strokeLinecap="round"
78
+ strokeLinejoin="round"
79
+ strokeWidth="2"
80
+ vectorEffect="non-scaling-stroke"
81
+ />
82
+ </svg>
83
+ </span>
84
+ );
85
+ }
86
+ );
@@ -0,0 +1,57 @@
1
+ import * as v from "valibot";
2
+
3
+ import { defineComponent } from "../define.js";
4
+ import { GAPS } from "./layout.js";
5
+ import { TRIM_CLS } from "./tones.js";
6
+
7
+ const ITEMS: Record<string, string> = {
8
+ baseline: "items-baseline",
9
+ center: "items-center",
10
+ end: "items-end",
11
+ start: "items-start",
12
+ stretch: "",
13
+ };
14
+
15
+ export const Stack = defineComponent(
16
+ {
17
+ description:
18
+ "縦積みレイアウト。余白を持たないコンポーネント (Input/Textarea 等の shadcn/ui プリミティブ) を gap 付きで縦に並べる",
19
+ schema: v.looseObject({
20
+ className: v.optional(v.string()),
21
+ gap: v.optional(v.picklist(Object.keys(GAPS)), "md"),
22
+ items: v.optional(
23
+ v.picklist(["stretch", "start", "center", "end", "baseline"]),
24
+ "stretch"
25
+ ),
26
+ }),
27
+ },
28
+ ({ gap, items, className, children }) => (
29
+ <div
30
+ className={`my-6 flex flex-col ${GAPS[gap]} ${ITEMS[items]} ${className ?? ""} ${TRIM_CLS}`}
31
+ >
32
+ {children}
33
+ </div>
34
+ )
35
+ );
36
+
37
+ export const Row = defineComponent(
38
+ {
39
+ description:
40
+ "横並びレイアウト。Button/Badge などのインライン系コンポーネントを gap 付きで並べ、幅が足りなければ折り返す",
41
+ schema: v.looseObject({
42
+ className: v.optional(v.string()),
43
+ gap: v.optional(v.picklist(Object.keys(GAPS)), "sm"),
44
+ items: v.optional(
45
+ v.picklist(["stretch", "start", "center", "end", "baseline"]),
46
+ "center"
47
+ ),
48
+ }),
49
+ },
50
+ ({ gap, items, className, children }) => (
51
+ <div
52
+ className={`my-6 flex flex-wrap ${ITEMS[items]} ${GAPS[gap]} ${className ?? ""} ${TRIM_CLS}`}
53
+ >
54
+ {children}
55
+ </div>
56
+ )
57
+ );