@gunjo/ui 0.0.1-alpha.0 → 0.0.1-alpha.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 (224) hide show
  1. package/LICENSE +21 -0
  2. package/README.ja.md +90 -0
  3. package/README.md +52 -91
  4. package/package.json +47 -6
  5. package/src/components/display/Accordion.tsx +185 -0
  6. package/src/components/display/AccordionGroup.tsx +155 -0
  7. package/src/components/display/ActionDataTable.tsx +413 -0
  8. package/src/components/display/ActivityTimelineCard.tsx +483 -0
  9. package/src/components/display/AnalyticsCard.tsx +167 -0
  10. package/src/components/display/AssetCard.tsx +242 -0
  11. package/src/components/display/AssetGrid.tsx +164 -0
  12. package/src/components/display/Avatar.tsx +127 -0
  13. package/src/components/display/AvatarGroup.tsx +131 -0
  14. package/src/components/{atoms → display}/Badge.tsx +3 -3
  15. package/src/components/display/BarChart.tsx +247 -0
  16. package/src/components/{molecules → display}/Card.tsx +1 -1
  17. package/src/components/display/Carousel.tsx +593 -0
  18. package/src/components/display/ChartLegend.tsx +124 -0
  19. package/src/components/display/ChatMessage.tsx +382 -0
  20. package/src/components/display/ChoroplethMap.tsx +613 -0
  21. package/src/components/display/Code.tsx +42 -0
  22. package/src/components/display/CodeBlock.tsx +338 -0
  23. package/src/components/display/ColorSwatch.tsx +71 -0
  24. package/src/components/display/ConcentricProgressCard.tsx +545 -0
  25. package/src/components/display/DataTable.tsx +522 -0
  26. package/src/components/display/DistributionBar.tsx +102 -0
  27. package/src/components/display/DocNote.tsx +36 -0
  28. package/src/components/display/DonutChart.tsx +257 -0
  29. package/src/components/display/EmptyState.tsx +44 -0
  30. package/src/components/display/FileTree.tsx +180 -0
  31. package/src/components/display/GaugeChart.tsx +219 -0
  32. package/src/components/display/HeatmapChart.tsx +266 -0
  33. package/src/components/display/Icon.tsx +66 -0
  34. package/src/components/display/ImagePreview.tsx +140 -0
  35. package/src/components/{atoms → display}/Img.tsx +46 -12
  36. package/src/components/display/LabeledDonutCard.tsx +475 -0
  37. package/src/components/display/LineChart.tsx +464 -0
  38. package/src/components/{molecules → display}/List.tsx +20 -13
  39. package/src/components/display/MarkdownRenderer.tsx +157 -0
  40. package/src/components/display/MetadataList.tsx +81 -0
  41. package/src/components/display/MiniDistributionBarCard.tsx +314 -0
  42. package/src/components/display/PieChart.tsx +234 -0
  43. package/src/components/display/QuadrantMatrix.tsx +330 -0
  44. package/src/components/display/RadarChart.tsx +335 -0
  45. package/src/components/display/RadialBarChart.tsx +264 -0
  46. package/src/components/display/RetentionCohortCard.tsx +350 -0
  47. package/src/components/display/RibbonChart.tsx +618 -0
  48. package/src/components/display/SearchableAccordion.tsx +270 -0
  49. package/src/components/display/SegmentTimelineCard.tsx +452 -0
  50. package/src/components/display/SegmentedGaugeCard.tsx +607 -0
  51. package/src/components/display/Spacer.tsx +51 -0
  52. package/src/components/display/SparklineChart.tsx +394 -0
  53. package/src/components/display/StackedBarChart.tsx +393 -0
  54. package/src/components/display/Statistic.tsx +70 -0
  55. package/src/components/{molecules → display}/Table.tsx +22 -7
  56. package/src/components/display/Tag.tsx +80 -0
  57. package/src/components/display/TagEditor.tsx +141 -0
  58. package/src/components/display/Timeline.tsx +121 -0
  59. package/src/components/{atoms → display}/ToolPill.tsx +42 -18
  60. package/src/components/display/TreeView.tsx +226 -0
  61. package/src/components/display/chart-tooltip.tsx +423 -0
  62. package/src/components/display/chart-utils.ts +71 -0
  63. package/src/components/display/circular-chart-utils.ts +147 -0
  64. package/src/components/display/generated/default-variant-keys.ts +90 -0
  65. package/src/components/display/generated/variant-keys.ts +169 -0
  66. package/src/components/{atoms → feedback}/Alert.tsx +12 -5
  67. package/src/components/feedback/Banner.tsx +90 -0
  68. package/src/components/{molecules → feedback}/NotificationCenter.tsx +64 -31
  69. package/src/components/feedback/ProgressWidget.tsx +44 -0
  70. package/src/components/{atoms → feedback}/Spinner.tsx +2 -2
  71. package/src/components/{molecules → feedback}/StatusBar.tsx +4 -4
  72. package/src/components/feedback/StatusScreen.tsx +148 -0
  73. package/src/components/{molecules → feedback}/Stepper.tsx +10 -5
  74. package/src/components/feedback/Toast.tsx +108 -0
  75. package/src/components/feedback/ToastProvider.tsx +78 -0
  76. package/src/components/feedback/generated/default-variant-keys.ts +16 -0
  77. package/src/components/feedback/generated/variant-keys.ts +21 -0
  78. package/src/components/generated/component-manifest.ts +1568 -454
  79. package/src/components/generated/component-style-hints.ts +1958 -718
  80. package/src/components/{atoms → inputs}/ButtonVariants.ts +13 -3
  81. package/src/components/inputs/Calendar.tsx +212 -0
  82. package/src/components/inputs/ChatComposer.tsx +75 -0
  83. package/src/components/inputs/ChatInput.tsx +528 -0
  84. package/src/components/{atoms → inputs}/Checkbox.tsx +2 -2
  85. package/src/components/inputs/Combobox.tsx +175 -0
  86. package/src/components/inputs/CopyButton.tsx +187 -0
  87. package/src/components/inputs/DatePicker.tsx +519 -0
  88. package/src/components/inputs/DateRangePicker.tsx +878 -0
  89. package/src/components/inputs/EditableField.tsx +182 -0
  90. package/src/components/{organisms → inputs}/FileUploader.tsx +24 -9
  91. package/src/components/inputs/FilterButton.tsx +163 -0
  92. package/src/components/{molecules → inputs}/Form.tsx +20 -3
  93. package/src/components/{atoms → inputs}/Input.tsx +2 -0
  94. package/src/components/inputs/InputOTP.tsx +75 -0
  95. package/src/components/inputs/Mention.tsx +279 -0
  96. package/src/components/inputs/NumberInput.tsx +109 -0
  97. package/src/components/inputs/PasswordGroup.tsx +138 -0
  98. package/src/components/inputs/PasswordInput.tsx +74 -0
  99. package/src/components/inputs/PasswordRequirementList.tsx +96 -0
  100. package/src/components/inputs/PasswordStrengthMeter.tsx +93 -0
  101. package/src/components/inputs/PhoneInput.tsx +99 -0
  102. package/src/components/inputs/PostalCodeInput.tsx +98 -0
  103. package/src/components/inputs/RangeSlider.tsx +129 -0
  104. package/src/components/inputs/SearchInput.tsx +76 -0
  105. package/src/components/inputs/Select.tsx +39 -0
  106. package/src/components/{atoms → inputs}/Slider.tsx +18 -5
  107. package/src/components/{molecules → inputs}/SortButton.tsx +5 -2
  108. package/src/components/{atoms → inputs}/Switch.tsx +15 -4
  109. package/src/components/inputs/TagInput.tsx +114 -0
  110. package/src/components/{atoms → inputs}/Textarea.tsx +1 -0
  111. package/src/components/inputs/TimePicker.tsx +150 -0
  112. package/src/components/inputs/Toggle.tsx +48 -0
  113. package/src/components/{atoms → inputs}/ToggleGroup.tsx +2 -2
  114. package/src/components/inputs/TooltipButton.tsx +148 -0
  115. package/src/components/inputs/VoiceInputButton.tsx +317 -0
  116. package/src/components/inputs/calendar-holidays.ts +56 -0
  117. package/src/components/inputs/generated/default-variant-keys.ts +32 -0
  118. package/src/components/{atoms → inputs}/generated/variant-keys.ts +19 -27
  119. package/src/components/layout/AspectRatio.tsx +12 -0
  120. package/src/components/layout/AssetInspectorPanel.tsx +416 -0
  121. package/src/components/layout/Cluster.tsx +56 -0
  122. package/src/components/layout/CollapsiblePanelToggle.tsx +94 -0
  123. package/src/components/layout/Container.tsx +43 -0
  124. package/src/components/layout/DeviceFrame.tsx +227 -0
  125. package/src/components/layout/Grid.tsx +65 -0
  126. package/src/components/layout/HStack.tsx +73 -0
  127. package/src/components/{organisms → layout}/InspectorPanel.tsx +6 -5
  128. package/src/components/layout/MarqueeFrame.tsx +158 -0
  129. package/src/components/layout/Resizable.tsx +94 -0
  130. package/src/components/layout/ScrollArea.tsx +71 -0
  131. package/src/components/{organisms → layout}/SpatialCanvas.tsx +12 -7
  132. package/src/components/layout/VStack.tsx +69 -0
  133. package/src/components/layout/generated/default-variant-keys.ts +16 -0
  134. package/src/components/layout/generated/variant-keys.ts +21 -0
  135. package/src/components/{molecules → navigation}/Breadcrumb.tsx +5 -4
  136. package/src/components/navigation/Command.tsx +266 -0
  137. package/src/components/navigation/CommandPalette.tsx +83 -0
  138. package/src/components/navigation/DocumentPager.tsx +171 -0
  139. package/src/components/navigation/Footer.tsx +88 -0
  140. package/src/components/navigation/Header.tsx +80 -0
  141. package/src/components/{molecules → navigation}/Menubar.tsx +45 -12
  142. package/src/components/navigation/NavigationMenu.tsx +128 -0
  143. package/src/components/navigation/PageAside.tsx +84 -0
  144. package/src/components/{molecules → navigation}/Pagination.tsx +60 -7
  145. package/src/components/{organisms → navigation}/RightRail.tsx +1 -1
  146. package/src/components/navigation/Sidebar.tsx +223 -0
  147. package/src/components/navigation/SidebarItem.tsx +160 -0
  148. package/src/components/{molecules → navigation}/Tabs.tsx +2 -2
  149. package/src/components/navigation/TextLink.tsx +71 -0
  150. package/src/components/navigation/generated/default-variant-keys.ts +12 -0
  151. package/src/components/navigation/generated/variant-keys.ts +13 -0
  152. package/src/components/overlay/AIChatInput.tsx +5 -0
  153. package/src/components/overlay/AIChatMessage.tsx +6 -0
  154. package/src/components/overlay/AlertDialog.tsx +145 -0
  155. package/src/components/overlay/ChatPanel.tsx +180 -0
  156. package/src/components/{molecules → overlay}/ContextMenu.tsx +65 -29
  157. package/src/components/{molecules → overlay}/Dialog.tsx +21 -13
  158. package/src/components/overlay/Drawer.tsx +131 -0
  159. package/src/components/{molecules → overlay}/DropdownMenu.tsx +52 -17
  160. package/src/components/overlay/FloatingPanel.tsx +90 -0
  161. package/src/components/overlay/HoverCard.tsx +36 -0
  162. package/src/components/overlay/MediaLightbox.tsx +403 -0
  163. package/src/components/overlay/MediaPickerDialog.tsx +198 -0
  164. package/src/components/overlay/Modal.tsx +103 -0
  165. package/src/components/overlay/OnboardingFlow.tsx +172 -0
  166. package/src/components/overlay/Popover.tsx +36 -0
  167. package/src/components/overlay/ShareModal.tsx +324 -0
  168. package/src/components/{molecules → overlay}/Sheet.tsx +76 -19
  169. package/src/components/overlay/Tooltip.tsx +130 -0
  170. package/src/components/overlay/generated/default-variant-keys.ts +14 -0
  171. package/src/components/overlay/generated/variant-keys.ts +17 -0
  172. package/src/components/patterns/BlogTemplate.tsx +46 -0
  173. package/src/components/{templates → patterns}/DashboardTemplate.tsx +2 -2
  174. package/src/components/patterns/DocsTemplate.tsx +41 -0
  175. package/src/components/{templates → patterns}/MediaLibraryTemplate.tsx +1 -1
  176. package/src/components/patterns/OnboardingTemplate.tsx +32 -0
  177. package/src/components/patterns/PricingTemplate.tsx +106 -0
  178. package/src/globals.css +173 -22
  179. package/src/index.ts +177 -76
  180. package/tailwind-theme-extend.cjs +48 -3
  181. package/design/atoms-metadata.json +0 -82
  182. package/design/molecules-metadata.json +0 -130
  183. package/design/organisms-metadata.json +0 -38
  184. package/design/templates-metadata.json +0 -38
  185. package/src/components/atoms/Avatar.tsx +0 -57
  186. package/src/components/atoms/Select.tsx +0 -28
  187. package/src/components/atoms/generated/default-variant-keys.ts +0 -36
  188. package/src/components/molecules/AIChatInput.tsx +0 -140
  189. package/src/components/molecules/AIChatMessage.tsx +0 -109
  190. package/src/components/molecules/Accordion.tsx +0 -99
  191. package/src/components/molecules/Calendar.tsx +0 -60
  192. package/src/components/molecules/Carousel.tsx +0 -261
  193. package/src/components/molecules/Command.tsx +0 -152
  194. package/src/components/molecules/FilterButton.tsx +0 -133
  195. package/src/components/molecules/HoverCard.tsx +0 -29
  196. package/src/components/molecules/Modal.tsx +0 -66
  197. package/src/components/molecules/Popover.tsx +0 -31
  198. package/src/components/molecules/ProgressWidget.tsx +0 -40
  199. package/src/components/molecules/Resizable.tsx +0 -47
  200. package/src/components/molecules/ScrollArea.tsx +0 -48
  201. package/src/components/molecules/SidebarItem.tsx +0 -134
  202. package/src/components/molecules/Toast.tsx +0 -57
  203. package/src/components/molecules/Tooltip.tsx +0 -30
  204. package/src/components/molecules/generated/default-variant-keys.ts +0 -22
  205. package/src/components/molecules/generated/variant-keys.ts +0 -33
  206. package/src/components/organisms/CommandPalette.tsx +0 -58
  207. package/src/components/organisms/FloatingPanel.tsx +0 -46
  208. package/src/components/organisms/ShareModal.tsx +0 -182
  209. package/src/components/organisms/ToastProvider.tsx +0 -49
  210. /package/src/components/{atoms → display}/Kbd.tsx +0 -0
  211. /package/src/components/{atoms → display}/Separator.tsx +0 -0
  212. /package/src/components/{atoms → display}/Skeleton.tsx +0 -0
  213. /package/src/components/{atoms → feedback}/Progress.tsx +0 -0
  214. /package/src/components/{atoms → inputs}/Button.tsx +0 -0
  215. /package/src/components/{atoms → inputs}/Label.tsx +0 -0
  216. /package/src/components/{atoms → inputs}/RadioGroup.tsx +0 -0
  217. /package/src/components/{organisms → navigation}/AppRail.tsx +0 -0
  218. /package/src/components/{templates → patterns}/AuthTemplate.tsx +0 -0
  219. /package/src/components/{templates → patterns}/BannalyzeTemplate.tsx +0 -0
  220. /package/src/components/{templates → patterns}/ChatTemplate.tsx +0 -0
  221. /package/src/components/{templates → patterns}/EditorTemplate.tsx +0 -0
  222. /package/src/components/{templates → patterns}/KanbanTemplate.tsx +0 -0
  223. /package/src/components/{templates → patterns}/LandingTemplate.tsx +0 -0
  224. /package/src/components/{templates → patterns}/SettingsTemplate.tsx +0 -0
