DOSSIER Multi-Phase: C#-Plugin + Yak + Wandstile + UX-Polish
- C#-Plugin "DOSSIER" mit 23 nativen Commands (dWall, dDoor, ..., dSection)
- Native Command-Namen + Autocomplete + saubere History
- Idle-Defer + RhinoCode-API → kein _-RunPythonScript-Echo
- Yak-Paket via build.sh, Install in ~/Library/.../packages/8.0/
- Launcher (Tauri):
- dossier_init Tauri-Command + Setup-Tab in Settings
- Yak-Install + StartupCommands-XML + Window-Layout in einem Schritt
- clean-rhino.sh fuer reproduzierbare Resets
- check_dossier_initialized triggert Auto-Open-Setup beim ersten Start
- Wand-Architektur:
- Chain-Logik DEAKTIVIERT → jede Wand baut eigenes Volume (individuell
anwaehlbar, einzeln loeschbar)
- Polyline-Wand: jedes Segment = eigene Wand
- Smart-Split fuer wand_axis/decke/dach/raum/aussparung/traeger
- Auto-Group axis+volume → kein ChooseOne-Dialog, Delete loescht beides
- Stale-Mitre-Fix: Joint-Cache wird vor jedem Wand-Regen invalidiert
- T-Junction-Tolerance auf 1mm (war 1cm, lieferte falsche T-Mitres)
- Wand-Stile:
- Schema in dossier_project_settings.wand_styles (Material + Prio +
Default-Dicke + Referenz, oder Layered mit Schichten)
- dWall-Command Stil-Picker
- ProjectSettingsDialog: Sidebar-Layout (Pill-Selection) +
Wandstile-Tab mit Liste/Editor
- _wand_chain_compat benutzt style_id
- Prio-Dominanz: hoehere Prio gewinnt Eckverbindung, niedrigere wird
T-mitered (siehe _resolve_corner_miter)
- Cmd+G fuer Group (Geschoss-Up auf Alias 'gu')
- Welcome + Cheatsheet borderless mit X/Back-Buttons
- BeginCommand-Hook fuer Gestaltung-Panel-Auto-Open
- panel_base: Python.NET-Enum-Fix fuer Material-Render
This commit is contained in:
@@ -126,6 +126,13 @@ export default function App() {
|
||||
invoke('list_window_layouts').then(setLayouts).catch(() => {})
|
||||
invoke('read_dossier_settings').then(ds => setActiveLayout(ds?.windowLayout || '')).catch(() => {})
|
||||
invoke('read_settings').then(s => setTags(s?.tags || [])).catch(() => {})
|
||||
// Auto-Open Setup-Dialog wenn DOSSIER nicht initialisiert ist (z.B. nach
|
||||
// clean-rhino.sh oder auf einem neuen Mac).
|
||||
invoke('check_dossier_initialized')
|
||||
.then(st => {
|
||||
if (!st?.initialized) { setSettingsTab('setup'); setSettingsOpen(true) }
|
||||
})
|
||||
.catch(() => {})
|
||||
}, [])
|
||||
|
||||
// File-Meta laden sobald recent sich aendert
|
||||
@@ -750,6 +757,7 @@ function SettingsDialog({ initialTab = 'rhino', onClose }) {
|
||||
<header style={{ display: 'flex', alignItems: 'center', gap: 16 }}>
|
||||
<span>Einstellungen</span>
|
||||
<div style={{ display: 'flex', gap: 4, marginLeft: 'auto', flexWrap: 'wrap' }}>
|
||||
<TabBtn active={tab === 'setup'} onClick={() => setTab('setup')}>Setup</TabBtn>
|
||||
<TabBtn active={tab === 'rhino'} onClick={() => setTab('rhino')}>Rhino</TabBtn>
|
||||
<TabBtn active={tab === 'view'} onClick={() => setTab('view')}>View</TabBtn>
|
||||
<TabBtn active={tab === 'ebenen'} onClick={() => setTab('ebenen')}>Ebenen</TabBtn>
|
||||
@@ -759,6 +767,7 @@ function SettingsDialog({ initialTab = 'rhino', onClose }) {
|
||||
</div>
|
||||
</header>
|
||||
<div className="body">
|
||||
{tab === 'setup' && <SetupSettings />}
|
||||
{tab === 'rhino' && <RhinoSettings />}
|
||||
{tab === 'view' && <ViewSettings />}
|
||||
{tab === 'ebenen' && <EbenenSchemaSettings />}
|
||||
@@ -774,6 +783,148 @@ function SettingsDialog({ initialTab = 'rhino', onClose }) {
|
||||
)
|
||||
}
|
||||
|
||||
function SetupSettings() {
|
||||
const [running, setRunning] = useState(false)
|
||||
const [result, setResult] = useState(null) // { steps, overall_ok }
|
||||
const [error, setError] = useState(null)
|
||||
const [rhinoBusy, setRhinoBusy] = useState(false)
|
||||
const [status, setStatus] = useState(null) // { plugin_installed, startup_cmd_set, layout_installed, initialized }
|
||||
const [rhinoApp, setRhinoApp] = useState('')
|
||||
const [startupPath, setStartupPath] = useState('')
|
||||
|
||||
// Live-Check: ob Rhino laeuft (Init kann nicht laufen wenn ja)
|
||||
useEffect(() => {
|
||||
let cancelled = false
|
||||
const tick = () => {
|
||||
invoke('is_rhino_running')
|
||||
.then(v => { if (!cancelled) setRhinoBusy(!!v) })
|
||||
.catch(() => {})
|
||||
}
|
||||
tick()
|
||||
const id = setInterval(tick, 2000)
|
||||
return () => { cancelled = true; clearInterval(id) }
|
||||
}, [])
|
||||
|
||||
// Initialer State-Check + erkannte Rhino-Konfig
|
||||
const refreshStatus = useCallback(() => {
|
||||
invoke('check_dossier_initialized').then(setStatus).catch(() => {})
|
||||
}, [])
|
||||
useEffect(() => {
|
||||
refreshStatus()
|
||||
invoke('read_settings').then(s => setRhinoApp(s?.rhinoApp || 'Rhinoceros 8')).catch(() => {})
|
||||
invoke('get_default_plugin_startup_path').then(setStartupPath).catch(() => {})
|
||||
}, [refreshStatus])
|
||||
|
||||
const runInit = async () => {
|
||||
setRunning(true); setError(null); setResult(null)
|
||||
try {
|
||||
const r = await invoke('dossier_init')
|
||||
setResult(r)
|
||||
refreshStatus()
|
||||
} catch (e) {
|
||||
setError(typeof e === 'string' ? e : (e?.message || String(e)))
|
||||
} finally {
|
||||
setRunning(false)
|
||||
}
|
||||
}
|
||||
|
||||
const dot = (ok) => (
|
||||
<span style={{
|
||||
display: 'inline-block', width: 8, height: 8, borderRadius: 4,
|
||||
background: ok ? 'var(--accent)' : '#e87b6b', marginRight: 8,
|
||||
}} />
|
||||
)
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h3 style={{ marginTop: 0 }}>DOSSIER einrichten</h3>
|
||||
<p style={{ fontSize: 12, color: 'var(--text-muted)', lineHeight: 1.5 }}>
|
||||
Setzt DOSSIER auf einem frischen Mac komplett auf: installiert das C#-Plugin in Rhino via Yak,
|
||||
traegt den Python-Bootstrap als Startup-Command ein, und kopiert das DOSSIER-Window-Layout in Rhinos
|
||||
Workspaces-Folder. Idempotent — kann mehrfach ausgefuehrt werden.
|
||||
</p>
|
||||
|
||||
{/* Erkannte Konfiguration */}
|
||||
<div style={{
|
||||
marginTop: 14, padding: 10, background: 'rgba(255,255,255,0.04)',
|
||||
border: '1px solid var(--border)', borderRadius: 6, fontSize: 11,
|
||||
}}>
|
||||
<div style={{ color: 'var(--text-muted)', marginBottom: 6 }}>Erkannte Konfiguration:</div>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: '120px 1fr', gap: 4 }}>
|
||||
<span style={{ color: 'var(--text-muted)' }}>Rhino-App:</span>
|
||||
<span>{rhinoApp || '(nicht gesetzt)'}</span>
|
||||
<span style={{ color: 'var(--text-muted)' }}>startup.py:</span>
|
||||
<span style={{ wordBreak: 'break-all' }}>{startupPath || '(nicht gefunden)'}</span>
|
||||
</div>
|
||||
<div style={{ marginTop: 6, color: 'var(--text-muted)', fontSize: 10 }}>
|
||||
(Aendern unter Settings → Rhino)
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Aktueller Install-Status (live) */}
|
||||
{status && (
|
||||
<div style={{ marginTop: 12, fontSize: 11 }}>
|
||||
<div style={{ color: 'var(--text-muted)', marginBottom: 4 }}>Status:</div>
|
||||
<div>{dot(status.plugin_installed)}DOSSIER-Plugin (.rhp) installiert</div>
|
||||
<div>{dot(status.startup_cmd_set)}Python-Bootstrap in Rhino-StartupCommands</div>
|
||||
<div>{dot(status.layout_installed)}Window-Layout in Rhino-Workspaces</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<p style={{ fontSize: 11, color: 'var(--text-muted)', marginTop: 14 }}>
|
||||
Hinweis: Rhino muss waehrend des Setups <strong>geschlossen</strong> sein.
|
||||
</p>
|
||||
|
||||
<div style={{ marginTop: 16, display: 'flex', alignItems: 'center', gap: 12 }}>
|
||||
<button
|
||||
className="primary pill"
|
||||
onClick={runInit}
|
||||
disabled={running || rhinoBusy}
|
||||
title={rhinoBusy ? 'Rhino laeuft — bitte beenden' : 'Setup starten'}
|
||||
>
|
||||
{running ? 'Setup laeuft…' : 'Setup starten'}
|
||||
</button>
|
||||
{rhinoBusy && (
|
||||
<span style={{ fontSize: 11, color: '#e87b6b' }}>
|
||||
Rhino laeuft — bitte beenden.
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{result && (
|
||||
<ul style={{ listStyle: 'none', padding: 0, marginTop: 20, borderTop: '1px solid var(--border)' }}>
|
||||
{result.steps.map(s => (
|
||||
<li key={s.id} style={{ display: 'flex', alignItems: 'flex-start', gap: 10,
|
||||
padding: '10px 0', borderBottom: '1px solid var(--border)' }}>
|
||||
<span style={{ width: 16, fontSize: 14,
|
||||
color: s.status === 'ok' ? 'var(--accent)' : '#e87b6b' }}>
|
||||
{s.status === 'ok' ? '✓' : '✗'}
|
||||
</span>
|
||||
<div style={{ flex: 1, minWidth: 0 }}>
|
||||
<div style={{ fontSize: 12 }}>{s.label}</div>
|
||||
<div style={{ fontSize: 10, color: 'var(--text-muted)', marginTop: 2,
|
||||
wordBreak: 'break-all' }}>
|
||||
{s.detail}
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
<li style={{ padding: '10px 0', fontSize: 11,
|
||||
color: result.overall_ok ? 'var(--accent)' : '#e87b6b' }}>
|
||||
{result.overall_ok
|
||||
? '✓ Alle Schritte erfolgreich. Rhino oeffnen — Plugin laedt bei erstem dWall/dDoor/...-Aufruf, startup.py bootstrappt automatisch.'
|
||||
: '✗ Mindestens ein Schritt ist fehlgeschlagen. Details oben.'}
|
||||
</li>
|
||||
</ul>
|
||||
)}
|
||||
|
||||
{error && (
|
||||
<p style={{ color: '#e87b6b', marginTop: 12, fontSize: 12 }}>{error}</p>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function TabBtn({ active, onClick, children }) {
|
||||
return (
|
||||
<button
|
||||
|
||||
Reference in New Issue
Block a user