style(space): 상단 토스트 크기·패딩·타이포 조정

This commit is contained in:
2026-03-06 01:41:12 +09:00
parent 5f7ca99f44
commit 2ac568a4ab
3 changed files with 183 additions and 20 deletions

View File

@@ -26,14 +26,14 @@ export const FocusTopToast = ({
role="status"
aria-live="polite"
aria-atomic="true"
className="pointer-events-auto inline-flex max-w-[min(420px,92vw)] items-center gap-2 rounded-full border border-white/14 bg-black/32 px-3 py-1.5 text-xs text-white/86 shadow-[0_8px_24px_rgba(2,6,23,0.28)] backdrop-blur-md"
className="pointer-events-auto inline-flex min-h-10 max-w-[min(480px,94vw)] items-center gap-2.5 rounded-full border border-white/14 bg-black/36 px-4 py-2 text-sm text-white/88 shadow-[0_10px_28px_rgba(2,6,23,0.3)] backdrop-blur-md"
>
<span className="truncate">{message}</span>
{actionLabel ? (
<button
type="button"
onClick={onAction}
className="shrink-0 text-xs font-medium text-white/92 underline underline-offset-2 transition-colors hover:text-white"
className="shrink-0 text-sm font-semibold text-white/92 underline underline-offset-2 transition-colors hover:text-white"
>
{actionLabel}
</button>

View File

@@ -143,14 +143,11 @@ const resolveInitialTimerLabel = (
export const SpaceWorkspaceWidget = () => {
const searchParams = useSearchParams();
const storedSelection = useMemo(() => readStoredWorkspaceSelection(), []);
const roomQuery = searchParams.get('room');
const goalQuery = searchParams.get('goal');
const goalQuery = searchParams.get('goal')?.trim() ?? '';
const soundQuery = searchParams.get('sound');
const timerQuery = searchParams.get('timer');
const storedGoal = storedSelection.goal?.trim() ?? '';
const hasQueryOverrides = Boolean(roomQuery || goalQuery || soundQuery || timerQuery);
const canOfferResume = storedGoal.length > 0 && !hasQueryOverrides;
const {
thoughts,
thoughtCount,
@@ -162,17 +159,17 @@ export const SpaceWorkspaceWidget = () => {
setThoughtCompleted,
} = useThoughtInbox();
const initialRoomId = resolveInitialRoomId(roomQuery, storedSelection.sceneId);
const initialRoomId = resolveInitialRoomId(roomQuery, undefined);
const initialRoom = getRoomById(initialRoomId) ?? ROOM_THEMES[0];
const initialGoal = goalQuery?.trim() ?? '';
const initialGoal = goalQuery;
const initialSoundPresetId = resolveInitialSoundPreset(
soundQuery,
storedSelection.soundPresetId,
undefined,
initialRoom.recommendedSoundPresetId,
);
const initialTimerLabel = resolveInitialTimerLabel(
timerQuery,
storedSelection.timerPresetId,
undefined,
initialRoom.recommendedTimerPresetId,
);
@@ -181,10 +178,12 @@ export const SpaceWorkspaceWidget = () => {
const [selectedTimerLabel, setSelectedTimerLabel] = useState(initialTimerLabel);
const [goalInput, setGoalInput] = useState(initialGoal);
const [selectedGoalId, setSelectedGoalId] = useState<string | null>(null);
const [showResumePrompt, setShowResumePrompt] = useState(canOfferResume);
const [resumeGoal, setResumeGoal] = useState('');
const [showResumePrompt, setShowResumePrompt] = useState(false);
const [hasHydratedSelection, setHasHydratedSelection] = useState(false);
const [selectionOverride, setSelectionOverride] = useState<SelectionOverride>({
sound: Boolean(storedSelection.override?.sound),
timer: Boolean(storedSelection.override?.timer),
sound: false,
timer: false,
});
const {
@@ -234,6 +233,41 @@ export const SpaceWorkspaceWidget = () => {
}
}, [selectionOverride.sound, selectionOverride.timer, setSelectedPresetId]);
useEffect(() => {
const storedSelection = readStoredWorkspaceSelection();
const restoredSelectionOverride: SelectionOverride = {
sound: Boolean(storedSelection.override?.sound),
timer: Boolean(storedSelection.override?.timer),
};
setSelectionOverride(restoredSelectionOverride);
if (!roomQuery && storedSelection.sceneId && getRoomById(storedSelection.sceneId)) {
setSelectedRoomId(storedSelection.sceneId);
}
if (!timerQuery) {
const restoredTimerLabel = resolveTimerLabelFromPresetId(storedSelection.timerPresetId);
if (restoredTimerLabel) {
setSelectedTimerLabel(restoredTimerLabel);
}
}
if (!soundQuery && storedSelection.soundPresetId && SOUND_PRESETS.some((preset) => preset.id === storedSelection.soundPresetId)) {
setSelectedPresetId(storedSelection.soundPresetId);
}
const restoredGoal = storedSelection.goal?.trim() ?? '';
if (!goalQuery && restoredGoal.length > 0 && !hasQueryOverrides) {
setResumeGoal(restoredGoal);
setShowResumePrompt(true);
}
setHasHydratedSelection(true);
}, [goalQuery, hasQueryOverrides, roomQuery, setSelectedPresetId, soundQuery, timerQuery]);
useEffect(() => {
applyRecommendedSelections(selectedRoomId);
}, [applyRecommendedSelections, selectedRoomId]);
@@ -323,11 +357,15 @@ export const SpaceWorkspaceWidget = () => {
return;
}
if (!hasHydratedSelection) {
return;
}
const timerPresetId = resolveTimerPresetIdFromLabel(selectedTimerLabel);
const normalizedGoal = goalInput.trim().length > 0
? goalInput.trim()
: showResumePrompt
? storedGoal
? resumeGoal
: '';
window.localStorage.setItem(
@@ -340,7 +378,7 @@ export const SpaceWorkspaceWidget = () => {
override: selectionOverride,
}),
);
}, [goalInput, selectedRoomId, selectedTimerLabel, selectedPresetId, selectionOverride, showResumePrompt, storedGoal]);
}, [goalInput, hasHydratedSelection, resumeGoal, selectedRoomId, selectedTimerLabel, selectedPresetId, selectionOverride, showResumePrompt]);
return (
<div className="relative h-dvh overflow-hidden text-white">
@@ -373,11 +411,11 @@ export const SpaceWorkspaceWidget = () => {
onGoalChipSelect={handleGoalChipSelect}
onStart={handleStart}
resumeHint={
showResumePrompt
showResumePrompt && resumeGoal
? {
goal: storedGoal,
goal: resumeGoal,
onResume: () => {
setGoalInput(storedGoal);
setGoalInput(resumeGoal);
setSelectedGoalId(null);
setShowResumePrompt(false);
setWorkspaceMode('focus');