@@ -0,0 +1,317 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import {
5
+ IconLoader2 as Loader,
6
+ IconMicrophone as Microphone,
7
+ IconPlayerStopFilled as Stop,
8
+ } from "@tabler/icons-react";
9
+ import { cn } from "../../lib/utils";
10
+ import { Button, type ButtonProps } from "./Button";
11
+ import { Tooltip, TooltipContent, TooltipTrigger } from "../overlay/Tooltip";
12
+
13
+ type SpeechRecognitionEvent = Event & {
14
+ resultIndex: number;
15
+ results: SpeechRecognitionResultList;
16
+ };
17
+
18
+ type SpeechRecognitionErrorEvent = Event & {
19
+ error?: string;
20
+ };
21
+
22
+ type SpeechRecognitionInstance = EventTarget & {
23
+ lang: string;
24
+ continuous: boolean;
25
+ interimResults: boolean;
26
+ maxAlternatives: number;
27
+ onresult: ((event: SpeechRecognitionEvent) => void) | null;
28
+ onerror: ((event: SpeechRecognitionErrorEvent) => void) | null;
29
+ onend: (() => void) | null;
30
+ start: () => void;
31
+ stop: () => void;
32
+ abort: () => void;
33
+ };
34
+
35
+ type SpeechRecognitionConstructor = new () => SpeechRecognitionInstance;
36
+
37
+ declare global {
38
+ interface Window {
39
+ SpeechRecognition?: SpeechRecognitionConstructor;
40
+ webkitSpeechRecognition?: SpeechRecognitionConstructor;
41
+ }
42
+ }
43
+
44
+ export type VoiceInputStatus = "idle" | "requesting" | "listening" | "unsupported" | "permission-denied" | "error";
45
+
46
+ export interface VoiceInputButtonLabels {
47
+ start?: string;
48
+ stop?: string;
49
+ requesting?: string;
50
+ unsupported?: string;
51
+ permissionDenied?: string;
52
+ error?: string;
53
+ }
54
+
55
+ export interface VoiceInputButtonProps extends Omit<ButtonProps, "onClick" | "children"> {
56
+ onTranscript: (transcript: string) => void;
57
+ onListeningChange?: (listening: boolean) => void;
58
+ onStatusChange?: (status: VoiceInputStatus) => void;
59
+ onToggle?: () => void;
60
+ listening?: boolean;
61
+ defaultListening?: boolean;
62
+ language?: string;
63
+ labels?: VoiceInputButtonLabels;
64
+ }
65
+
66
+ function getSpeechRecognitionConstructor() {
67
+ if (typeof window === "undefined") return null;
68
+ return window.SpeechRecognition ?? window.webkitSpeechRecognition ?? null;
69
+ }
70
+
71
+ async function requestMicrophoneAccess(timeoutMs = 8000) {
72
+ if (!navigator.mediaDevices?.getUserMedia) {
73
+ const error = new Error("Microphone permission API is unavailable.");
74
+ error.name = "NotSupportedError";
75
+ throw error;
76
+ }
77
+
78
+ let timeoutId: number | null = null;
79
+ const timeout = new Promise<never>((_, reject) => {
80
+ timeoutId = window.setTimeout(() => {
81
+ const error = new Error("Microphone permission request timed out.");
82
+ error.name = "TimeoutError";
83
+ reject(error);
84
+ }, timeoutMs);
85
+ });
86
+
87
+ let stream: MediaStream;
88
+ try {
89
+ stream = await Promise.race([
90
+ navigator.mediaDevices.getUserMedia({ audio: true }),
91
+ timeout,
92
+ ]);
93
+ } finally {
94
+ if (timeoutId !== null) {
95
+ window.clearTimeout(timeoutId);
96
+ }
97
+ }
98
+
99
+ for (const track of stream.getTracks()) {
100
+ track.stop();
101
+ }
102
+ }
103
+
104
+ export function VoiceInputButton({
105
+ onTranscript,
106
+ onListeningChange,
107
+ onStatusChange,
108
+ onToggle,
109
+ listening,
110
+ defaultListening = false,
111
+ language = "ja-JP",
112
+ labels,
113
+ disabled,
114
+ className,
115
+ ...props
116
+ }: VoiceInputButtonProps) {
117
+ const [status, setStatus] = React.useState<VoiceInputStatus>("idle");
118
+ const [isSupported, setIsSupported] = React.useState(true);
119
+ const [uncontrolledListening, setUncontrolledListening] = React.useState(defaultListening);
120
+ const recognitionRef = React.useRef<SpeechRecognitionInstance | null>(null);
121
+ const stoppedByUserRef = React.useRef(false);
122
+ const finalTranscriptRef = React.useRef("");
123
+ const interimTranscriptRef = React.useRef("");
124
+ const statusRef = React.useRef<VoiceInputStatus>("idle");
125
+ const isListeningControlled = listening !== undefined;
126
+ const currentListening = isListeningControlled ? listening : uncontrolledListening;
127
+
128
+ const updateStatus = React.useCallback((nextStatus: VoiceInputStatus) => {
129
+ statusRef.current = nextStatus;
130
+ setStatus(nextStatus);
131
+ }, []);
132
+
133
+ const resolvedLabels = {
134
+ start: labels?.start ?? "音声入力",
135
+ stop: labels?.stop ?? "録音を停止",
136
+ requesting: labels?.requesting ?? "マイクのアクセス権を確認しています。",
137
+ unsupported: labels?.unsupported ?? "このブラウザは音声入力に対応していません。",
138
+ permissionDenied: labels?.permissionDenied ?? "マイクのアクセス権が許可されていません。",
139
+ error: labels?.error ?? "音声入力を開始できませんでした。",
140
+ };
141
+
142
+ React.useEffect(() => {
143
+ const supported = Boolean(getSpeechRecognitionConstructor());
144
+ setIsSupported(supported);
145
+ if (!supported) updateStatus("unsupported");
146
+ }, [updateStatus]);
147
+
148
+ React.useEffect(() => {
149
+ statusRef.current = status;
150
+ onStatusChange?.(status);
151
+ }, [onStatusChange, status]);
152
+
153
+ React.useEffect(() => {
154
+ return () => {
155
+ recognitionRef.current?.abort();
156
+ };
157
+ }, []);
158
+
159
+ const setListening = React.useCallback(
160
+ (nextListening: boolean) => {
161
+ if (!isListeningControlled) {
162
+ setUncontrolledListening(nextListening);
163
+ }
164
+ onListeningChange?.(nextListening);
165
+ },
166
+ [isListeningControlled, onListeningChange]
167
+ );
168
+
169
+ const emitTranscript = React.useCallback(() => {
170
+ const transcript = (finalTranscriptRef.current || interimTranscriptRef.current).trim();
171
+ finalTranscriptRef.current = "";
172
+ interimTranscriptRef.current = "";
173
+ if (transcript.length > 0) {
174
+ onTranscript(transcript);
175
+ }
176
+ }, [onTranscript]);
177
+
178
+ const stopRecognition = React.useCallback(() => {
179
+ stoppedByUserRef.current = true;
180
+ recognitionRef.current?.stop();
181
+ setListening(false);
182
+ updateStatus("idle");
183
+ }, [setListening, updateStatus]);
184
+
185
+ const startRecognition = React.useCallback(async () => {
186
+ const SpeechRecognition = getSpeechRecognitionConstructor();
187
+ if (!SpeechRecognition) {
188
+ setIsSupported(false);
189
+ updateStatus("unsupported");
190
+ return;
191
+ }
192
+
193
+ updateStatus("requesting");
194
+ stoppedByUserRef.current = false;
195
+ finalTranscriptRef.current = "";
196
+ interimTranscriptRef.current = "";
197
+
198
+ try {
199
+ await requestMicrophoneAccess();
200
+
201
+ const recognition = new SpeechRecognition();
202
+ recognition.lang = language;
203
+ recognition.continuous = false;
204
+ recognition.interimResults = true;
205
+ recognition.maxAlternatives = 1;
206
+ recognition.onresult = (event) => {
207
+ let interim = "";
208
+ let finalText = "";
209
+ for (let index = event.resultIndex; index < event.results.length; index += 1) {
210
+ const result = event.results[index];
211
+ const transcript = result[0]?.transcript ?? "";
212
+ if (result.isFinal) {
213
+ finalText += transcript;
214
+ } else {
215
+ interim += transcript;
216
+ }
217
+ }
218
+ if (finalText) {
219
+ finalTranscriptRef.current = `${finalTranscriptRef.current} ${finalText}`.trim();
220
+ }
221
+ interimTranscriptRef.current = interim.trim();
222
+ };
223
+ recognition.onerror = (event) => {
224
+ if (event.error === "not-allowed" || event.error === "service-not-allowed") {
225
+ updateStatus("permission-denied");
226
+ } else {
227
+ updateStatus("error");
228
+ }
229
+ setListening(false);
230
+ };
231
+ recognition.onend = () => {
232
+ emitTranscript();
233
+ recognitionRef.current = null;
234
+ setListening(false);
235
+ if (
236
+ stoppedByUserRef.current ||
237
+ statusRef.current === "listening" ||
238
+ statusRef.current === "requesting"
239
+ ) {
240
+ updateStatus("idle");
241
+ }
242
+ };
243
+ recognitionRef.current = recognition;
244
+ recognition.start();
245
+ setListening(true);
246
+ updateStatus("listening");
247
+ } catch (error) {
248
+ const errorName = error instanceof Error ? error.name : "";
249
+ setListening(false);
250
+ if (errorName === "NotAllowedError" || errorName === "PermissionDeniedError") {
251
+ updateStatus("permission-denied");
252
+ } else if (errorName === "NotSupportedError") {
253
+ setIsSupported(false);
254
+ updateStatus("unsupported");
255
+ } else {
256
+ updateStatus("error");
257
+ }
258
+ }
259
+ }, [emitTranscript, language, setListening, updateStatus]);
260
+
261
+ const handleClick = React.useCallback(() => {
262
+ onToggle?.();
263
+ if (currentListening || status === "requesting") {
264
+ stopRecognition();
265
+ } else {
266
+ void startRecognition();
267
+ }
268
+ }, [currentListening, onToggle, startRecognition, status, stopRecognition]);
269
+
270
+ const isUnavailable = disabled || !isSupported;
271
+ const tooltip = !isSupported
272
+ ? resolvedLabels.unsupported
273
+ : status === "permission-denied"
274
+ ? resolvedLabels.permissionDenied
275
+ : status === "error"
276
+ ? resolvedLabels.error
277
+ : status === "requesting"
278
+ ? resolvedLabels.requesting
279
+ : currentListening
280
+ ? resolvedLabels.stop
281
+ : resolvedLabels.start;
282
+ const label = currentListening || status === "requesting" ? resolvedLabels.stop : resolvedLabels.start;
283
+
284
+ return (
285
+ <Tooltip>
286
+ <TooltipTrigger asChild>
287
+ <span className="inline-flex shrink-0 rounded-md" tabIndex={isUnavailable ? 0 : -1}>
288
+ <Button
289
+ {...props}
290
+ type="button"
291
+ variant={props.variant ?? "ghost"}
292
+ size={props.size ?? "icon"}
293
+ className={cn(
294
+ "h-9 w-9 shrink-0 text-muted-foreground hover:text-foreground",
295
+ currentListening && "bg-destructive-subtle text-destructive-subtle-foreground hover:bg-destructive-subtle hover:text-destructive-subtle-foreground",
296
+ status === "requesting" && "bg-primary-subtle text-primary-subtle-foreground hover:bg-primary-subtle hover:text-primary-subtle-foreground",
297
+ className
298
+ )}
299
+ onClick={handleClick}
300
+ disabled={isUnavailable}
301
+ aria-label={label}
302
+ aria-pressed={currentListening}
303
+ >
304
+ {status === "requesting" ? (
305
+ <Loader className="h-5 w-5 animate-spin" />
306
+ ) : currentListening ? (
307
+ <Stop className="h-4 w-4" />
308
+ ) : (
309
+ <Microphone className="h-5 w-5" />
310
+ )}
311
+ </Button>
312
+ </span>
313
+ </TooltipTrigger>
314
+ <TooltipContent>{tooltip}</TooltipContent>
315
+ </Tooltip>
316
+ );
317
+ }
@@ -0,0 +1,56 @@
1
+ export interface CalendarHoliday {
2
+ date: Date
3
+ label: string
4
+ }
5
+
6
+ function holiday(year: number, month: number, day: number, label: string): CalendarHoliday {
7
+ return { date: new Date(year, month - 1, day), label }
8
+ }
9
+
10
+ export function calendarDateKey(date: Date): string {
11
+ const year = date.getFullYear()
12
+ const month = String(date.getMonth() + 1).padStart(2, "0")
13
+ const day = String(date.getDate()).padStart(2, "0")
14
+ return `${year}-${month}-${day}`
15
+ }
16
+
17
+ // Source: Cabinet Office, Government of Japan, "国民の祝日について".
18
+ // Keep this list isolated so applications can replace it with their own
19
+ // holiday file without changing Calendar rendering logic.
20
+ export const japaneseHolidays: CalendarHoliday[] = [
21
+ holiday(2026, 1, 1, "元日"),
22
+ holiday(2026, 1, 12, "成人の日"),
23
+ holiday(2026, 2, 11, "建国記念の日"),
24
+ holiday(2026, 2, 23, "天皇誕生日"),
25
+ holiday(2026, 3, 20, "春分の日"),
26
+ holiday(2026, 4, 29, "昭和の日"),
27
+ holiday(2026, 5, 3, "憲法記念日"),
28
+ holiday(2026, 5, 4, "みどりの日"),
29
+ holiday(2026, 5, 5, "こどもの日"),
30
+ holiday(2026, 5, 6, "休日"),
31
+ holiday(2026, 7, 20, "海の日"),
32
+ holiday(2026, 8, 11, "山の日"),
33
+ holiday(2026, 9, 21, "敬老の日"),
34
+ holiday(2026, 9, 22, "休日"),
35
+ holiday(2026, 9, 23, "秋分の日"),
36
+ holiday(2026, 10, 12, "スポーツの日"),
37
+ holiday(2026, 11, 3, "文化の日"),
38
+ holiday(2026, 11, 23, "勤労感謝の日"),
39
+ holiday(2027, 1, 1, "元日"),
40
+ holiday(2027, 1, 11, "成人の日"),
41
+ holiday(2027, 2, 11, "建国記念の日"),
42
+ holiday(2027, 2, 23, "天皇誕生日"),
43
+ holiday(2027, 3, 21, "春分の日"),
44
+ holiday(2027, 3, 22, "休日"),
45
+ holiday(2027, 4, 29, "昭和の日"),
46
+ holiday(2027, 5, 3, "憲法記念日"),
47
+ holiday(2027, 5, 4, "みどりの日"),
48
+ holiday(2027, 5, 5, "こどもの日"),
49
+ holiday(2027, 7, 19, "海の日"),
50
+ holiday(2027, 8, 11, "山の日"),
51
+ holiday(2027, 9, 20, "敬老の日"),
52
+ holiday(2027, 9, 23, "秋分の日"),
53
+ holiday(2027, 10, 11, "スポーツの日"),
54
+ holiday(2027, 11, 3, "文化の日"),
55
+ holiday(2027, 11, 23, "勤労感謝の日"),
56
+ ]
@@ -0,0 +1,32 @@
1
+ /* eslint-disable */
2
+ // Generated by `npm run design:sync:components`. Do not edit manually.
3
+
4
+ import type { ButtonVariantKey, ChatComposerVariantKey, ChatInputVariantKey, CheckboxVariantKey, CopyButtonVariantKey, FilterButtonVariantKey, InputVariantKey, RadioGroupVariantKey, SortButtonVariantKey, SwitchVariantKey, TextareaVariantKey, ToggleGroupVariantKey } from "./variant-keys";
5
+
6
+ export const buttonDefaultVariantKey: ButtonVariantKey = "default";
7
+ export const chatComposerDefaultVariantKey: ChatComposerVariantKey = "default";
8
+ export const chatInputDefaultVariantKey: ChatInputVariantKey = "default";
9
+ export const checkboxDefaultVariantKey: CheckboxVariantKey = "unchecked";
10
+ export const copyButtonDefaultVariantKey: CopyButtonVariantKey = "default";
11
+ export const filterButtonDefaultVariantKey: FilterButtonVariantKey = "default";
12
+ export const inputDefaultVariantKey: InputVariantKey = "default";
13
+ export const radioGroupDefaultVariantKey: RadioGroupVariantKey = "unchecked";
14
+ export const sortButtonDefaultVariantKey: SortButtonVariantKey = "none";
15
+ export const switchDefaultVariantKey: SwitchVariantKey = "unchecked";
16
+ export const textareaDefaultVariantKey: TextareaVariantKey = "default";
17
+ export const toggleGroupDefaultVariantKey: ToggleGroupVariantKey = "default";
18
+
19
+ export const inputsDefaultVariantKeys = {
20
+ button: buttonDefaultVariantKey,
21
+ chatComposer: chatComposerDefaultVariantKey,
22
+ chatInput: chatInputDefaultVariantKey,
23
+ checkbox: checkboxDefaultVariantKey,
24
+ copyButton: copyButtonDefaultVariantKey,
25
+ filterButton: filterButtonDefaultVariantKey,
26
+ input: inputDefaultVariantKey,
27
+ radioGroup: radioGroupDefaultVariantKey,
28
+ sortButton: sortButtonDefaultVariantKey,
29
+ switch: switchDefaultVariantKey,
30
+ textarea: textareaDefaultVariantKey,
31
+ toggleGroup: toggleGroupDefaultVariantKey,
32
+ } as const;
@@ -1,23 +1,23 @@
1
1
  /* eslint-disable */
