Merge remote-tracking branch 'origin/sub_laptop_4' into sub_laptop_1

This commit is contained in:
2026-09-27 20:02:04 +09:00
7 changed files with 75 additions and 15 deletions
+16
View File
@@ -149,6 +149,7 @@ body > canvas[data-id="canvas"] {
flex-direction: column; flex-direction: column;
background: var(--cad-chrome-raised); background: var(--cad-chrome-raised);
border-bottom: 1px solid var(--cad-line); border-bottom: 1px solid var(--cad-line);
transition: height 120ms ease;
} }
.cad-ribbon-tabs { .cad-ribbon-tabs {
display: flex; display: flex;
@@ -157,6 +158,21 @@ body > canvas[data-id="canvas"] {
padding: 0 8px; padding: 0 8px;
border-bottom: 1px solid var(--cad-line); border-bottom: 1px solid var(--cad-line);
} }
.cad-ribbon-tabs__collapse {
margin-left: auto;
width: 24px;
height: 20px;
align-self: center;
padding: 0;
border: 1px solid transparent;
border-radius: 4px;
background: transparent;
color: var(--cad-text-dim);
font-size: 11px;
}
.cad-ribbon-tabs__collapse:hover {
background: var(--cad-hover);
}
.cad-ribbon-tabs button { .cad-ribbon-tabs button {
padding: 0 14px; padding: 0 14px;
border: 1px solid transparent; border: 1px solid transparent;
@@ -12,6 +12,7 @@ import { QuickAccessBar } from './QuickAccessBar';
import { Ribbon } from '../ribbon/Ribbon'; import { Ribbon } from '../ribbon/Ribbon';
import { StatusBar } from './StatusBar'; import { StatusBar } from './StatusBar';
import { useCadRefresh } from './use-cad-refresh'; import { useCadRefresh } from './use-cad-refresh';
import { isRibbonCollapsed } from './ui-state';
import { getActiveToolActor } from '../state'; import { getActiveToolActor } from '../state';
import type { Tool } from '../tools'; import type { Tool } from '../tools';
import { ViewControls } from './ViewControls'; import { ViewControls } from './ViewControls';
@@ -22,6 +23,7 @@ export const Toolbar: FC = () => {
const [commandLineVisible, setCommandLineVisible] = useState(false); const [commandLineVisible, setCommandLineVisible] = useState(false);
const activeTool = (getActiveToolActor()?.getSnapshot()?.context?.type ?? null) as Tool | null; const activeTool = (getActiveToolActor()?.getSnapshot()?.context?.type ?? null) as Tool | null;
const ribbonCollapsed = isRibbonCollapsed();
useEffect(() => { useEffect(() => {
// 편 상태의 폭은 App.css의 `--cad-panel-width` 기본값을 쓴다. 여기 숫자를 박아 // 편 상태의 폭은 App.css의 `--cad-panel-width` 기본값을 쓴다. 여기 숫자를 박아
@@ -32,8 +34,11 @@ export const Toolbar: FC = () => {
'--cad-command-height', '--cad-command-height',
commandLineVisible ? '86px' : '0px' commandLineVisible ? '86px' : '0px'
); );
// 리본 접힘 — 탭 줄(26px)만 남기고 패널 줄 자리를 걷는다(`Ribbon.tsx` 가 안 그림).
if (ribbonCollapsed) document.documentElement.style.setProperty('--cad-ribbon-height', '26px');
else document.documentElement.style.removeProperty('--cad-ribbon-height');
window.dispatchEvent(new Event('resize')); window.dispatchEvent(new Event('resize'));
}, [panelCollapsed, commandLineVisible]); }, [panelCollapsed, commandLineVisible, ribbonCollapsed]);
return ( return (
<> <>
@@ -12,6 +12,8 @@ let inspectorCollapsed = false;
let quickPropertiesVisible = false; let quickPropertiesVisible = false;
let activeRibbonTab = 'home'; let activeRibbonTab = 'home';
let blockLibraryVisible = false; let blockLibraryVisible = false;
/** 호스트(M02)가 처음부터 접어 실을 때 true — 탭을 누르면 펼침(`setActiveRibbonTab`). */
let ribbonCollapsed = false;
const notify = () => notifyWindow(HtmlEvent.UPDATE_STATE); const notify = () => notifyWindow(HtmlEvent.UPDATE_STATE);
@@ -20,12 +22,18 @@ export const isInspectorCollapsed = () => inspectorCollapsed;
export const isQuickPropertiesVisible = () => quickPropertiesVisible; export const isQuickPropertiesVisible = () => quickPropertiesVisible;
export const getActiveRibbonTab = () => activeRibbonTab; export const getActiveRibbonTab = () => activeRibbonTab;
export const isBlockLibraryVisible = () => blockLibraryVisible; export const isBlockLibraryVisible = () => blockLibraryVisible;
export const isRibbonCollapsed = () => ribbonCollapsed;
export function setActiveRibbonTab(tabId: string): void { export function setActiveRibbonTab(tabId: string): void {
activeRibbonTab = tabId; activeRibbonTab = tabId;
notify(); notify();
} }
export function setRibbonCollapsed(collapsed: boolean): void {
ribbonCollapsed = collapsed;
notify();
}
export function openInspector(tab: InspectorTab): void { export function openInspector(tab: InspectorTab): void {
inspectorTab = tab; inspectorTab = tab;
inspectorCollapsed = false; inspectorCollapsed = false;
@@ -25,6 +25,7 @@ import {
setHostTitle, setHostTitle,
setLayers, setLayers,
} from '../state.ts'; } from '../state.ts';
import { setRibbonCollapsed } from '../components/ui-state.ts';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import { runCommandInput } from '../commands/run-command.ts'; import { runCommandInput } from '../commands/run-command.ts';
import { setRecoveryScope } from '../helpers/autosave.ts'; import { setRecoveryScope } from '../helpers/autosave.ts';
@@ -57,6 +58,8 @@ interface DrawingLoadMessage {
hostTitle?: string; hostTitle?: string;
/** 저장(💾 · Ctrl+S)을 부모 [저장]에 맡기는가. */ /** 저장(💾 · Ctrl+S)을 부모 [저장]에 맡기는가. */
hostSave?: boolean; hostSave?: boolean;
/** 리본을 접힌 채로 여는가 — 없으면(B07) 펼침 그대로. */
ribbonCollapsed?: boolean;
} }
interface DrawingSaveRequestMessage { interface DrawingSaveRequestMessage {
@@ -207,6 +210,7 @@ export function registerAisloDrawingBridge() {
setHostReadOnly(event.data.readOnly === true); setHostReadOnly(event.data.readOnly === true);
setHostTitle(event.data.hostTitle ?? 'B07 상세 설계'); setHostTitle(event.data.hostTitle ?? 'B07 상세 설계');
setHostSave(event.data.hostSave === true); setHostSave(event.data.hostSave === true);
setRibbonCollapsed(event.data.ribbonCollapsed === true);
if (event.data.frameEdit) applyFramePreview(); if (event.data.frameEdit) applyFramePreview();
// 앞 도면에서 켜 둔 그리기 도구를 내린다. 안 내리면 **확정한 도면 위에도** // 앞 도면에서 켜 둔 그리기 도구를 내린다. 안 내리면 **확정한 도면 위에도**
// 그 도구가 계속 그린다 — 읽기 전용은 새 명령만 막기 때문이다(2026-09-01 실측: // 그 도구가 계속 그린다 — 읽기 전용은 새 명령만 막기 때문이다(2026-09-01 실측:
@@ -3,7 +3,12 @@ import { type FC, type ReactNode, useEffect, useRef, useState } from 'react';
import type { CadCommand } from '../commands/command.types'; import type { CadCommand } from '../commands/command.types';
import { getCommandById } from '../commands/registry'; import { getCommandById } from '../commands/registry';
import { runCommand } from '../commands/run-command'; import { runCommand } from '../commands/run-command';
import { getActiveRibbonTab, setActiveRibbonTab } from '../components/ui-state'; import {
getActiveRibbonTab,
isRibbonCollapsed,
setActiveRibbonTab,
setRibbonCollapsed,
} from '../components/ui-state';
import type { Tool } from '../tools'; import type { Tool } from '../tools';
import { RIBBON_TABS, type RibbonPanel, type RibbonWidget } from './ribbon.config'; import { RIBBON_TABS, type RibbonPanel, type RibbonWidget } from './ribbon.config';
@@ -147,6 +152,7 @@ const Panel: FC<{
export const Ribbon: FC<RibbonProps> = ({ activeTool, widgets, onCommandRun }) => { export const Ribbon: FC<RibbonProps> = ({ activeTool, widgets, onCommandRun }) => {
const activeTabId = getActiveRibbonTab(); const activeTabId = getActiveRibbonTab();
const activeTab = RIBBON_TABS.find((tab) => tab.id === activeTabId) ?? RIBBON_TABS[0]; const activeTab = RIBBON_TABS.find((tab) => tab.id === activeTabId) ?? RIBBON_TABS[0];
const collapsed = isRibbonCollapsed();
const handleRun = (command: CadCommand) => { const handleRun = (command: CadCommand) => {
runCommand(command); runCommand(command);
@@ -161,23 +167,36 @@ export const Ribbon: FC<RibbonProps> = ({ activeTool, widgets, onCommandRun }) =
type="button" type="button"
key={tab.id} key={tab.id}
data-active={tab.id === activeTab.id} data-active={tab.id === activeTab.id}
onClick={() => setActiveRibbonTab(tab.id)} onClick={() => {
setActiveRibbonTab(tab.id);
if (collapsed) setRibbonCollapsed(false);
}}
> >
{tab.label} {tab.label}
</button> </button>
))} ))}
<button
type="button"
className="cad-ribbon-tabs__collapse"
onClick={() => setRibbonCollapsed(!collapsed)}
title={collapsed ? '리본 펼치기' : '리본 접기'}
>
{collapsed ? '⌄' : '⌃'}
</button>
</div> </div>
<div className="cad-ribbon-panels"> {!collapsed && (
{activeTab.panels.map((panel) => ( <div className="cad-ribbon-panels">
<Panel {activeTab.panels.map((panel) => (
key={panel.label} <Panel
panel={panel} key={panel.label}
activeTool={activeTool} panel={panel}
widgets={widgets} activeTool={activeTool}
onRun={handleRun} widgets={widgets}
/> onRun={handleRun}
))} />
</div> ))}
</div>
)}
</nav> </nav>
); );
}; };
@@ -189,7 +189,13 @@ export function mountDrawingTemplate(
null, null,
!readOnly, !readOnly,
{}, {},
{ recoveryScope, readOnly, hostTitle: "도면 양식 편집", hostSave: true }, {
recoveryScope,
readOnly,
hostTitle: "도면 양식 편집",
hostSave: true,
ribbonCollapsed: true,
},
); );
}; };
+2
View File
@@ -63,6 +63,8 @@ export interface CadLoadExtra {
hostTitle?: string; hostTitle?: string;
/** 저장(💾 · Ctrl+S)을 부모 [저장]에 맡긴다 — 켜면 `onHostSave` 로 옴. */ /** 저장(💾 · Ctrl+S)을 부모 [저장]에 맡긴다 — 켜면 `onHostSave` 로 옴. */
hostSave?: boolean; hostSave?: boolean;
/** 리본을 접힌 채로 연다 — 안 보내면 펼침 그대로. */
ribbonCollapsed?: boolean;
} }
export interface CadHost<D extends CadHostDrawing, M, Q> { export interface CadHost<D extends CadHostDrawing, M, Q> {