@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.
- package/LICENSE +21 -0
- package/README.ja.md +90 -0
- package/README.md +52 -91
- package/package.json +47 -6
- package/src/components/display/Accordion.tsx +185 -0
- package/src/components/display/AccordionGroup.tsx +155 -0
- package/src/components/display/ActionDataTable.tsx +413 -0
- package/src/components/display/ActivityTimelineCard.tsx +483 -0
- package/src/components/display/AnalyticsCard.tsx +167 -0
- package/src/components/display/AssetCard.tsx +242 -0
- package/src/components/display/AssetGrid.tsx +164 -0
- package/src/components/display/Avatar.tsx +127 -0
- package/src/components/display/AvatarGroup.tsx +131 -0
- package/src/components/{atoms → display}/Badge.tsx +3 -3
- package/src/components/display/BarChart.tsx +247 -0
- package/src/components/{molecules → display}/Card.tsx +1 -1
- package/src/components/display/Carousel.tsx +593 -0
- package/src/components/display/ChartLegend.tsx +124 -0
- package/src/components/display/ChatMessage.tsx +382 -0
- package/src/components/display/ChoroplethMap.tsx +613 -0
- package/src/components/display/Code.tsx +42 -0
- package/src/components/display/CodeBlock.tsx +338 -0
- package/src/components/display/ColorSwatch.tsx +71 -0
- package/src/components/display/ConcentricProgressCard.tsx +545 -0
- package/src/components/display/DataTable.tsx +522 -0
- package/src/components/display/DistributionBar.tsx +102 -0
- package/src/components/display/DocNote.tsx +36 -0
- package/src/components/display/DonutChart.tsx +257 -0
- package/src/components/display/EmptyState.tsx +44 -0
- package/src/components/display/FileTree.tsx +180 -0
- package/src/components/display/GaugeChart.tsx +219 -0
- package/src/components/display/HeatmapChart.tsx +266 -0
- package/src/components/display/Icon.tsx +66 -0
- package/src/components/display/ImagePreview.tsx +140 -0
- package/src/components/{atoms → display}/Img.tsx +46 -12
- package/src/components/display/LabeledDonutCard.tsx +475 -0
- package/src/components/display/LineChart.tsx +464 -0
- package/src/components/{molecules → display}/List.tsx +20 -13
- package/src/components/display/MarkdownRenderer.tsx +157 -0
- package/src/components/display/MetadataList.tsx +81 -0
- package/src/components/display/MiniDistributionBarCard.tsx +314 -0
- package/src/components/display/PieChart.tsx +234 -0
- package/src/components/display/QuadrantMatrix.tsx +330 -0
- package/src/components/display/RadarChart.tsx +335 -0
- package/src/components/display/RadialBarChart.tsx +264 -0
- package/src/components/display/RetentionCohortCard.tsx +350 -0
- package/src/components/display/RibbonChart.tsx +618 -0
- package/src/components/display/SearchableAccordion.tsx +270 -0
- package/src/components/display/SegmentTimelineCard.tsx +452 -0
- package/src/components/display/SegmentedGaugeCard.tsx +607 -0
- package/src/components/display/Spacer.tsx +51 -0
- package/src/components/display/SparklineChart.tsx +394 -0
- package/src/components/display/StackedBarChart.tsx +393 -0
- package/src/components/display/Statistic.tsx +70 -0
- package/src/components/{molecules → display}/Table.tsx +22 -7
- package/src/components/display/Tag.tsx +80 -0
- package/src/components/display/TagEditor.tsx +141 -0
- package/src/components/display/Timeline.tsx +121 -0
- package/src/components/{atoms → display}/ToolPill.tsx +42 -18
- package/src/components/display/TreeView.tsx +226 -0
- package/src/components/display/chart-tooltip.tsx +423 -0
- package/src/components/display/chart-utils.ts +71 -0
- package/src/components/display/circular-chart-utils.ts +147 -0
- package/src/components/display/generated/default-variant-keys.ts +90 -0
- package/src/components/display/generated/variant-keys.ts +169 -0
- package/src/components/{atoms → feedback}/Alert.tsx +12 -5
- package/src/components/feedback/Banner.tsx +90 -0
- package/src/components/{molecules → feedback}/NotificationCenter.tsx +64 -31
- package/src/components/feedback/ProgressWidget.tsx +44 -0
- package/src/components/{atoms → feedback}/Spinner.tsx +2 -2
- package/src/components/{molecules → feedback}/StatusBar.tsx +4 -4
- package/src/components/feedback/StatusScreen.tsx +148 -0
- package/src/components/{molecules → feedback}/Stepper.tsx +10 -5
- package/src/components/feedback/Toast.tsx +108 -0
- package/src/components/feedback/ToastProvider.tsx +78 -0
- package/src/components/feedback/generated/default-variant-keys.ts +16 -0
- package/src/components/feedback/generated/variant-keys.ts +21 -0
- package/src/components/generated/component-manifest.ts +1568 -454
- package/src/components/generated/component-style-hints.ts +1958 -718
- package/src/components/{atoms → inputs}/ButtonVariants.ts +13 -3
- package/src/components/inputs/Calendar.tsx +212 -0
- package/src/components/inputs/ChatComposer.tsx +75 -0
- package/src/components/inputs/ChatInput.tsx +528 -0
- package/src/components/{atoms → inputs}/Checkbox.tsx +2 -2
- package/src/components/inputs/Combobox.tsx +175 -0
- package/src/components/inputs/CopyButton.tsx +187 -0
- package/src/components/inputs/DatePicker.tsx +519 -0
- package/src/components/inputs/DateRangePicker.tsx +878 -0
- package/src/components/inputs/EditableField.tsx +182 -0
- package/src/components/{organisms → inputs}/FileUploader.tsx +24 -9
- package/src/components/inputs/FilterButton.tsx +163 -0
- package/src/components/{molecules → inputs}/Form.tsx +20 -3
- package/src/components/{atoms → inputs}/Input.tsx +2 -0
- package/src/components/inputs/InputOTP.tsx +75 -0
- package/src/components/inputs/Mention.tsx +279 -0
- package/src/components/inputs/NumberInput.tsx +109 -0
- package/src/components/inputs/PasswordGroup.tsx +138 -0
- package/src/components/inputs/PasswordInput.tsx +74 -0
- package/src/components/inputs/PasswordRequirementList.tsx +96 -0
- package/src/components/inputs/PasswordStrengthMeter.tsx +93 -0
- package/src/components/inputs/PhoneInput.tsx +99 -0
- package/src/components/inputs/PostalCodeInput.tsx +98 -0
- package/src/components/inputs/RangeSlider.tsx +129 -0
- package/src/components/inputs/SearchInput.tsx +76 -0
- package/src/components/inputs/Select.tsx +39 -0
- package/src/components/{atoms → inputs}/Slider.tsx +18 -5
- package/src/components/{molecules → inputs}/SortButton.tsx +5 -2
- package/src/components/{atoms → inputs}/Switch.tsx +15 -4
- package/src/components/inputs/TagInput.tsx +114 -0
- package/src/components/{atoms → inputs}/Textarea.tsx +1 -0
- package/src/components/inputs/TimePicker.tsx +150 -0
- package/src/components/inputs/Toggle.tsx +48 -0
- package/src/components/{atoms → inputs}/ToggleGroup.tsx +2 -2
- package/src/components/inputs/TooltipButton.tsx +148 -0
- package/src/components/inputs/VoiceInputButton.tsx +317 -0
- package/src/components/inputs/calendar-holidays.ts +56 -0
- package/src/components/inputs/generated/default-variant-keys.ts +32 -0
- package/src/components/{atoms → inputs}/generated/variant-keys.ts +19 -27
- package/src/components/layout/AspectRatio.tsx +12 -0
- package/src/components/layout/AssetInspectorPanel.tsx +416 -0
- package/src/components/layout/Cluster.tsx +56 -0
- package/src/components/layout/CollapsiblePanelToggle.tsx +94 -0
- package/src/components/layout/Container.tsx +43 -0
- package/src/components/layout/DeviceFrame.tsx +227 -0
- package/src/components/layout/Grid.tsx +65 -0
- package/src/components/layout/HStack.tsx +73 -0
- package/src/components/{organisms → layout}/InspectorPanel.tsx +6 -5
- package/src/components/layout/MarqueeFrame.tsx +158 -0
- package/src/components/layout/Resizable.tsx +94 -0
- package/src/components/layout/ScrollArea.tsx +71 -0
- package/src/components/{organisms → layout}/SpatialCanvas.tsx +12 -7
- package/src/components/layout/VStack.tsx +69 -0
- package/src/components/layout/generated/default-variant-keys.ts +16 -0
- package/src/components/layout/generated/variant-keys.ts +21 -0
- package/src/components/{molecules → navigation}/Breadcrumb.tsx +5 -4
- package/src/components/navigation/Command.tsx +266 -0
- package/src/components/navigation/CommandPalette.tsx +83 -0
- package/src/components/navigation/DocumentPager.tsx +171 -0
- package/src/components/navigation/Footer.tsx +88 -0
- package/src/components/navigation/Header.tsx +80 -0
- package/src/components/{molecules → navigation}/Menubar.tsx +45 -12
- package/src/components/navigation/NavigationMenu.tsx +128 -0
- package/src/components/navigation/PageAside.tsx +84 -0
- package/src/components/{molecules → navigation}/Pagination.tsx +60 -7
- package/src/components/{organisms → navigation}/RightRail.tsx +1 -1
- package/src/components/navigation/Sidebar.tsx +223 -0
- package/src/components/navigation/SidebarItem.tsx +160 -0
- package/src/components/{molecules → navigation}/Tabs.tsx +2 -2
- package/src/components/navigation/TextLink.tsx +71 -0
- package/src/components/navigation/generated/default-variant-keys.ts +12 -0
- package/src/components/navigation/generated/variant-keys.ts +13 -0
- package/src/components/overlay/AIChatInput.tsx +5 -0
- package/src/components/overlay/AIChatMessage.tsx +6 -0
- package/src/components/overlay/AlertDialog.tsx +145 -0
- package/src/components/overlay/ChatPanel.tsx +180 -0
- package/src/components/{molecules → overlay}/ContextMenu.tsx +65 -29
- package/src/components/{molecules → overlay}/Dialog.tsx +21 -13
- package/src/components/overlay/Drawer.tsx +131 -0
- package/src/components/{molecules → overlay}/DropdownMenu.tsx +52 -17
- package/src/components/overlay/FloatingPanel.tsx +90 -0
- package/src/components/overlay/HoverCard.tsx +36 -0
- package/src/components/overlay/MediaLightbox.tsx +403 -0
- package/src/components/overlay/MediaPickerDialog.tsx +198 -0
- package/src/components/overlay/Modal.tsx +103 -0
- package/src/components/overlay/OnboardingFlow.tsx +172 -0
- package/src/components/overlay/Popover.tsx +36 -0
- package/src/components/overlay/ShareModal.tsx +324 -0
- package/src/components/{molecules → overlay}/Sheet.tsx +76 -19
- package/src/components/overlay/Tooltip.tsx +130 -0
- package/src/components/overlay/generated/default-variant-keys.ts +14 -0
- package/src/components/overlay/generated/variant-keys.ts +17 -0
- package/src/components/patterns/BlogTemplate.tsx +46 -0
- package/src/components/{templates → patterns}/DashboardTemplate.tsx +2 -2
- package/src/components/patterns/DocsTemplate.tsx +41 -0
- package/src/components/{templates → patterns}/MediaLibraryTemplate.tsx +1 -1
- package/src/components/patterns/OnboardingTemplate.tsx +32 -0
- package/src/components/patterns/PricingTemplate.tsx +106 -0
- package/src/globals.css +173 -22
- package/src/index.ts +177 -76
- package/tailwind-theme-extend.cjs +48 -3
- package/design/atoms-metadata.json +0 -82
- package/design/molecules-metadata.json +0 -130
- package/design/organisms-metadata.json +0 -38
- package/design/templates-metadata.json +0 -38
- package/src/components/atoms/Avatar.tsx +0 -57
- package/src/components/atoms/Select.tsx +0 -28
- package/src/components/atoms/generated/default-variant-keys.ts +0 -36
- package/src/components/molecules/AIChatInput.tsx +0 -140
- package/src/components/molecules/AIChatMessage.tsx +0 -109
- package/src/components/molecules/Accordion.tsx +0 -99
- package/src/components/molecules/Calendar.tsx +0 -60
- package/src/components/molecules/Carousel.tsx +0 -261
- package/src/components/molecules/Command.tsx +0 -152
- package/src/components/molecules/FilterButton.tsx +0 -133
- package/src/components/molecules/HoverCard.tsx +0 -29
- package/src/components/molecules/Modal.tsx +0 -66
- package/src/components/molecules/Popover.tsx +0 -31
- package/src/components/molecules/ProgressWidget.tsx +0 -40
- package/src/components/molecules/Resizable.tsx +0 -47
- package/src/components/molecules/ScrollArea.tsx +0 -48
- package/src/components/molecules/SidebarItem.tsx +0 -134
- package/src/components/molecules/Toast.tsx +0 -57
- package/src/components/molecules/Tooltip.tsx +0 -30
- package/src/components/molecules/generated/default-variant-keys.ts +0 -22
- package/src/components/molecules/generated/variant-keys.ts +0 -33
- package/src/components/organisms/CommandPalette.tsx +0 -58
- package/src/components/organisms/FloatingPanel.tsx +0 -46
- package/src/components/organisms/ShareModal.tsx +0 -182
- package/src/components/organisms/ToastProvider.tsx +0 -49
- /package/src/components/{atoms → display}/Kbd.tsx +0 -0
- /package/src/components/{atoms → display}/Separator.tsx +0 -0
- /package/src/components/{atoms → display}/Skeleton.tsx +0 -0
- /package/src/components/{atoms → feedback}/Progress.tsx +0 -0
- /package/src/components/{atoms → inputs}/Button.tsx +0 -0
- /package/src/components/{atoms → inputs}/Label.tsx +0 -0
- /package/src/components/{atoms → inputs}/RadioGroup.tsx +0 -0
- /package/src/components/{organisms → navigation}/AppRail.tsx +0 -0
- /package/src/components/{templates → patterns}/AuthTemplate.tsx +0 -0
- /package/src/components/{templates → patterns}/BannalyzeTemplate.tsx +0 -0
- /package/src/components/{templates → patterns}/ChatTemplate.tsx +0 -0
- /package/src/components/{templates → patterns}/EditorTemplate.tsx +0 -0
- /package/src/components/{templates → patterns}/KanbanTemplate.tsx +0 -0
- /package/src/components/{templates → patterns}/LandingTemplate.tsx +0 -0
- /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
|
|
5
|
-
export type
|
|
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
|
|
11
|
-
export type
|
|
7
|
+
export const chatComposerVariantKeys = ["context", "default", "prompt"] as const;
|
|
8
|
+
export type ChatComposerVariantKey = (typeof chatComposerVariantKeys)[number];
|
|
12
9
|
|
|
13
|
-
export const
|
|
14
|
-
export type
|
|
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
|
|
20
|
-
export type
|
|
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
|
|
29
|
-
export type
|
|
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
|
|
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
|
-
|
|
45
|
+
copyButton: copyButtonVariantKeys,
|
|
46
|
+
filterButton: filterButtonVariantKeys,
|
|
53
47
|
input: inputVariantKeys,
|
|
54
48
|
radioGroup: radioGroupVariantKeys,
|
|
55
|
-
|
|
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 }
|