2
2
  // Generated by `npm run design:sync:components`. Do not edit manually.
3
3
 
4
- export const alertVariantKeys = ["default", "destructive"] as const;
5
- export type AlertVariantKey = (typeof alertVariantKeys)[number];
6
-
7
- export const avatarVariantKeys = ["fallback", "image"] as const;
8
- export type AvatarVariantKey = (typeof avatarVariantKeys)[number];
4
+ export const buttonVariantKeys = ["default", "destructive", "ghost", "info", "link", "outline", "primary", "secondary", "success", "warning"] as const;
5
+ export type ButtonVariantKey = (typeof buttonVariantKeys)[number];
9
6
 
10
- export const badgeVariantKeys = ["default", "destructive", "outline", "secondary"] as const;
11
- export type BadgeVariantKey = (typeof badgeVariantKeys)[number];
7
+ export const chatComposerVariantKeys = ["context", "default", "prompt"] as const;
8
+ export type ChatComposerVariantKey = (typeof chatComposerVariantKeys)[number];
12
9
 
13
- export const buttonVariantKeys = ["default", "destructive", "ghost", "link", "outline", "secondary"] as const;
14
- export type ButtonVariantKey = (typeof buttonVariantKeys)[number];
10
+ export const chatInputVariantKeys = ["default", "processing"] as const;
11
+ export type ChatInputVariantKey = (typeof chatInputVariantKeys)[number];
15
12
 
16
13
  export const checkboxVariantKeys = ["checked", "disabled", "unchecked"] as const;
17
14
  export type CheckboxVariantKey = (typeof checkboxVariantKeys)[number];
18
15
 
19
- export const imgVariantKeys = ["error", "loaded", "loading"] as const;
20
- export type ImgVariantKey = (typeof imgVariantKeys)[number];
16
+ export const copyButtonVariantKeys = ["default", "label"] as const;
17
+ export type CopyButtonVariantKey = (typeof copyButtonVariantKeys)[number];
18
+
19
+ export const filterButtonVariantKeys = ["default", "popover", "selected"] as const;
20
+ export type FilterButtonVariantKey = (typeof filterButtonVariantKeys)[number];
21
21
 
22
22
  export const inputVariantKeys = ["default", "disabled", "placeholder"] as const;
23
23
  export type InputVariantKey = (typeof inputVariantKeys)[number];
@@ -25,11 +25,8 @@ export type InputVariantKey = (typeof inputVariantKeys)[number];
25
25
  export const radioGroupVariantKeys = ["checked", "unchecked"] as const;
26
26
  export type RadioGroupVariantKey = (typeof radioGroupVariantKeys)[number];
27
27
 
28
- export const separatorVariantKeys = ["horizontal", "vertical"] as const;
29
- export type SeparatorVariantKey = (typeof separatorVariantKeys)[number];
30
-
31
- export const spinnerVariantKeys = ["default", "lg", "sm"] as const;
32
- export type SpinnerVariantKey = (typeof spinnerVariantKeys)[number];
28
+ export const sortButtonVariantKeys = ["asc", "desc", "none"] as const;
29
+ export type SortButtonVariantKey = (typeof sortButtonVariantKeys)[number];
33
30
 
34
31
  export const switchVariantKeys = ["checked", "unchecked"] as const;
35
32
  export type SwitchVariantKey = (typeof switchVariantKeys)[number];
@@ -40,22 +37,17 @@ export type TextareaVariantKey = (typeof textareaVariantKeys)[number];
40
37
  export const toggleGroupVariantKeys = ["default", "outline"] as const;
41
38
  export type ToggleGroupVariantKey = (typeof toggleGroupVariantKeys)[number];
42
39
 
43
- export const toolPillVariantKeys = ["danger", "primary", "secondary"] as const;
44
- export type ToolPillVariantKey = (typeof toolPillVariantKeys)[number];
45
-
46
- export const atomVariantKeys = {
47
- alert: alertVariantKeys,
48
- avatar: avatarVariantKeys,
49
- badge: badgeVariantKeys,
40
+ export const inputsVariantKeys = {
50
41
  button: buttonVariantKeys,
42
+ chatComposer: chatComposerVariantKeys,
43
+ chatInput: chatInputVariantKeys,
51
44
  checkbox: checkboxVariantKeys,
52
- img: imgVariantKeys,
45
+ copyButton: copyButtonVariantKeys,
46
+ filterButton: filterButtonVariantKeys,
53
47
  input: inputVariantKeys,
54
48
  radioGroup: radioGroupVariantKeys,
55
- separator: separatorVariantKeys,
56
- spinner: spinnerVariantKeys,
49
+ sortButton: sortButtonVariantKeys,
57
50
  switch: switchVariantKeys,
58
51
  textarea: textareaVariantKeys,
59
52
  toggleGroup: toggleGroupVariantKeys,
60
- toolPill: toolPillVariantKeys,
61
53
  } as const;
@@ -0,0 +1,12 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio"
5
+
6
+ const AspectRatio = AspectRatioPrimitive.Root
7
+
8
+ export type AspectRatioProps = React.ComponentPropsWithoutRef<
9
+ typeof AspectRatioPrimitive.Root
10
+ >
11
+
12
+ export { AspectRatio }