i18n DE/EN + DossierSettings panel + English file renames
i18n: - src/i18n/de.json + en.json: 200+ keys covering all main panels - src/i18n/index.js: t(key, vars) reads window.DOSSIER_LANG - panel_base.py: injects window.DOSSIER_LANG from dossier_settings.json - EbenenManager, GeschossManager, AusschnitteApp, LayoutsApp: all context menus and main labels use t() DossierSettings panel: - DossierSettingsApp.jsx: language toggle (DE/EN pill) + launcher status - toolbar.py: OPEN_SETTINGS opens new Rhino-hosted satellite window, SAVE_LANG writes lang to dossier_settings.json + reloads all panels File renames (JSX → English): - ZeichnungsebenenApp → DrawingLevelsApp - GeschossManager/Dialog/Settings → Floor* - AusschnitteApp/Settings → Viewports* - EbenenManager/Settings → Layer* - GestaltungApp → StylesApp, OberleisteApp → ToolbarApp - WerkzeugeApp → ToolsApp, DimensionenApp → DimensionsApp - MassstabApp → ScaleApp, KameraApp → CameraApp - MasseSettingsApp → UnitsSettingsApp - ConfirmDeleteEbene → ConfirmDeleteLayer - AusschnittLayerDialog → ViewportLayerDialog Python module renames: - rhinopanel.py → layers_panel.py - oberleiste.py → toolbar.py - gestaltung.py → styles.py - werkzeuge.py → tools.py - dimensionen.py → dimensions.py - startup.py _MODULE_TO_PY updated, all cross-imports fixed
This commit is contained in:
@@ -20,7 +20,7 @@ else:
|
||||
"cutAtLine": True, "namePrefix": "S",
|
||||
}
|
||||
try:
|
||||
import rhinopanel
|
||||
import layers_panel as rhinopanel
|
||||
ps = rhinopanel.load_project_settings(doc)
|
||||
d = (ps or {}).get("defaults", {})
|
||||
defaults["depthBack"] = float(d.get("schnittDepthBack", 8.0))
|
||||
|
||||
@@ -392,7 +392,7 @@ def _run():
|
||||
# gestaltung fuer Fill-Re-Apply
|
||||
_g = None
|
||||
try:
|
||||
import gestaltung as _gmod; _g = _gmod
|
||||
import styles as _gmod; _g = _gmod
|
||||
except Exception as iex:
|
||||
print("[SMART-JOIN] gestaltung import:", iex)
|
||||
|
||||
|
||||
@@ -233,7 +233,7 @@ def _run():
|
||||
_replicate_hatch(doc, nobj, hatch_props)
|
||||
else:
|
||||
try:
|
||||
import gestaltung as _gmod
|
||||
import styles as _gmod
|
||||
for nid in new_ids:
|
||||
nobj = doc.Objects.FindId(nid)
|
||||
if nobj is not None:
|
||||
|
||||
@@ -556,7 +556,7 @@ class AusschnittBridge(panel_base.BaseBridge):
|
||||
kombi = (snap.get("layerCombination") or "").strip()
|
||||
if kombi:
|
||||
try:
|
||||
import rhinopanel
|
||||
import layers_panel as rhinopanel
|
||||
rhinopanel.apply_layer_preset_by_name(doc, kombi)
|
||||
except Exception as ex:
|
||||
print("[AUSSCHNITTE] kombi-apply '{}':".format(kombi), ex)
|
||||
@@ -565,7 +565,7 @@ class AusschnittBridge(panel_base.BaseBridge):
|
||||
_apply_layers_global(doc, snap.get("layers", []))
|
||||
# Eigene Sichtbarkeit → active_comb_name clearen
|
||||
try:
|
||||
import rhinopanel
|
||||
import layers_panel as rhinopanel
|
||||
rhinopanel.set_active_comb_name(doc, None)
|
||||
rhinopanel._notify_oberleiste_combs()
|
||||
except Exception: pass
|
||||
@@ -785,7 +785,7 @@ class AusschnittBridge(panel_base.BaseBridge):
|
||||
# Listen fuer Dropdowns
|
||||
display_modes = []
|
||||
try:
|
||||
import oberleiste
|
||||
import toolbar as oberleiste
|
||||
display_modes = oberleiste._list_display_modes()
|
||||
except Exception as ex:
|
||||
print("[AUSSCHNITTE] display_modes:", ex)
|
||||
@@ -797,7 +797,7 @@ class AusschnittBridge(panel_base.BaseBridge):
|
||||
print("[AUSSCHNITTE] overrides_presets:", ex)
|
||||
layer_kombis = []
|
||||
try:
|
||||
import rhinopanel
|
||||
import layers_panel as rhinopanel
|
||||
layer_kombis = rhinopanel.list_layer_preset_names(d)
|
||||
except Exception as ex:
|
||||
print("[AUSSCHNITTE] layer_kombis:", ex)
|
||||
|
||||
+12
-12
@@ -771,7 +771,7 @@ def _find_ebene_sublayer_name(doc, keywords, default_code, default_name,
|
||||
print("[ELEMENTE] build_layers nach auto-add:", ex)
|
||||
# Ebenen-Manager UI mit-informieren via broadcast_state
|
||||
try:
|
||||
import rhinopanel
|
||||
import layers_panel as rhinopanel
|
||||
rhinopanel._broadcast_state(doc)
|
||||
except Exception as ex:
|
||||
print("[ELEMENTE] broadcast_state:", ex)
|
||||
@@ -866,7 +866,7 @@ def _ensure_referenz_child_in_doc(doc, parent_code, parent_keywords,
|
||||
z_raw = doc.Strings.GetValue("dossier_zeichnungsebenen")
|
||||
zlist = json.loads(z_raw) if z_raw else []
|
||||
if zlist: layer_builder.build_layers(doc, zlist, ebenen)
|
||||
import rhinopanel
|
||||
import layers_panel as rhinopanel
|
||||
rhinopanel._broadcast_state(doc)
|
||||
except Exception as ex:
|
||||
print("[ELEMENTE] _ensure_referenz_child:", ex)
|
||||
@@ -953,7 +953,7 @@ def _ensure_oeff_ebenen_in_doc(doc):
|
||||
z_raw = doc.Strings.GetValue("dossier_zeichnungsebenen")
|
||||
zlist = json.loads(z_raw) if z_raw else []
|
||||
if zlist: layer_builder.build_layers(doc, zlist, ebenen)
|
||||
import rhinopanel
|
||||
import layers_panel as rhinopanel
|
||||
rhinopanel._broadcast_state(doc)
|
||||
except Exception as ex:
|
||||
print("[ELEMENTE] _ensure_oeff_ebenen_in_doc build:", ex)
|
||||
@@ -3641,7 +3641,7 @@ def _get_all_materials(doc):
|
||||
merged[n].setdefault("uvScaleM", 1.0)
|
||||
merged[n].setdefault("textures", {})
|
||||
try:
|
||||
import rhinopanel
|
||||
import layers_panel as rhinopanel
|
||||
ps = rhinopanel.load_project_settings(doc) if doc else None
|
||||
if isinstance(ps, dict):
|
||||
for m in ps.get("materials", []):
|
||||
@@ -3657,7 +3657,7 @@ def _get_all_wand_styles(doc):
|
||||
"""Liefert alle Wand-Stile aus den Project-Settings. Wenn keine
|
||||
konfiguriert (auch keine Defaults greifbar), leere Liste."""
|
||||
try:
|
||||
import rhinopanel
|
||||
import layers_panel as rhinopanel
|
||||
ps = rhinopanel.load_project_settings(doc) if doc else None
|
||||
if isinstance(ps, dict):
|
||||
return list(ps.get("wand_styles", []) or [])
|
||||
@@ -13337,7 +13337,7 @@ class ElementeBridge(panel_base.BaseBridge):
|
||||
# Projekt-Adresse als Vorschlag fuer die Adress-Suche
|
||||
project_address = ""
|
||||
try:
|
||||
import rhinopanel
|
||||
import layers_panel as rhinopanel
|
||||
ps = rhinopanel.load_project_settings(doc) if doc else None
|
||||
if isinstance(ps, dict):
|
||||
project_address = (ps.get("project", {}) or {}).get("address") or ""
|
||||
@@ -14223,7 +14223,7 @@ class ElementeBridge(panel_base.BaseBridge):
|
||||
zlist = json.loads(z_raw) if z_raw else []
|
||||
if zlist:
|
||||
layer_builder.build_layers(doc, zlist, ebenen)
|
||||
import rhinopanel
|
||||
import layers_panel as rhinopanel
|
||||
rhinopanel._broadcast_state(doc)
|
||||
except Exception as ex:
|
||||
self._push_log(" swisstopo-ebenen build: {}".format(ex))
|
||||
@@ -14317,7 +14317,7 @@ class ElementeBridge(panel_base.BaseBridge):
|
||||
# UI informieren — broadcast_state schickt STATE_SYNC an
|
||||
# ebenen_bridge_ref + zeichnungsebenen_bridge_ref
|
||||
try:
|
||||
import rhinopanel
|
||||
import layers_panel as rhinopanel
|
||||
rhinopanel._broadcast_state(doc)
|
||||
except Exception as ex:
|
||||
self._push_log("broadcast_state: {}".format(ex))
|
||||
@@ -14660,7 +14660,7 @@ class ElementeBridge(panel_base.BaseBridge):
|
||||
if zlist:
|
||||
import layer_builder
|
||||
layer_builder.build_layers(doc, zlist, ebenen)
|
||||
import rhinopanel
|
||||
import layers_panel as rhinopanel
|
||||
rhinopanel._broadcast_state(doc)
|
||||
except Exception as ex:
|
||||
self._push_log("osm-ebenen build: {}".format(ex))
|
||||
@@ -16679,7 +16679,7 @@ def _migrate_plangrafik_60_to_80_once(doc):
|
||||
except Exception as ex:
|
||||
print("[ELEMENTE] build_layers nach Plangrafik-Migration:", ex)
|
||||
try:
|
||||
import rhinopanel as _rp
|
||||
import layers_panel as _rp
|
||||
_rp._broadcast_state(doc)
|
||||
except Exception: pass
|
||||
|
||||
@@ -16789,7 +16789,7 @@ def _migrate_layer_caps_once(doc):
|
||||
except Exception as ex:
|
||||
print("[ELEMENTE] build_layers nach CAPS-Migration:", ex)
|
||||
try:
|
||||
import rhinopanel as _rp
|
||||
import layers_panel as _rp
|
||||
_rp._broadcast_state(doc)
|
||||
except Exception: pass
|
||||
|
||||
@@ -16874,7 +16874,7 @@ def _migrate_referenz_layer_once(doc):
|
||||
# nicht neu angelegt hat (z.B. weil er schon existiert) wird das
|
||||
# Panel sonst nicht neu gerendert.
|
||||
try:
|
||||
import rhinopanel
|
||||
import layers_panel as rhinopanel
|
||||
rhinopanel._broadcast_state(doc)
|
||||
except Exception: pass
|
||||
except Exception as ex:
|
||||
|
||||
@@ -2204,7 +2204,7 @@ class EbenenBridge(panel_base.BaseBridge):
|
||||
# geaendert hat (nicht bei reinen Name/Farb-Aenderungen, die das
|
||||
# Settings-Dialog auch triggern koennte).
|
||||
try:
|
||||
import gestaltung
|
||||
import styles as gestaltung
|
||||
if fill_changed:
|
||||
gestaltung.refresh_layer_fills(doc)
|
||||
else:
|
||||
+1
-1
@@ -664,7 +664,7 @@ def import_material(doc, item):
|
||||
"uvScaleM", "textures"):
|
||||
if k in data: new_mat[k] = data[k]
|
||||
# Lazy-Import um Zyklen zu vermeiden
|
||||
import rhinopanel
|
||||
import layers_panel as rhinopanel
|
||||
settings = rhinopanel.load_project_settings(doc)
|
||||
mats = list(settings.get("materials", []))
|
||||
for m in mats:
|
||||
|
||||
+17
-1
@@ -20,6 +20,21 @@ import scriptcontext as sc
|
||||
|
||||
_HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
_DIST = os.path.join(_HERE, "..", "dist", "index.html")
|
||||
_SETTINGS_PATH = os.path.expanduser(
|
||||
"~/Library/Application Support/ch.gabrielevarano.Dossier/dossier_settings.json")
|
||||
|
||||
|
||||
def _read_lang():
|
||||
"""Liest die UI-Sprache aus dossier_settings.json. Default: 'de'."""
|
||||
try:
|
||||
if os.path.isfile(_SETTINGS_PATH):
|
||||
with open(_SETTINGS_PATH, "rb") as f:
|
||||
d = json.loads(f.read().decode("utf-8"))
|
||||
lang = d.get("lang", "de")
|
||||
return lang if lang in ("de", "en") else "de"
|
||||
except Exception:
|
||||
pass
|
||||
return "de"
|
||||
|
||||
|
||||
# --- Timing-Instrumentierung ------------------------------------------------
|
||||
@@ -317,7 +332,8 @@ def load_inline(wv, mode, params=None):
|
||||
_INLINE_TEMPLATE = (sig, tmpl)
|
||||
|
||||
# Per-Mount: nur das Mode-Script-Snippet bauen
|
||||
parts = ['window.PANEL_MODE="{}";'.format(mode)]
|
||||
parts = ['window.PANEL_MODE="{}";'.format(mode),
|
||||
'window.DOSSIER_LANG="{}";'.format(_read_lang())]
|
||||
if params is not None:
|
||||
try:
|
||||
parts.append('window.PANEL_PARAMS=' + json.dumps(params, ensure_ascii=False) + ';')
|
||||
|
||||
@@ -155,7 +155,7 @@ def _update_linePts(doc, schnitt_id, new_p1, new_p2):
|
||||
# Panel-Broadcast (linePts haben sich geaendert, Ebenen-Panel will
|
||||
# ggf. mit-rendern)
|
||||
try:
|
||||
import rhinopanel
|
||||
import layers_panel as rhinopanel
|
||||
rhinopanel._broadcast_state(doc)
|
||||
except Exception: pass
|
||||
try: doc.Views.Redraw()
|
||||
|
||||
+1
-1
@@ -609,7 +609,7 @@ def pick_schnitt_interactive(doc, defaults=None):
|
||||
# Project-Defaults als 2-stufiges Fallback (defaults > project > hardcoded)
|
||||
proj_d = {}
|
||||
try:
|
||||
import rhinopanel
|
||||
import layers_panel as rhinopanel
|
||||
ps = rhinopanel.load_project_settings(doc) or {}
|
||||
proj_d = ps.get("defaults", {}) or {}
|
||||
except Exception: pass
|
||||
|
||||
+6
-6
@@ -44,13 +44,13 @@ _UI_FILE = os.path.join(_HERE, "DOSSIERUI.rhw")
|
||||
# Muss synchron sein mit launcher/modules.json. Wenn neue Module dazukommen,
|
||||
# beide Stellen pflegen.
|
||||
_MODULE_TO_PY = {
|
||||
"ebenen": "rhinopanel",
|
||||
"oberleiste": "oberleiste",
|
||||
"ebenen": "layers_panel",
|
||||
"oberleiste": "toolbar",
|
||||
"ausschnitte": "ausschnitte",
|
||||
"gestaltung": "gestaltung",
|
||||
"werkzeuge": "werkzeuge",
|
||||
"gestaltung": "styles",
|
||||
"werkzeuge": "tools",
|
||||
"overrides": "overrides_panel",
|
||||
"dimensionen": "dimensionen",
|
||||
"dimensionen": "dimensions",
|
||||
"layouts": "layouts",
|
||||
"elemente": "elemente",
|
||||
}
|
||||
@@ -209,7 +209,7 @@ def _check_doc_unit(doc):
|
||||
return
|
||||
except Exception: pass
|
||||
try:
|
||||
import rhinopanel
|
||||
import layers_panel as rhinopanel
|
||||
target_unit_str = rhinopanel.get_project_unit(doc)
|
||||
target_unit_enum = rhinopanel.get_project_unit_enum(doc)
|
||||
except Exception as ex:
|
||||
|
||||
@@ -22,7 +22,7 @@ if _HERE not in sys.path:
|
||||
import panel_base
|
||||
import massstab
|
||||
import overrides
|
||||
import rhinopanel
|
||||
import layers_panel as rhinopanel
|
||||
|
||||
PANEL_GUID_STR = "7e1f6a5b-8e2f-4f3c-d5e6-f70819203b51"
|
||||
OVERRIDES_PANEL_GUID_STR = "8f2a7b6c-9f3a-4f4d-e6f7-08192a3c4d62"
|
||||
@@ -1849,13 +1849,17 @@ class OberleisteBridge(panel_base.BaseBridge):
|
||||
|
||||
# --- Settings + Window-Layout -----------------------------------
|
||||
elif t == "OPEN_SETTINGS":
|
||||
# Primaerweg: Dossier-Launcher (Tauri-App) oeffnen, dort lebt das
|
||||
# echte Settings-UI. Wenn der Launcher nicht installiert ist,
|
||||
# faellt es auf den Eto-Dialog zurueck.
|
||||
if not _launch_dossier_app():
|
||||
open_settings_dialog()
|
||||
_open_dossier_settings_panel()
|
||||
elif t == "GET_SETTINGS":
|
||||
self._send_settings_state()
|
||||
elif t == "SAVE_LANG":
|
||||
lang = (p.get("lang") or "de")
|
||||
if lang not in ("de", "en"):
|
||||
lang = "de"
|
||||
cfg = _settings_load()
|
||||
cfg["lang"] = lang
|
||||
_settings_save(cfg)
|
||||
_reload_all_panel_langs()
|
||||
elif t == "APPLY_LAYOUT":
|
||||
name = (p.get("name") or "").strip()
|
||||
if name: _apply_window_layout(name)
|
||||
@@ -2195,6 +2199,53 @@ class OberleisteBridge(panel_base.BaseBridge):
|
||||
self._send_state(force=False)
|
||||
|
||||
|
||||
# --- Dossier-Settings-Panel -------------------------------------------------
|
||||
|
||||
def _open_dossier_settings_panel():
|
||||
"""Oeffnet das Dossier-Settings-Fenster (Rhino-hosted Eto-Satellite)."""
|
||||
existing = sc.sticky.get("_dossier_settings_form")
|
||||
if existing is not None:
|
||||
try:
|
||||
existing.BringToFront()
|
||||
return
|
||||
except Exception:
|
||||
sc.sticky["_dossier_settings_form"] = None
|
||||
|
||||
cfg = _settings_load()
|
||||
launcher_ok = os.path.isfile(_LAUNCHER_PATH)
|
||||
|
||||
try:
|
||||
import panel_base
|
||||
form = panel_base.open_satellite_window(
|
||||
"dossier_settings",
|
||||
params={
|
||||
"lang": cfg.get("lang", "de"),
|
||||
"launcherOk": launcher_ok,
|
||||
},
|
||||
title="Dossier-Einstellungen",
|
||||
size=(340, 320),
|
||||
)
|
||||
sc.sticky["_dossier_settings_form"] = form
|
||||
except Exception as ex:
|
||||
print("[OBERLEISTE] open_dossier_settings_panel:", ex)
|
||||
|
||||
|
||||
def _reload_all_panel_langs():
|
||||
"""Laedt alle registrierten Panel-WebViews neu (Sprache geaendert)."""
|
||||
try:
|
||||
import panel_base
|
||||
for key, val in list(sc.sticky.items()):
|
||||
if (key.endswith("_bridge") or key.endswith("_bridge_ref")) \
|
||||
and hasattr(val, "_wv") and val._wv is not None \
|
||||
and hasattr(val, "mode"):
|
||||
try:
|
||||
panel_base.load_inline(val._wv, val.mode)
|
||||
except Exception as ex:
|
||||
print("[OBERLEISTE] reload_lang ({}): {}".format(key, ex))
|
||||
except Exception as ex:
|
||||
print("[OBERLEISTE] _reload_all_panel_langs:", ex)
|
||||
|
||||
|
||||
# --- Listener-Hookup --------------------------------------------------------
|
||||
|
||||
def _install_listeners(bridge):
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
// Copyright (C) 2026 Karim Gabriele Varano
|
||||
import { useState, useEffect, useMemo } from 'react'
|
||||
import EbenenManager from './components/EbenenManager'
|
||||
import EbenenManager from './components/LayerManager'
|
||||
import {
|
||||
applyAll, setActiveEbene,
|
||||
onMessage, notifyReady, applyVisibility,
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
// Copyright (C) 2026 Karim Gabriele Varano
|
||||
import { useState, useEffect } from 'react'
|
||||
import { notifyReady, send as bridgeSend, onMessage } from './lib/rhinoBridge'
|
||||
import Icon from './components/Icon'
|
||||
|
||||
const LANGS = [
|
||||
{ id: 'de', label: 'Deutsch' },
|
||||
{ id: 'en', label: 'English' },
|
||||
]
|
||||
|
||||
export default function DossierSettingsApp() {
|
||||
const initial = (typeof window !== 'undefined' && window.PANEL_PARAMS) || {}
|
||||
const [lang, setLang] = useState(initial.lang || 'de')
|
||||
const [launcherOk, setLauncherOk] = useState(initial.launcherOk ?? null)
|
||||
const [saved, setSaved] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
notifyReady()
|
||||
onMessage('SETTINGS', (p) => {
|
||||
if (p.lang) setLang(p.lang)
|
||||
if (p.launcherOk != null) setLauncherOk(p.launcherOk)
|
||||
})
|
||||
}, [])
|
||||
|
||||
function handleLang(id) {
|
||||
setLang(id)
|
||||
bridgeSend('SAVE_LANG', { lang: id })
|
||||
setSaved(true)
|
||||
setTimeout(() => setSaved(false), 1800)
|
||||
}
|
||||
|
||||
const label = (de, en) => lang === 'en' ? en : de
|
||||
|
||||
return (
|
||||
<div style={{
|
||||
height: '100vh',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
background: 'var(--bg-base)',
|
||||
color: 'var(--text-primary)',
|
||||
fontFamily: 'var(--font)',
|
||||
fontSize: 12,
|
||||
overflow: 'hidden',
|
||||
}}>
|
||||
{/* Header */}
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
padding: '14px 16px 10px',
|
||||
borderBottom: '1px solid var(--border-light)',
|
||||
flexShrink: 0,
|
||||
}}>
|
||||
<span style={{ fontWeight: 600, fontSize: 13, letterSpacing: '0.01em' }}>
|
||||
{label('Dossier-Einstellungen', 'Dossier Settings')}
|
||||
</span>
|
||||
{saved && (
|
||||
<span style={{
|
||||
fontSize: 10,
|
||||
color: 'var(--accent)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: 4,
|
||||
}}>
|
||||
<Icon name="check_circle" size={13} />
|
||||
{label('Gespeichert', 'Saved')}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Body */}
|
||||
<div style={{ flex: 1, overflowY: 'auto', padding: '16px' }}>
|
||||
|
||||
{/* Language */}
|
||||
<Section label={label('Sprache', 'Language')} icon="language">
|
||||
<div style={{ display: 'flex', gap: 6 }}>
|
||||
{LANGS.map(l => (
|
||||
<button
|
||||
key={l.id}
|
||||
onClick={() => handleLang(l.id)}
|
||||
style={{
|
||||
flex: 1,
|
||||
padding: '7px 0',
|
||||
borderRadius: 999,
|
||||
border: lang === l.id
|
||||
? '1.5px solid var(--accent)'
|
||||
: '1px solid var(--border)',
|
||||
background: lang === l.id ? 'var(--accent-dim)' : 'var(--bg-item)',
|
||||
color: lang === l.id ? 'var(--accent)' : 'var(--text-secondary)',
|
||||
fontFamily: 'var(--font)',
|
||||
fontSize: 11,
|
||||
fontWeight: lang === l.id ? 600 : 400,
|
||||
cursor: 'pointer',
|
||||
transition: 'all 120ms ease',
|
||||
letterSpacing: '0.02em',
|
||||
}}
|
||||
>
|
||||
{l.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<p style={{ marginTop: 8, fontSize: 10, color: 'var(--text-muted)', lineHeight: 1.4 }}>
|
||||
{label('Gilt für alle Panels — Rhino neu laden um alle anzuwenden.', 'Applies to all panels — reload Rhino to apply everywhere.')}
|
||||
</p>
|
||||
</Section>
|
||||
|
||||
{/* Launcher status */}
|
||||
<Section label={label('Launcher', 'Launcher')} icon="hub">
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: 8,
|
||||
padding: '8px 10px',
|
||||
borderRadius: 8,
|
||||
background: 'var(--bg-item)',
|
||||
border: '1px solid var(--border-light)',
|
||||
}}>
|
||||
<div style={{
|
||||
width: 7, height: 7, borderRadius: '50%', flexShrink: 0,
|
||||
background: launcherOk === true
|
||||
? 'var(--accent)'
|
||||
: launcherOk === false
|
||||
? 'var(--danger)'
|
||||
: 'var(--text-muted)',
|
||||
}} />
|
||||
<span style={{ fontSize: 11, color: 'var(--text-secondary)' }}>
|
||||
{launcherOk === true
|
||||
? label('Launcher verbunden', 'Launcher connected')
|
||||
: launcherOk === false
|
||||
? label('Launcher nicht gefunden', 'Launcher not found')
|
||||
: label('Unbekannt', 'Unknown')}
|
||||
</span>
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
</div>
|
||||
|
||||
{/* Footer */}
|
||||
<div style={{
|
||||
padding: '10px 16px',
|
||||
borderTop: '1px solid var(--border-light)',
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-end',
|
||||
flexShrink: 0,
|
||||
}}>
|
||||
<button
|
||||
className="btn-outlined"
|
||||
onClick={() => bridgeSend('CANCEL', {})}
|
||||
>
|
||||
{label('Schließen', 'Close')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function Section({ label, icon, children }) {
|
||||
return (
|
||||
<div style={{ marginBottom: 20 }}>
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: 6,
|
||||
marginBottom: 10,
|
||||
color: 'var(--text-muted)',
|
||||
fontSize: 10,
|
||||
fontWeight: 600,
|
||||
letterSpacing: '0.08em',
|
||||
textTransform: 'uppercase',
|
||||
}}>
|
||||
{icon && <Icon name={icon} size={13} />}
|
||||
{label}
|
||||
</div>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
// Copyright (C) 2026 Karim Gabriele Varano
|
||||
import { useState, useEffect, useMemo } from 'react'
|
||||
import GeschossManager from './components/GeschossManager'
|
||||
import GeschossManager from './components/FloorManager'
|
||||
import {
|
||||
applyAll, setActiveZeichnungsebene,
|
||||
onMessage, notifyReady, applyVisibility,
|
||||
@@ -1,7 +1,7 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
// Copyright (C) 2026 Karim Gabriele Varano
|
||||
import { useEffect } from 'react'
|
||||
import GeschossDialog from './components/GeschossDialog'
|
||||
import GeschossDialog from './components/FloorDialog'
|
||||
import { notifyReady, send as bridgeSend } from './lib/rhinoBridge'
|
||||
|
||||
// recalcOkff direkt hier — gleiche Logik wie in ZeichnungsebenenApp.jsx,
|
||||
@@ -1,7 +1,7 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
// Copyright (C) 2026 Karim Gabriele Varano
|
||||
import { useEffect } from 'react'
|
||||
import GeschossSettingsDialog from './components/GeschossSettingsDialog'
|
||||
import GeschossSettingsDialog from './components/FloorSettingsDialog'
|
||||
import { notifyReady, send as bridgeSend } from './lib/rhinoBridge'
|
||||
|
||||
export default function GeschossSettingsApp() {
|
||||
@@ -1,7 +1,7 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
// Copyright (C) 2026 Karim Gabriele Varano
|
||||
import { useState, useEffect } from 'react'
|
||||
import AusschnittLayerDialog from './components/AusschnittLayerDialog'
|
||||
import AusschnittLayerDialog from './components/ViewportLayerDialog'
|
||||
import { onMessage, notifyReady } from './lib/rhinoBridge'
|
||||
|
||||
function send(type, payload) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
// Copyright (C) 2026 Karim Gabriele Varano
|
||||
import { useEffect, useState, useRef } from 'react'
|
||||
import EbenenSettingsDialog from './components/EbenenSettingsDialog'
|
||||
import EbenenSettingsDialog from './components/LayerSettingsDialog'
|
||||
import { notifyReady, onMessage, send as bridgeSend } from './lib/rhinoBridge'
|
||||
|
||||
export default function EbenenSettingsApp() {
|
||||
+16
-18
@@ -3,6 +3,7 @@
|
||||
import { useEffect, useState, useRef } from 'react'
|
||||
import Icon from './components/Icon'
|
||||
import ContextMenu from './components/ContextMenu'
|
||||
import { t } from './i18n/index.js'
|
||||
import { BarButton, BarCombo, BAR_H } from './components/BarControls'
|
||||
import {
|
||||
onMessage, notifyReady,
|
||||
@@ -181,69 +182,66 @@ export default function LayoutsApp() {
|
||||
|
||||
// Kontextmenue-Items pro Layout
|
||||
const layoutCtxItems = (l) => [
|
||||
{ label: 'In Rhino oeffnen', icon: 'open_in_new',
|
||||
{ label: t('layouts.open_in_rhino'), icon: 'open_in_new',
|
||||
onClick: () => activateLayout(l.id) },
|
||||
{ label: 'Umbenennen', icon: 'edit',
|
||||
{ label: t('common.rename'), icon: 'edit',
|
||||
onClick: () => setRenamingId(l.id) },
|
||||
{ divider: true },
|
||||
{ label: 'Als PDF exportieren', icon: 'picture_as_pdf',
|
||||
{ label: t('layouts.export_pdf'), icon: 'picture_as_pdf',
|
||||
onClick: () => exportPdf(l.id, 300) },
|
||||
{ label: 'Papierformat ändern', icon: 'aspect_ratio',
|
||||
{ label: t('layouts.change_paper'), icon: 'aspect_ratio',
|
||||
onClick: () => openLayoutDialog('edit', {
|
||||
id: l.id, name: l.name, width: l.widthMm, height: l.heightMm,
|
||||
}) },
|
||||
{ divider: true },
|
||||
...(folders.length > 0 ? [
|
||||
...folders.map(f => ({
|
||||
label: `Verschieben → ${f}`,
|
||||
label: t('layouts.move_to', { folder: f }),
|
||||
icon: 'folder',
|
||||
disabled: l.folder === f,
|
||||
onClick: () => setLayoutFolder(l.id, f),
|
||||
})),
|
||||
{ label: 'Aus Ordner entfernen',
|
||||
{ label: t('layouts.remove_from_folder'),
|
||||
icon: 'folder_off',
|
||||
disabled: !l.folder,
|
||||
onClick: () => setLayoutFolder(l.id, '') },
|
||||
{ divider: true },
|
||||
] : []),
|
||||
{ label: 'Löschen', icon: 'delete', danger: true,
|
||||
{ label: t('common.delete'), icon: 'delete', danger: true,
|
||||
onClick: () => {
|
||||
if (window.confirm(`Layout "${l.name}" löschen?`)) deleteLayout(l.id)
|
||||
if (window.confirm(`${t('common.delete')} "${l.name}"?`)) deleteLayout(l.id)
|
||||
} },
|
||||
]
|
||||
|
||||
// Kontextmenue-Items pro Ordner
|
||||
const folderCtxItems = (folderName) => {
|
||||
const items = grouped[folderName] || []
|
||||
return [
|
||||
{ label: collapsedFolders.has(folderName) ? 'Aufklappen' : 'Einklappen',
|
||||
{ label: collapsedFolders.has(folderName) ? t('common.expand') : t('common.collapse'),
|
||||
icon: collapsedFolders.has(folderName) ? 'expand_more' : 'expand_less',
|
||||
onClick: () => toggleFolderCollapse(folderName) },
|
||||
{ divider: true },
|
||||
{ label: 'Alle ankreuzen / abwählen',
|
||||
{ label: t('layouts.check_all'),
|
||||
icon: 'check_box',
|
||||
onClick: () => checkAllInFolder(items) },
|
||||
{ label: `Ordner als PDF (${items.length})`,
|
||||
{ label: t('layouts.folder_pdf', { count: items.length }),
|
||||
icon: 'picture_as_pdf',
|
||||
disabled: items.length === 0,
|
||||
onClick: () => handleExportFolder(folderName) },
|
||||
{ divider: true },
|
||||
{ label: 'Ordner umbenennen',
|
||||
{ label: t('common.rename_folder'),
|
||||
icon: 'edit',
|
||||
onClick: () => {
|
||||
const next = window.prompt('Neuer Ordner-Name:', folderName)
|
||||
const next = window.prompt(t('layouts.new_folder_name'), folderName)
|
||||
if (next && next.trim() && next !== folderName) {
|
||||
// Atomar via Server-Side waere besser; simpler: alle Layouts
|
||||
// umhaengen + alten loeschen + neuen anlegen.
|
||||
addLayoutFolder(next.trim())
|
||||
items.forEach(l => setLayoutFolder(l.id, next.trim()))
|
||||
removeLayoutFolder(folderName)
|
||||
}
|
||||
} },
|
||||
{ label: 'Ordner loeschen',
|
||||
{ label: t('common.delete_folder'),
|
||||
icon: 'folder_off', danger: true,
|
||||
onClick: () => {
|
||||
if (window.confirm(`Ordner "${folderName}" loeschen? Layouts werden zur Wurzel verschoben.`))
|
||||
if (window.confirm(`${t('common.delete_folder')} "${folderName}"? ${t('layouts.delete_folder_confirm')}`))
|
||||
removeLayoutFolder(folderName)
|
||||
} },
|
||||
]
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { useState, useEffect, useMemo } from 'react'
|
||||
import Icon from './components/Icon'
|
||||
import ContextMenu from './components/ContextMenu'
|
||||
import { t } from './i18n/index.js'
|
||||
import {
|
||||
onMessage, notifyReady,
|
||||
listAusschnitte, saveAusschnitt, updateAusschnitt,
|
||||
@@ -287,25 +288,25 @@ export default function AusschnitteApp() {
|
||||
}
|
||||
|
||||
const handleAddFolder = () => {
|
||||
const name = window.prompt('Name für neuen Ordner:')
|
||||
const name = window.prompt(t('viewports.new_folder_name'))
|
||||
if (name && name.trim()) addAusschnittFolder(name.trim())
|
||||
}
|
||||
|
||||
const ctxItems = (id) => [
|
||||
{ label: 'Wiederherstellen', icon: 'restore', onClick: () => restoreAusschnitt(id) },
|
||||
{ label: 'Auf Detail anwenden', icon: 'crop_landscape', onClick: () => applyAusschnittToDetail(id) },
|
||||
{ label: t('viewports.restore'), icon: 'restore', onClick: () => restoreAusschnitt(id) },
|
||||
{ label: t('viewports.apply_to_detail'),icon: 'crop_landscape', onClick: () => applyAusschnittToDetail(id) },
|
||||
{ divider: true },
|
||||
{ label: 'Ausschnittseinstellungen…', icon: 'tune', onClick: () => openAusschnittSettings(id) },
|
||||
{ label: t('viewports.settings'), icon: 'tune', onClick: () => openAusschnittSettings(id) },
|
||||
{ divider: true },
|
||||
{ label: 'Duplizieren', icon: 'content_copy', onClick: () => duplicateAusschnitt(id) },
|
||||
{ label: 'Aktualisieren', icon: 'sync', onClick: () => updateAusschnitt(id) },
|
||||
{ label: t('common.duplicate'), icon: 'content_copy', onClick: () => duplicateAusschnitt(id) },
|
||||
{ label: t('viewports.update'), icon: 'sync', onClick: () => updateAusschnitt(id) },
|
||||
{ divider: true },
|
||||
{ label: 'Löschen', icon: 'delete', danger: true, onClick: () => deleteAusschnitt(id) },
|
||||
{ label: t('common.delete'), icon: 'delete', danger: true, onClick: () => deleteAusschnitt(id) },
|
||||
]
|
||||
|
||||
const folderCtxItems = (folderName) => [
|
||||
{ label: 'Ordner umbenennen', icon: 'edit', onClick: () => {
|
||||
const newName = window.prompt('Neuer Ordnername:', folderName)
|
||||
{ label: t('common.rename_folder'), icon: 'edit', onClick: () => {
|
||||
const newName = window.prompt(t('common.new_folder_name'), folderName)
|
||||
if (newName && newName.trim() && newName !== folderName) {
|
||||
snaps.filter(s => s.folder === folderName).forEach(s => setAusschnittFolder(s.id, newName.trim()))
|
||||
addAusschnittFolder(newName.trim())
|
||||
@@ -313,8 +314,8 @@ export default function AusschnitteApp() {
|
||||
}
|
||||
}},
|
||||
{ divider: true },
|
||||
{ label: 'Ordner löschen', icon: 'folder_off', danger: true, onClick: () => {
|
||||
if (window.confirm(`Ordner "${folderName}" löschen? Ausschnitte werden zur Wurzel verschoben.`)) {
|
||||
{ label: t('common.delete_folder'), icon: 'folder_off', danger: true, onClick: () => {
|
||||
if (window.confirm(`${t('common.delete_folder')} "${folderName}"? ${t('viewports.delete_folder_confirm')}`)) {
|
||||
removeAusschnittFolder(folderName)
|
||||
}
|
||||
}},
|
||||
@@ -3,6 +3,7 @@
|
||||
import { useState } from 'react'
|
||||
import Icon from './Icon'
|
||||
import ContextMenu from './ContextMenu'
|
||||
import { t } from '../i18n/index.js'
|
||||
import { BarCombo, BarButton } from './BarControls'
|
||||
import { openGeschossSettings, openGeschossDialog, createSchnitt } from '../lib/rhinoBridge'
|
||||
|
||||
@@ -150,12 +151,12 @@ function ZeichnungsebeneRow({
|
||||
)
|
||||
}
|
||||
|
||||
const MODES = [
|
||||
{ value: 'all_force', label: 'Alle anzeigen' },
|
||||
{ value: 'all', label: 'Ausgewählte' },
|
||||
{ value: 'active', label: 'Nur aktive' },
|
||||
{ value: 'grey', label: 'Andere grau' },
|
||||
{ value: 'grey_locked', label: 'Andere grau & gesperrt' },
|
||||
const MODES = () => [
|
||||
{ value: 'all_force', label: t('layers.show_all_mode') },
|
||||
{ value: 'all', label: t('layers.selected_mode') },
|
||||
{ value: 'active', label: t('layers.active_only_mode') },
|
||||
{ value: 'grey', label: t('layers.others_gray') },
|
||||
{ value: 'grey_locked', label: t('layers.others_gray_locked') },
|
||||
]
|
||||
|
||||
export default function GeschossManager({
|
||||
@@ -443,11 +444,11 @@ export default function GeschossManager({
|
||||
const z = zeichnungsebenen.find(x => x.id === id)
|
||||
if (!z) return []
|
||||
return [
|
||||
{ label: 'Einstellungen…', icon: 'settings', onClick: () => openGeschossSettings(z) },
|
||||
{ label: t('common.settings') + '…', icon: 'settings', onClick: () => openGeschossSettings(z) },
|
||||
{ divider: true },
|
||||
{ label: 'Duplizieren', icon: 'content_copy', onClick: () => duplicate(id) },
|
||||
{ label: t('common.duplicate'), icon: 'content_copy', onClick: () => duplicate(id) },
|
||||
{ divider: true },
|
||||
{ label: 'Löschen', icon: 'delete', danger: true,
|
||||
{ label: t('common.delete'), icon: 'delete', danger: true,
|
||||
disabled: zeichnungsebenen.length <= 1,
|
||||
onClick: () => remove(id) },
|
||||
]
|
||||
@@ -461,22 +462,22 @@ export default function GeschossManager({
|
||||
background: 'var(--bg-section)',
|
||||
borderBottom: '1px solid var(--border-light)',
|
||||
}}>
|
||||
<span className="label-xs">Sichtbarkeit</span>
|
||||
<span className="label-xs">{t('layers.visibility_mode')}</span>
|
||||
<div style={{ display: 'flex', width: '100%' }}>
|
||||
<BarCombo
|
||||
stretch
|
||||
icon="visibility"
|
||||
value={mode}
|
||||
onChange={onModeChange}
|
||||
title="Sichtbarkeits-Modus"
|
||||
title={t('layers.visibility_mode')}
|
||||
onGear={() => openGeschossDialog(zeichnungsebenen)}
|
||||
gearIcon="settings"
|
||||
gearTitle="Einstellungen"
|
||||
gearTitle={t('common.settings')}
|
||||
onSecond={openAddMenu}
|
||||
secondIcon="add"
|
||||
secondTitle="Hinzufuegen: Geschoss / Schnitt / Zeichnung"
|
||||
secondTitle={t('floors.add')}
|
||||
>
|
||||
{MODES.map(m => (
|
||||
{MODES().map(m => (
|
||||
<option key={m.value} value={m.value}>{m.label}</option>
|
||||
))}
|
||||
</BarCombo>
|
||||
@@ -501,8 +502,7 @@ export default function GeschossManager({
|
||||
if (mode === 'active' || mode === 'all_force') onModeChange('all')
|
||||
}}
|
||||
title={zeichnungsebenen.every(z => z.visible !== false)
|
||||
? 'Alle Zeichnungsebenen ausblenden'
|
||||
: 'Alle Zeichnungsebenen einblenden'}
|
||||
? t('floors.hide_all') : t('floors.show_all')}
|
||||
style={{ width: 16, height: 16,
|
||||
opacity: (mode === 'active' || mode === 'all_force') ? 0.5 : 1 }}
|
||||
>
|
||||
@@ -519,8 +519,7 @@ export default function GeschossManager({
|
||||
onChange(zeichnungsebenen.map(z => ({ ...z, locked: !anyLocked })))
|
||||
}}
|
||||
title={zeichnungsebenen.every(z => z.locked === true)
|
||||
? 'Alle Zeichnungsebenen entsperren'
|
||||
: 'Alle Zeichnungsebenen sperren'}
|
||||
? t('floors.unlock_all') : t('floors.lock_all')}
|
||||
style={{ width: 14, height: 14 }}
|
||||
>
|
||||
<Icon
|
||||
@@ -2,19 +2,20 @@
|
||||
// Copyright (C) 2026 Karim Gabriele Varano
|
||||
import { useState, useRef, useMemo, useEffect } from 'react'
|
||||
import Icon from './Icon'
|
||||
import ConfirmDeleteEbene from './ConfirmDeleteEbene'
|
||||
import ConfirmDeleteEbene from './ConfirmDeleteLayer'
|
||||
import ContextMenu from './ContextMenu'
|
||||
import { BarCombo, BarButton } from './BarControls'
|
||||
import { setLayerStyle, deleteEbene, moveSelectionToEbene, openEbenenSettings,
|
||||
pickLayerCombination, saveLayerCombination, deleteLayerCombination,
|
||||
openLayerCombinationsDialog } from '../lib/rhinoBridge'
|
||||
import { t } from '../i18n/index.js'
|
||||
|
||||
const MODES = [
|
||||
{ value: 'all_force', label: 'Alle anzeigen' },
|
||||
{ value: 'all', label: 'Ausgewählte' },
|
||||
{ value: 'active', label: 'Nur aktive' },
|
||||
{ value: 'grey', label: 'Andere grau' },
|
||||
{ value: 'grey_locked', label: 'Andere grau & gesperrt' },
|
||||
const MODES = () => [
|
||||
{ value: 'all_force', label: t('layers.show_all_mode') },
|
||||
{ value: 'all', label: t('layers.selected_mode') },
|
||||
{ value: 'active', label: t('layers.active_only_mode') },
|
||||
{ value: 'grey', label: t('layers.others_gray') },
|
||||
{ value: 'grey_locked', label: t('layers.others_gray_locked') },
|
||||
]
|
||||
|
||||
const LW_PRESETS = [0.02, 0.10, 0.13, 0.18, 0.25, 0.35, 0.50, 0.70, 1.00]
|
||||
@@ -532,20 +533,20 @@ export default function EbenenManager({
|
||||
}
|
||||
|
||||
const ctxItems = (code) => [
|
||||
{ label: 'Ebeneneinstellungen…', icon: 'settings', onClick: () => {
|
||||
{ label: t('layers.settings'), icon: 'settings', onClick: () => {
|
||||
const target = _findInTree(ebenen, code)
|
||||
if (target) openEbenenSettings(target, hatchPatterns)
|
||||
} },
|
||||
{ divider: true },
|
||||
{ label: 'Sub-Ebene hinzufügen…', icon: 'add', onClick: () => addChild(code) },
|
||||
{ label: 'Selektion hierher übertragen', icon: 'move_down', onClick: () => moveSelectionToEbene(code) },
|
||||
{ label: t('layers.add_sub'), icon: 'add', onClick: () => addChild(code) },
|
||||
{ label: t('layers.move_selection'), icon: 'move_down', onClick: () => moveSelectionToEbene(code) },
|
||||
{ divider: true },
|
||||
{ label: 'Duplizieren', icon: 'content_copy', onClick: () => duplicateEbene(code) },
|
||||
{ label: 'Eigenschaften kopieren', icon: 'colorize', onClick: () => copyProps(code) },
|
||||
{ label: 'Eigenschaften einfügen', icon: 'format_paint', onClick: () => pasteProps(code), disabled: !clipboard },
|
||||
{ label: t('common.duplicate'), icon: 'content_copy', onClick: () => duplicateEbene(code) },
|
||||
{ label: t('layers.copy_props'), icon: 'colorize', onClick: () => copyProps(code) },
|
||||
{ label: t('layers.paste_props'), icon: 'format_paint', onClick: () => pasteProps(code), disabled: !clipboard },
|
||||
{ divider: true },
|
||||
{ label: 'Löschen', icon: 'delete', onClick: () => handleDelete(code), danger: true,
|
||||
disabled: ebenen.length <= 1 },
|
||||
{ label: t('common.delete'), icon: 'delete', onClick: () => handleDelete(code), danger: true,
|
||||
disabled: ebenen.length <= 1 },
|
||||
]
|
||||
|
||||
return (
|
||||
@@ -556,7 +557,7 @@ export default function EbenenManager({
|
||||
background: 'var(--bg-section)',
|
||||
borderBottom: '1px solid var(--border-light)',
|
||||
}}>
|
||||
<span className="label-xs">Ebenenkombination</span>
|
||||
<span className="label-xs">{t('layers.combination')}</span>
|
||||
<div style={{ display: 'flex', width: '100%' }}>
|
||||
<BarCombo
|
||||
stretch
|
||||
@@ -582,21 +583,21 @@ export default function EbenenManager({
|
||||
pickLayerCombination(v === '__none__' ? null : v)
|
||||
}}
|
||||
title={activeKombi
|
||||
? `Aktive Kombi: ${activeKombi}`
|
||||
: 'Keine Kombination — manuelle Sichtbarkeit'}
|
||||
? `${t('layers.combination')}: ${activeKombi}`
|
||||
: t('layers.no_combination')}
|
||||
onGear={openLayerCombinationsDialog}
|
||||
gearTitle="Ebenenkombinationen bearbeiten"
|
||||
gearTitle={t('layers.edit_combinations')}
|
||||
>
|
||||
<option value="__none__">— Eigene —</option>
|
||||
<option value="__none__">{t('layers.custom')}</option>
|
||||
{layerCombinations.map(n => (
|
||||
<option key={n} value={n}>{n}</option>
|
||||
))}
|
||||
<option disabled>──────────</option>
|
||||
<option value="__save__">+ Aktuelle speichern…</option>
|
||||
<option value="__save__">{t('layers.save_current')}</option>
|
||||
{activeKombi && (
|
||||
<option value="__delete__">🗑 Aktuelle löschen</option>
|
||||
<option value="__delete__">🗑 {t('layers.delete_current')}</option>
|
||||
)}
|
||||
<option value="__configure__">Bearbeiten…</option>
|
||||
<option value="__configure__">{t('common.edit')}…</option>
|
||||
</BarCombo>
|
||||
</div>
|
||||
</div>
|
||||
@@ -607,19 +608,19 @@ export default function EbenenManager({
|
||||
background: 'var(--bg-section)',
|
||||
borderBottom: '1px solid var(--border-light)',
|
||||
}}>
|
||||
<span className="label-xs">Sichtbarkeit</span>
|
||||
<span className="label-xs">{t('layers.visibility_mode')}</span>
|
||||
<div style={{ display: 'flex', width: '100%' }}>
|
||||
<BarCombo
|
||||
stretch
|
||||
icon="visibility"
|
||||
value={mode}
|
||||
onChange={onModeChange}
|
||||
title="Sichtbarkeits-Modus"
|
||||
title={t('layers.visibility_mode')}
|
||||
onSecond={addNew}
|
||||
secondIcon="add"
|
||||
secondTitle="Ebene hinzufügen"
|
||||
secondTitle={t('layers.add')}
|
||||
>
|
||||
{MODES.map(m => <option key={m.value} value={m.value}>{m.label}</option>)}
|
||||
{MODES().map(m => <option key={m.value} value={m.value}>{m.label}</option>)}
|
||||
</BarCombo>
|
||||
</div>
|
||||
</div>
|
||||
@@ -642,7 +643,7 @@ export default function EbenenManager({
|
||||
if (mode === 'active' || mode === 'all_force') onModeChange('all')
|
||||
}}
|
||||
title={ebenen.every(e => e.visible !== false)
|
||||
? 'Alle Ebenen ausblenden' : 'Alle Ebenen einblenden'}
|
||||
? t('layers.hide_all') : t('layers.show_all')}
|
||||
style={{ width: 16, height: 16,
|
||||
opacity: (mode === 'active' || mode === 'all_force') ? 0.5 : 1 }}
|
||||
>
|
||||
@@ -658,7 +659,7 @@ export default function EbenenManager({
|
||||
const anyLocked = ebenen.some(e => e.locked === true)
|
||||
onChange(ebenen.map(e => ({ ...e, locked: !anyLocked })))
|
||||
}}
|
||||
title={ebenen.every(e => e.locked === true) ? 'Alle Ebenen entsperren' : 'Alle Ebenen sperren'}
|
||||
title={ebenen.every(e => e.locked === true) ? t('layers.unlock_all') : t('layers.lock_all')}
|
||||
style={{ width: 14, height: 14 }}
|
||||
>
|
||||
<Icon name={ebenen.every(e => e.locked === true) ? 'lock' : 'lock_open'} size={11} />
|
||||
@@ -0,0 +1,290 @@
|
||||
{
|
||||
"common.save": "Speichern",
|
||||
"common.cancel": "Abbrechen",
|
||||
"common.delete": "Löschen",
|
||||
"common.duplicate": "Duplizieren",
|
||||
"common.rename": "Umbenennen",
|
||||
"common.settings": "Einstellungen",
|
||||
"common.edit": "Bearbeiten",
|
||||
"common.add": "Hinzufügen",
|
||||
"common.expand": "Aufklappen",
|
||||
"common.collapse": "Einklappen",
|
||||
"common.show": "Einblenden",
|
||||
"common.hide": "Ausblenden",
|
||||
"common.lock": "Sperren",
|
||||
"common.unlock": "Entsperren",
|
||||
"common.refresh": "Aktualisieren",
|
||||
"common.close": "Schließen",
|
||||
"common.apply": "Anwenden",
|
||||
"common.new_folder": "Neuer Ordner",
|
||||
"common.rename_folder": "Ordner umbenennen",
|
||||
"common.delete_folder": "Ordner löschen",
|
||||
"common.new_folder_name": "Neuer Ordnername:",
|
||||
|
||||
"lang.de": "Deutsch",
|
||||
"lang.en": "English",
|
||||
"lang.label": "Sprache",
|
||||
|
||||
"settings.title": "Dossier-Einstellungen",
|
||||
"settings.language": "Sprache",
|
||||
"settings.language_hint": "Gilt für alle Panels",
|
||||
"settings.launcher_sync": "Launcher-Einstellungen",
|
||||
"settings.launcher_available": "Launcher verbunden",
|
||||
"settings.launcher_missing": "Launcher nicht gefunden",
|
||||
"settings.window_layout": "Fenster-Layout",
|
||||
"settings.apply_layout": "Layout automatisch anwenden",
|
||||
"settings.saved": "Gespeichert",
|
||||
|
||||
"layers.title": "Ebenen",
|
||||
"layers.settings": "Ebeneneinstellungen…",
|
||||
"layers.add_sub": "Sub-Ebene hinzufügen…",
|
||||
"layers.move_selection": "Selektion hierher übertragen",
|
||||
"layers.copy_props": "Eigenschaften kopieren",
|
||||
"layers.paste_props": "Eigenschaften einfügen",
|
||||
"layers.add": "Ebene hinzufügen",
|
||||
"layers.hide_all": "Alle Ebenen ausblenden",
|
||||
"layers.show_all": "Alle Ebenen einblenden",
|
||||
"layers.lock_all": "Alle Ebenen sperren",
|
||||
"layers.unlock_all": "Alle Ebenen entsperren",
|
||||
"layers.visibility_mode": "Sichtbarkeits-Modus",
|
||||
"layers.show_all_mode": "Alle anzeigen",
|
||||
"layers.selected_mode": "Ausgewählte",
|
||||
"layers.active_only_mode": "Nur aktive",
|
||||
"layers.others_gray": "Andere grau",
|
||||
"layers.others_gray_locked": "Andere grau & gesperrt",
|
||||
"layers.combination": "Ebenenkombination",
|
||||
"layers.no_combination": "Keine Kombination — manuelle Sichtbarkeit",
|
||||
"layers.edit_combinations": "Ebenenkombinationen bearbeiten",
|
||||
"layers.save_current": "+ Aktuelle speichern…",
|
||||
"layers.delete_current": "Aktuelle löschen",
|
||||
"layers.custom": "— Eigene —",
|
||||
"layers.dblclick_edit": "Doppelklick zum Bearbeiten",
|
||||
"layers.dblclick_rename": "Doppelklick zum Umbenennen",
|
||||
|
||||
"floors.title": "Zeichnungsebenen",
|
||||
"floors.add": "Hinzufügen: Geschoss / Schnitt / Zeichnung",
|
||||
"floors.floor": "Geschoss",
|
||||
"floors.section": "Schnitt / Ansicht",
|
||||
"floors.drawing": "Zeichnung",
|
||||
"floors.new_floor": "Neues Geschoss",
|
||||
"floors.position_relative": "Position relativ zu",
|
||||
"floors.none": "— keins —",
|
||||
"floors.insert_above": "Über dem Anker einfügen",
|
||||
"floors.insert_below": "Unter dem Anker einfügen",
|
||||
"floors.height": "Höhe (m)",
|
||||
"floors.section_height": "Schnitt (m)",
|
||||
"floors.section_height_hint": "Höhe der horizontalen Schnitt-Plane über OKFF",
|
||||
"floors.hide_all": "Alle Zeichnungsebenen ausblenden",
|
||||
"floors.show_all": "Alle Zeichnungsebenen einblenden",
|
||||
"floors.lock_all": "Alle Zeichnungsebenen sperren",
|
||||
"floors.unlock_all": "Alle Zeichnungsebenen entsperren",
|
||||
"floors.clipping_off": "Clipping Plane ausschalten",
|
||||
"floors.clipping_on": "Clipping Plane einschalten",
|
||||
"floors.copy_suffix": "Kopie",
|
||||
"floors.confirm_delete": "wirklich löschen?",
|
||||
|
||||
"viewports.title": "Ausschnitte",
|
||||
"viewports.save": "Ausschnitt speichern",
|
||||
"viewports.new_name": "Name für neuen Ausschnitt…",
|
||||
"viewports.restore": "Wiederherstellen",
|
||||
"viewports.apply_to_detail": "Auf Detail anwenden",
|
||||
"viewports.settings": "Ausschnittseinstellungen…",
|
||||
"viewports.update": "Aktualisieren",
|
||||
"viewports.empty": "Noch keine Ausschnitte.",
|
||||
"viewports.empty_hint": "Oben einen Namen eingeben und klicken.",
|
||||
"viewports.empty_folder": "Leer — Ausschnitte hier ablegen.",
|
||||
"viewports.drop_root": "Hier ablegen für Wurzel",
|
||||
"viewports.folder_actions": "Ordner-Aktionen",
|
||||
"viewports.new_folder_name": "Name für neuen Ordner:",
|
||||
"viewports.delete_folder_confirm": "Ausschnitte werden zur Wurzel verschoben.",
|
||||
"viewports.scale_hint": "Doppelklick um Maßstab einzutragen (z.B. 1:50)",
|
||||
"viewports.footer_hint": "Drag & Drop auf Ordner-Card zum Verschieben · Doppelklick = bearbeiten · ⋮ für Aktionen",
|
||||
|
||||
"layouts.title": "Layouts",
|
||||
"layouts.landscape": "Querformat",
|
||||
"layouts.portrait": "Hochformat",
|
||||
"layouts.untitled": "(unbenannt)",
|
||||
"layouts.empty": "Noch keine Layouts.",
|
||||
"layouts.empty_hint": "Unten klicken um ein neues Layout anzulegen.",
|
||||
"layouts.empty_folder": "Leer — Layouts hier ablegen.",
|
||||
"layouts.drop_remove": "Hier ablegen um aus Ordner zu entfernen",
|
||||
"layouts.new_detail": "Neues Detail (zentriert auf Seite)",
|
||||
"layouts.sync_all": "Alle Details mit ihren Ausschnitten neu synchronisieren",
|
||||
"layouts.no_details": "Keine Details auf diesem Layout.",
|
||||
"layouts.no_details_hint": "Oben klicken um eines hinzuzufügen.",
|
||||
"layouts.no_section": "— kein Ausschnitt —",
|
||||
"layouts.reapply_section": "Gebundenen Ausschnitt neu anwenden",
|
||||
"layouts.new": "Neues Layout erstellen",
|
||||
"layouts.open_in_rhino": "In Rhino öffnen",
|
||||
"layouts.export_pdf": "Als PDF exportieren",
|
||||
"layouts.export_selection_pdf": "Auswahl ({count}) als ein PDF exportieren",
|
||||
"layouts.export_all_pdf": "Alle Layouts als ein PDF exportieren",
|
||||
"layouts.select_first": "Erst Layouts ankreuzen",
|
||||
"layouts.change_paper": "Papierformat ändern",
|
||||
"layouts.move_to": "Verschieben → {folder}",
|
||||
"layouts.remove_from_folder": "Aus Ordner entfernen",
|
||||
"layouts.check_all": "Alle ankreuzen / abwählen",
|
||||
"layouts.folder_pdf": "Ordner als PDF ({count})",
|
||||
"layouts.check_for_pdf": "Für PDF-Export ankreuzen",
|
||||
"layouts.delete_detail_confirm": "Detail löschen?",
|
||||
"layouts.new_folder_name": "Neuer Ordner-Name:",
|
||||
"layouts.delete_folder_confirm": "Layouts werden zur Wurzel verschoben.",
|
||||
|
||||
"overrides.title": "Overrides",
|
||||
"overrides.layer_name": "Layer-Name",
|
||||
"overrides.object_name": "Objekt-Name",
|
||||
"overrides.contains": "enthält",
|
||||
"overrides.starts_with": "beginnt mit",
|
||||
"overrides.ends_with": "endet mit",
|
||||
"overrides.remove_condition": "Diese Bedingung entfernen",
|
||||
"overrides.logic_all": "Alle Bedingungen müssen zutreffen",
|
||||
"overrides.logic_any": "Mindestens eine Bedingung muss zutreffen",
|
||||
"overrides.add_condition": "Weitere Bedingung hinzufügen",
|
||||
"overrides.color": "Farbe",
|
||||
"overrides.lineweight": "Strichstärke",
|
||||
"overrides.linetype": "Linientyp",
|
||||
"overrides.hatch": "Schraffur",
|
||||
"overrides.hatch_scale": "Schraffur-Skala",
|
||||
"overrides.hatch_hint": "Hatch-Override modifiziert nur existierende Schraffuren.",
|
||||
"overrides.conditions": "Bedingungen",
|
||||
"overrides.overrides": "Überschreibungen",
|
||||
"overrides.combinations": "Override-Kombinationen",
|
||||
"overrides.no_combination": "— neu / keine —",
|
||||
"overrides.save_as_combination": "Als Kombination speichern…",
|
||||
"overrides.save_changes_to": "Änderungen in \"{name}\" speichern",
|
||||
"overrides.save_current_as_new": "Aktuelle Regeln als neue Kombination speichern",
|
||||
"overrides.delete_combination_confirm": "Kombination \"{name}\" dauerhaft löschen?",
|
||||
"overrides.delete_combination": "Gewählte Kombination dauerhaft löschen",
|
||||
"overrides.insert_empty": "Leere Regel oben einfügen",
|
||||
"overrides.from_template": "+ Aus Vorlage…",
|
||||
"overrides.insert_from_template": "Regel aus Vorlage einfügen",
|
||||
"overrides.delete_template": "Vorlage löschen",
|
||||
"overrides.additive_hint": "Regeln sind additiv. Bei Konflikt gewinnt die oberste.",
|
||||
"overrides.empty": "Noch keine Regeln.",
|
||||
"overrides.empty_hint": "Oben klicken um eine neue Regel zu erstellen.",
|
||||
"overrides.activate": "Aktivieren",
|
||||
"overrides.deactivate": "Deaktivieren",
|
||||
"overrides.prio_up": "Prio höher (nach oben)",
|
||||
"overrides.prio_down": "Prio tiefer (nach unten)",
|
||||
"overrides.save_as_template": "Als Vorlage speichern…",
|
||||
"overrides.template_name": "Name für Vorlage:",
|
||||
"overrides.delete_rule_confirm": "Regel \"{name}\" löschen?",
|
||||
"overrides.rule_active": "Regel aktiv",
|
||||
"overrides.logic_label": "Logik:",
|
||||
"overrides.key": "Key",
|
||||
"overrides.value": "Wert",
|
||||
"overrides.condition": "Bedingung",
|
||||
"overrides.empty_value": "(leer)",
|
||||
|
||||
"toolbar.shortcuts_hint": "Shortcuts (Shift+Klick = Über Dossier)",
|
||||
"toolbar.app_settings": "Dossier-Einstellungen",
|
||||
"toolbar.project_settings": "Projekt-Einstellungen",
|
||||
"toolbar.overrides_on": "Grafische Overrides aktiv — klick zum Ausschalten",
|
||||
"toolbar.overrides_off": "Grafische Overrides ausgeschaltet",
|
||||
"toolbar.open_overrides": "Overrides-Regel-Editor öffnen",
|
||||
"toolbar.zoom_snap": "Zoom auf 1:{scale} snappen",
|
||||
"toolbar.zoom_select_scale": "Erst einen Massstab wählen",
|
||||
"toolbar.zoom_all": "Auf gesamten Inhalt zoomen",
|
||||
"toolbar.zoom_selection": "Auf Selektion zoomen",
|
||||
"toolbar.refs_show": "Referenzlinien einblenden",
|
||||
"toolbar.refs_hide": "Referenzlinien ausblenden",
|
||||
"toolbar.osnap_on": "Object-Snap an — Klick zum Ausschalten",
|
||||
"toolbar.osnap_off": "Object-Snap aus — Klick zum Einschalten",
|
||||
"toolbar.grid_toggle": "Konstruktions-Raster ein-/ausblenden",
|
||||
"toolbar.print_view_on": "Print-View aktiv — klick zum Ausschalten",
|
||||
"toolbar.print_view_off": "Strichstärken anzeigen (Print-View)",
|
||||
"toolbar.set_scale": "Gesetzter Massstab",
|
||||
"toolbar.live_scale": "Aktueller Live-Massstab",
|
||||
"toolbar.no_scale": "Kein Massstab gesetzt",
|
||||
"toolbar.perspective_no_scale": "Perspektive — kein Massstab",
|
||||
"toolbar.scale_input": "Massstab eingeben",
|
||||
"toolbar.font": "Schriftart",
|
||||
"toolbar.text_height": "Texthöhe (m)",
|
||||
"toolbar.bold": "Fett",
|
||||
"toolbar.italic": "Kursiv",
|
||||
"toolbar.insert_text": "Neuen Text einfügen",
|
||||
"toolbar.measures_edit": "Masse bearbeiten / neues anlegen",
|
||||
"toolbar.style_save": "+ Speichern…",
|
||||
"toolbar.style_delete": "Aktiven löschen",
|
||||
"toolbar.bring_front": "In den Vordergrund",
|
||||
"toolbar.bring_forward": "Eine Stufe hoch",
|
||||
"toolbar.send_backward": "Eine Stufe runter",
|
||||
"toolbar.send_back": "In den Hintergrund",
|
||||
"toolbar.camera_settings": "Kamera-Einstellungen",
|
||||
"toolbar.detail_level_simple": "Einfach (1:100)",
|
||||
"toolbar.detail_level_standard": "Standard (1:50)",
|
||||
"toolbar.detail_level_detail": "Detail (1:20)",
|
||||
|
||||
"tools.drawing_2d": "2D Zeichnen",
|
||||
"tools.editing_2d": "2D Editieren",
|
||||
"tools.modeling_3d": "3D Modellieren",
|
||||
"tools.selection": "Auswahl",
|
||||
"tools.line": "Linie",
|
||||
"tools.polyline": "Polylinie",
|
||||
"tools.rectangle": "Rechteck",
|
||||
"tools.circle": "Kreis",
|
||||
"tools.arc": "Bogen",
|
||||
"tools.spline": "Freie Kurve (Spline)",
|
||||
"tools.text": "Text",
|
||||
"tools.hatch": "Schraffur",
|
||||
"tools.dimension": "Linearbemaßung",
|
||||
"tools.move": "Verschieben",
|
||||
"tools.copy": "Kopieren",
|
||||
"tools.rotate": "Drehen",
|
||||
"tools.scale": "Skalieren",
|
||||
"tools.mirror": "Spiegeln",
|
||||
"tools.offset": "Parallelversatz",
|
||||
"tools.trim": "Stutzen",
|
||||
"tools.extend": "Verlängern",
|
||||
"tools.join": "Verbinden",
|
||||
"tools.explode": "Auflösen",
|
||||
"tools.fillet": "Verrunden",
|
||||
"tools.array": "Polar-Array",
|
||||
"tools.extrude": "Kurve zu 3D extrudieren",
|
||||
"tools.box": "Quader",
|
||||
"tools.bool_union": "Boolean-Vereinigung",
|
||||
"tools.bool_diff": "Boolean-Differenz",
|
||||
"tools.bool_intersect": "Boolean-Schnittmenge",
|
||||
"tools.close_holes": "Planare Löcher schließen",
|
||||
"tools.section_lines": "Schnittlinien erzeugen",
|
||||
"tools.loft": "Loft",
|
||||
"tools.select_tangent": "Tangentiale Kurvenkette wählen",
|
||||
"tools.select_duplicates": "Doppelte Objekte wählen",
|
||||
"tools.select_closed": "Geschlossene Kurven wählen",
|
||||
"tools.invert_selection": "Auswahl invertieren",
|
||||
"tools.select_all": "Alle auswählen",
|
||||
"tools.deselect_all": "Auswahl aufheben",
|
||||
|
||||
"dimensions.no_selection": "Keine Selektion",
|
||||
"dimensions.no_selection_hint": "In Rhino ein oder mehrere Objekte auswählen.",
|
||||
"dimensions.world_coords": "Weltkoordinaten",
|
||||
"dimensions.active_cplane": "Aktive Konstruktionsebene",
|
||||
"dimensions.rotate_hint": "Selektion um Z-Achse der aktiven Plane drehen",
|
||||
"dimensions.rotate_ccw": "90° gegen den Uhrzeigersinn",
|
||||
"dimensions.rotate_cw": "90° im Uhrzeigersinn",
|
||||
|
||||
"measures.title": "Masse",
|
||||
"measures.subtitle": "Globale Vorgaben für Raum-Rundung und Mass-Linien",
|
||||
"measures.active": "Aktiv",
|
||||
"measures.new": "Neues Mass anlegen",
|
||||
"measures.delete_active": "Aktives Mass löschen",
|
||||
"measures.name": "Name",
|
||||
"measures.room_rounding": "Raum-Rundung",
|
||||
"measures.decimal_places": "Mass-Dezimalstellen",
|
||||
"measures.unit": "Mass-Einheit",
|
||||
"measures.hint": "Änderungen werden sofort auf alle Räume angewendet.",
|
||||
"measures.delete_confirm": "Mass \"{name}\" löschen?",
|
||||
"measures.min_one": "Mindestens ein Mass muss erhalten bleiben.",
|
||||
"measures.new_name": "Name für neues Mass:",
|
||||
"measures.new_name_placeholder": "Name für neues Mass:",
|
||||
|
||||
"about.tagline": "Architektur-Studio für Rhino 8",
|
||||
"about.part_of": "Teil von",
|
||||
"about.author": "Autor",
|
||||
"about.web": "Web",
|
||||
"about.license": "Lizenz · Dual",
|
||||
"about.license_open": "Frei nutzbar, modifizierbar und weitergebbar unter den Bedingungen der AGPL-3.0.",
|
||||
"about.license_commercial": "Kommerzielle Lizenz",
|
||||
"about.license_commercial_hint": "Für proprietäre Integrationen — Kontakt: karim@gabrielevarano.ch",
|
||||
"about.made_in": "made in Switzerland"
|
||||
}
|
||||
@@ -0,0 +1,290 @@
|
||||
{
|
||||
"common.save": "Save",
|
||||
"common.cancel": "Cancel",
|
||||
"common.delete": "Delete",
|
||||
"common.duplicate": "Duplicate",
|
||||
"common.rename": "Rename",
|
||||
"common.settings": "Settings",
|
||||
"common.edit": "Edit",
|
||||
"common.add": "Add",
|
||||
"common.expand": "Expand",
|
||||
"common.collapse": "Collapse",
|
||||
"common.show": "Show",
|
||||
"common.hide": "Hide",
|
||||
"common.lock": "Lock",
|
||||
"common.unlock": "Unlock",
|
||||
"common.refresh": "Refresh",
|
||||
"common.close": "Close",
|
||||
"common.apply": "Apply",
|
||||
"common.new_folder": "New Folder",
|
||||
"common.rename_folder": "Rename Folder",
|
||||
"common.delete_folder": "Delete Folder",
|
||||
"common.new_folder_name": "New Folder Name:",
|
||||
|
||||
"lang.de": "Deutsch",
|
||||
"lang.en": "English",
|
||||
"lang.label": "Language",
|
||||
|
||||
"settings.title": "Dossier Settings",
|
||||
"settings.language": "Language",
|
||||
"settings.language_hint": "Applies to all panels",
|
||||
"settings.launcher_sync": "Launcher Settings",
|
||||
"settings.launcher_available": "Launcher connected",
|
||||
"settings.launcher_missing": "Launcher not found",
|
||||
"settings.window_layout": "Window Layout",
|
||||
"settings.apply_layout": "Auto-apply layout",
|
||||
"settings.saved": "Saved",
|
||||
|
||||
"layers.title": "Layers",
|
||||
"layers.settings": "Layer Settings…",
|
||||
"layers.add_sub": "Add Sub-Layer…",
|
||||
"layers.move_selection": "Move Selection Here",
|
||||
"layers.copy_props": "Copy Properties",
|
||||
"layers.paste_props": "Paste Properties",
|
||||
"layers.add": "Add Layer",
|
||||
"layers.hide_all": "Hide All Layers",
|
||||
"layers.show_all": "Show All Layers",
|
||||
"layers.lock_all": "Lock All Layers",
|
||||
"layers.unlock_all": "Unlock All Layers",
|
||||
"layers.visibility_mode": "Visibility Mode",
|
||||
"layers.show_all_mode": "Show All",
|
||||
"layers.selected_mode": "Selected",
|
||||
"layers.active_only_mode": "Active Only",
|
||||
"layers.others_gray": "Others Gray",
|
||||
"layers.others_gray_locked": "Others Gray & Locked",
|
||||
"layers.combination": "Layer Combination",
|
||||
"layers.no_combination": "No Combination — Manual Visibility",
|
||||
"layers.edit_combinations": "Edit Layer Combinations",
|
||||
"layers.save_current": "+ Save Current…",
|
||||
"layers.delete_current": "Delete Current",
|
||||
"layers.custom": "— Custom —",
|
||||
"layers.dblclick_edit": "Double-click to Edit",
|
||||
"layers.dblclick_rename": "Double-click to Rename",
|
||||
|
||||
"floors.title": "Drawing Levels",
|
||||
"floors.add": "Add: Floor / Section / Drawing",
|
||||
"floors.floor": "Floor",
|
||||
"floors.section": "Section / View",
|
||||
"floors.drawing": "Drawing",
|
||||
"floors.new_floor": "New Floor",
|
||||
"floors.position_relative": "Position Relative To",
|
||||
"floors.none": "— none —",
|
||||
"floors.insert_above": "Insert Above Anchor",
|
||||
"floors.insert_below": "Insert Below Anchor",
|
||||
"floors.height": "Height (m)",
|
||||
"floors.section_height": "Section (m)",
|
||||
"floors.section_height_hint": "Height of Section Plane Above OKFF",
|
||||
"floors.hide_all": "Hide All Drawing Levels",
|
||||
"floors.show_all": "Show All Drawing Levels",
|
||||
"floors.lock_all": "Lock All Drawing Levels",
|
||||
"floors.unlock_all": "Unlock All Drawing Levels",
|
||||
"floors.clipping_off": "Turn Off Clipping Plane",
|
||||
"floors.clipping_on": "Turn On Clipping Plane",
|
||||
"floors.copy_suffix": "Copy",
|
||||
"floors.confirm_delete": "Really delete?",
|
||||
|
||||
"viewports.title": "Viewports",
|
||||
"viewports.save": "Save Viewport",
|
||||
"viewports.new_name": "Name for New Viewport…",
|
||||
"viewports.restore": "Restore",
|
||||
"viewports.apply_to_detail": "Apply to Detail",
|
||||
"viewports.settings": "Viewport Settings…",
|
||||
"viewports.update": "Update",
|
||||
"viewports.empty": "No Viewports Yet.",
|
||||
"viewports.empty_hint": "Enter a name above and click.",
|
||||
"viewports.empty_folder": "Empty — Drop viewports here.",
|
||||
"viewports.drop_root": "Drop here for root",
|
||||
"viewports.folder_actions": "Folder Actions",
|
||||
"viewports.new_folder_name": "Name for New Folder:",
|
||||
"viewports.delete_folder_confirm": "Viewports will be moved to root.",
|
||||
"viewports.scale_hint": "Double-click to enter scale (e.g. 1:50)",
|
||||
"viewports.footer_hint": "Drag & Drop onto folder card to move · Double-click = edit · ⋮ for actions",
|
||||
|
||||
"layouts.title": "Layouts",
|
||||
"layouts.landscape": "Landscape",
|
||||
"layouts.portrait": "Portrait",
|
||||
"layouts.untitled": "(Untitled)",
|
||||
"layouts.empty": "No Layouts Yet.",
|
||||
"layouts.empty_hint": "Click below to create a new layout.",
|
||||
"layouts.empty_folder": "Empty — Drop layouts here.",
|
||||
"layouts.drop_remove": "Drop here to remove from folder",
|
||||
"layouts.new_detail": "New Detail (Centered on Page)",
|
||||
"layouts.sync_all": "Re-sync All Details with Their Viewports",
|
||||
"layouts.no_details": "No Details on This Layout.",
|
||||
"layouts.no_details_hint": "Click above to add one.",
|
||||
"layouts.no_section": "— no viewport —",
|
||||
"layouts.reapply_section": "Re-apply Bound Viewport",
|
||||
"layouts.new": "Create New Layout",
|
||||
"layouts.open_in_rhino": "Open in Rhino",
|
||||
"layouts.export_pdf": "Export as PDF",
|
||||
"layouts.export_selection_pdf": "Export Selection ({count}) as PDF",
|
||||
"layouts.export_all_pdf": "Export All Layouts as PDF",
|
||||
"layouts.select_first": "Select Layouts First",
|
||||
"layouts.change_paper": "Change Paper Format",
|
||||
"layouts.move_to": "Move → {folder}",
|
||||
"layouts.remove_from_folder": "Remove from Folder",
|
||||
"layouts.check_all": "Check All / Uncheck All",
|
||||
"layouts.folder_pdf": "Folder as PDF ({count})",
|
||||
"layouts.check_for_pdf": "Check for PDF Export",
|
||||
"layouts.delete_detail_confirm": "Delete Detail?",
|
||||
"layouts.new_folder_name": "New Folder Name:",
|
||||
"layouts.delete_folder_confirm": "Layouts will be moved to root.",
|
||||
|
||||
"overrides.title": "Overrides",
|
||||
"overrides.layer_name": "Layer Name",
|
||||
"overrides.object_name": "Object Name",
|
||||
"overrides.contains": "contains",
|
||||
"overrides.starts_with": "starts with",
|
||||
"overrides.ends_with": "ends with",
|
||||
"overrides.remove_condition": "Remove This Condition",
|
||||
"overrides.logic_all": "All Conditions Must Be Met",
|
||||
"overrides.logic_any": "At Least One Condition Must Be Met",
|
||||
"overrides.add_condition": "Add Another Condition",
|
||||
"overrides.color": "Color",
|
||||
"overrides.lineweight": "Line Weight",
|
||||
"overrides.linetype": "Line Type",
|
||||
"overrides.hatch": "Hatch Pattern",
|
||||
"overrides.hatch_scale": "Hatch Scale",
|
||||
"overrides.hatch_hint": "Hatch Override modifies only existing hatches.",
|
||||
"overrides.conditions": "Conditions",
|
||||
"overrides.overrides": "Overrides",
|
||||
"overrides.combinations": "Override Combinations",
|
||||
"overrides.no_combination": "— new / none —",
|
||||
"overrides.save_as_combination": "Save as Combination…",
|
||||
"overrides.save_changes_to": "Save Changes to \"{name}\"",
|
||||
"overrides.save_current_as_new": "Save Current Rules as New Combination",
|
||||
"overrides.delete_combination_confirm": "Permanently delete combination \"{name}\"?",
|
||||
"overrides.delete_combination": "Permanently Delete Selected Combination",
|
||||
"overrides.insert_empty": "Insert Empty Rule Above",
|
||||
"overrides.from_template": "+ From Template…",
|
||||
"overrides.insert_from_template": "Insert Rule from Template",
|
||||
"overrides.delete_template": "Delete Template",
|
||||
"overrides.additive_hint": "Rules are additive. In case of conflict, the topmost wins.",
|
||||
"overrides.empty": "No Rules Yet.",
|
||||
"overrides.empty_hint": "Click above to create a new rule.",
|
||||
"overrides.activate": "Activate",
|
||||
"overrides.deactivate": "Deactivate",
|
||||
"overrides.prio_up": "Higher Priority (Up)",
|
||||
"overrides.prio_down": "Lower Priority (Down)",
|
||||
"overrides.save_as_template": "Save as Template…",
|
||||
"overrides.template_name": "Name for Template:",
|
||||
"overrides.delete_rule_confirm": "Delete rule \"{name}\"?",
|
||||
"overrides.rule_active": "Rule Active",
|
||||
"overrides.logic_label": "Logic:",
|
||||
"overrides.key": "Key",
|
||||
"overrides.value": "Value",
|
||||
"overrides.condition": "Condition",
|
||||
"overrides.empty_value": "(empty)",
|
||||
|
||||
"toolbar.shortcuts_hint": "Shortcuts (Shift+Click = About Dossier)",
|
||||
"toolbar.app_settings": "Dossier Settings",
|
||||
"toolbar.project_settings": "Project Settings",
|
||||
"toolbar.overrides_on": "Graphic Overrides Active — click to turn off",
|
||||
"toolbar.overrides_off": "Graphic Overrides Off",
|
||||
"toolbar.open_overrides": "Open Overrides Editor",
|
||||
"toolbar.zoom_snap": "Snap Zoom to 1:{scale}",
|
||||
"toolbar.zoom_select_scale": "Select a Scale First",
|
||||
"toolbar.zoom_all": "Zoom to All Content",
|
||||
"toolbar.zoom_selection": "Zoom to Selection",
|
||||
"toolbar.refs_show": "Show Reference Lines",
|
||||
"toolbar.refs_hide": "Hide Reference Lines",
|
||||
"toolbar.osnap_on": "Object Snap On — click to turn off",
|
||||
"toolbar.osnap_off": "Object Snap Off — click to turn on",
|
||||
"toolbar.grid_toggle": "Toggle Construction Grid",
|
||||
"toolbar.print_view_on": "Print View Active — click to turn off",
|
||||
"toolbar.print_view_off": "Show Line Weights (Print View)",
|
||||
"toolbar.set_scale": "Set Scale",
|
||||
"toolbar.live_scale": "Current Live Scale",
|
||||
"toolbar.no_scale": "No Scale Set",
|
||||
"toolbar.perspective_no_scale": "Perspective — No Scale",
|
||||
"toolbar.scale_input": "Enter Scale",
|
||||
"toolbar.font": "Font",
|
||||
"toolbar.text_height": "Text Height (m)",
|
||||
"toolbar.bold": "Bold",
|
||||
"toolbar.italic": "Italic",
|
||||
"toolbar.insert_text": "Insert New Text",
|
||||
"toolbar.measures_edit": "Edit Measures / Create New",
|
||||
"toolbar.style_save": "+ Save…",
|
||||
"toolbar.style_delete": "Delete Active",
|
||||
"toolbar.bring_front": "Bring to Front",
|
||||
"toolbar.bring_forward": "Bring Forward",
|
||||
"toolbar.send_backward": "Send Backward",
|
||||
"toolbar.send_back": "Send to Back",
|
||||
"toolbar.camera_settings": "Camera Settings",
|
||||
"toolbar.detail_level_simple": "Simple (1:100)",
|
||||
"toolbar.detail_level_standard": "Standard (1:50)",
|
||||
"toolbar.detail_level_detail": "Detail (1:20)",
|
||||
|
||||
"tools.drawing_2d": "2D Drawing",
|
||||
"tools.editing_2d": "2D Editing",
|
||||
"tools.modeling_3d": "3D Modeling",
|
||||
"tools.selection": "Selection",
|
||||
"tools.line": "Line",
|
||||
"tools.polyline": "Polyline",
|
||||
"tools.rectangle": "Rectangle",
|
||||
"tools.circle": "Circle",
|
||||
"tools.arc": "Arc",
|
||||
"tools.spline": "Free Curve (Spline)",
|
||||
"tools.text": "Text",
|
||||
"tools.hatch": "Hatch",
|
||||
"tools.dimension": "Linear Dimension",
|
||||
"tools.move": "Move",
|
||||
"tools.copy": "Copy",
|
||||
"tools.rotate": "Rotate",
|
||||
"tools.scale": "Scale",
|
||||
"tools.mirror": "Mirror",
|
||||
"tools.offset": "Offset",
|
||||
"tools.trim": "Trim",
|
||||
"tools.extend": "Extend",
|
||||
"tools.join": "Join",
|
||||
"tools.explode": "Explode",
|
||||
"tools.fillet": "Fillet",
|
||||
"tools.array": "Polar Array",
|
||||
"tools.extrude": "Extrude Curve to 3D",
|
||||
"tools.box": "Box",
|
||||
"tools.bool_union": "Boolean Union",
|
||||
"tools.bool_diff": "Boolean Difference",
|
||||
"tools.bool_intersect": "Boolean Intersection",
|
||||
"tools.close_holes": "Close Planar Holes",
|
||||
"tools.section_lines": "Create Section Lines",
|
||||
"tools.loft": "Loft",
|
||||
"tools.select_tangent": "Select Tangent Curve Chain",
|
||||
"tools.select_duplicates": "Select Duplicate Objects",
|
||||
"tools.select_closed": "Select Closed Curves",
|
||||
"tools.invert_selection": "Invert Selection",
|
||||
"tools.select_all": "Select All",
|
||||
"tools.deselect_all": "Deselect All",
|
||||
|
||||
"dimensions.no_selection": "No Selection",
|
||||
"dimensions.no_selection_hint": "Select one or more objects in Rhino.",
|
||||
"dimensions.world_coords": "World Coordinates",
|
||||
"dimensions.active_cplane": "Active Construction Plane",
|
||||
"dimensions.rotate_hint": "Rotate selection around Z-axis of active plane",
|
||||
"dimensions.rotate_ccw": "90° Counterclockwise",
|
||||
"dimensions.rotate_cw": "90° Clockwise",
|
||||
|
||||
"measures.title": "Measures",
|
||||
"measures.subtitle": "Global defaults for room rounding and measure lines",
|
||||
"measures.active": "Active",
|
||||
"measures.new": "Create New Measure",
|
||||
"measures.delete_active": "Delete Active Measure",
|
||||
"measures.name": "Name",
|
||||
"measures.room_rounding": "Room Rounding",
|
||||
"measures.decimal_places": "Decimal Places",
|
||||
"measures.unit": "Unit",
|
||||
"measures.hint": "Changes apply immediately to all rooms.",
|
||||
"measures.delete_confirm": "Delete measure \"{name}\"?",
|
||||
"measures.min_one": "At least one measure must remain.",
|
||||
"measures.new_name": "Name for new measure:",
|
||||
"measures.new_name_placeholder": "Name for new measure:",
|
||||
|
||||
"about.tagline": "Architecture Studio for Rhino 8",
|
||||
"about.part_of": "Part of",
|
||||
"about.author": "Author",
|
||||
"about.web": "Web",
|
||||
"about.license": "License · Dual",
|
||||
"about.license_open": "Freely usable, modifiable and distributable under AGPL-3.0.",
|
||||
"about.license_commercial": "Commercial License",
|
||||
"about.license_commercial_hint": "For proprietary integrations — contact: karim@gabrielevarano.ch",
|
||||
"about.made_in": "made in Switzerland"
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import de from './de.json'
|
||||
import en from './en.json'
|
||||
|
||||
const _langs = { de, en }
|
||||
|
||||
function _getLang() {
|
||||
return (typeof window !== 'undefined' && window.DOSSIER_LANG) || 'de'
|
||||
}
|
||||
|
||||
export function t(key, vars) {
|
||||
const lang = _getLang()
|
||||
const dict = _langs[lang] || _langs.de
|
||||
const str = dict[key] ?? _langs.de[key] ?? key
|
||||
if (!vars) return str
|
||||
return str.replace(/\{(\w+)\}/g, (_, k) => (vars[k] ?? `{${k}}`))
|
||||
}
|
||||
|
||||
export function getLang() { return _getLang() }
|
||||
+38
-30
@@ -3,63 +3,71 @@
|
||||
import { StrictMode } from 'react'
|
||||
import { createRoot } from 'react-dom/client'
|
||||
import './index.css'
|
||||
|
||||
// Set language before any component renders
|
||||
if (typeof window !== 'undefined' && window.DOSSIER_LANG == null) {
|
||||
window.DOSSIER_LANG = 'de'
|
||||
}
|
||||
|
||||
import App from './App.jsx'
|
||||
import ZeichnungsebenenApp from './ZeichnungsebenenApp.jsx'
|
||||
import GeschossSettingsApp from './GeschossSettingsApp.jsx'
|
||||
import DrawingLevelsApp from './DrawingLevelsApp.jsx'
|
||||
import FloorSettingsApp from './FloorSettingsApp.jsx'
|
||||
import FloorDialogApp from './FloorDialogApp.jsx'
|
||||
import ProjectSettingsApp from './ProjectSettingsApp.jsx'
|
||||
import DossierSettingsApp from './DossierSettingsApp.jsx'
|
||||
import LibraryApp from './LibraryApp.jsx'
|
||||
import SymbolPickerApp from './SymbolPickerApp.jsx'
|
||||
import EbenenSettingsApp from './EbenenSettingsApp.jsx'
|
||||
import GeschossDialogApp from './GeschossDialogApp.jsx'
|
||||
import LayerSettingsApp from './LayerSettingsApp.jsx'
|
||||
import LayerCombinationsApp from './LayerCombinationsApp.jsx'
|
||||
import AusschnittSettingsApp from './AusschnittSettingsApp.jsx'
|
||||
import ViewportSettingsApp from './ViewportSettingsApp.jsx'
|
||||
import LayoutDialogApp from './LayoutDialogApp.jsx'
|
||||
import SwisstopoApp from './SwisstopoApp.jsx'
|
||||
import OsmApp from './OsmApp.jsx'
|
||||
import KameraApp from './KameraApp.jsx'
|
||||
import MasseSettingsApp from './MasseSettingsApp.jsx'
|
||||
import CameraApp from './CameraApp.jsx'
|
||||
import UnitsSettingsApp from './UnitsSettingsApp.jsx'
|
||||
import AboutApp from './AboutApp.jsx'
|
||||
import TextEditorApp from './TextEditorApp.jsx'
|
||||
import GestaltungApp from './GestaltungApp.jsx'
|
||||
import AusschnitteApp from './AusschnitteApp.jsx'
|
||||
import MassstabApp from './MassstabApp.jsx'
|
||||
import WerkzeugeApp from './WerkzeugeApp.jsx'
|
||||
import OberleisteApp from './OberleisteApp.jsx'
|
||||
import StylesApp from './StylesApp.jsx'
|
||||
import ViewportsApp from './ViewportsApp.jsx'
|
||||
import ScaleApp from './ScaleApp.jsx'
|
||||
import ToolsApp from './ToolsApp.jsx'
|
||||
import ToolbarApp from './ToolbarApp.jsx'
|
||||
import OverridesApp from './OverridesApp.jsx'
|
||||
import DimensionenApp from './DimensionenApp.jsx'
|
||||
import DimensionsApp from './DimensionsApp.jsx'
|
||||
import LayoutsApp from './LayoutsApp.jsx'
|
||||
import ElementeApp from './ElementeApp.jsx'
|
||||
import ElementeUebersichtApp from './ElementeUebersichtApp.jsx'
|
||||
import ElementePropertiesApp from './ElementePropertiesApp.jsx'
|
||||
import ElementsOverviewApp from './ElementsOverviewApp.jsx'
|
||||
import ElementPropertiesApp from './ElementPropertiesApp.jsx'
|
||||
|
||||
const mode = (typeof window !== 'undefined' && window.PANEL_MODE) || 'ebenen'
|
||||
const RootApp = mode === 'gestaltung' ? GestaltungApp
|
||||
: mode === 'ausschnitte' ? AusschnitteApp
|
||||
: mode === 'massstab' ? MassstabApp
|
||||
: mode === 'werkzeuge' ? WerkzeugeApp
|
||||
: mode === 'oberleiste' ? OberleisteApp
|
||||
const RootApp = mode === 'gestaltung' ? StylesApp
|
||||
: mode === 'ausschnitte' ? ViewportsApp
|
||||
: mode === 'massstab' ? ScaleApp
|
||||
: mode === 'werkzeuge' ? ToolsApp
|
||||
: mode === 'oberleiste' ? ToolbarApp
|
||||
: mode === 'overrides' ? OverridesApp
|
||||
: mode === 'dimensionen' ? DimensionenApp
|
||||
: mode === 'dimensionen' ? DimensionsApp
|
||||
: mode === 'layouts' ? LayoutsApp
|
||||
: mode === 'elemente' ? ElementeApp
|
||||
: mode === 'zeichnungsebenen' ? ZeichnungsebenenApp
|
||||
: mode === 'geschoss_settings' ? GeschossSettingsApp
|
||||
: mode === 'zeichnungsebenen' ? DrawingLevelsApp
|
||||
: mode === 'geschoss_settings' ? FloorSettingsApp
|
||||
: mode === 'project_settings' ? ProjectSettingsApp
|
||||
: mode === 'dossier_settings' ? DossierSettingsApp
|
||||
: mode === 'library' ? LibraryApp
|
||||
: mode === 'symbol_picker' ? SymbolPickerApp
|
||||
: mode === 'ebenen_settings' ? EbenenSettingsApp
|
||||
: mode === 'geschoss_dialog' ? GeschossDialogApp
|
||||
: mode === 'ebenen_settings' ? LayerSettingsApp
|
||||
: mode === 'geschoss_dialog' ? FloorDialogApp
|
||||
: mode === 'layer_combinations' ? LayerCombinationsApp
|
||||
: mode === 'ausschnitt_settings' ? AusschnittSettingsApp
|
||||
: mode === 'ausschnitt_settings' ? ViewportSettingsApp
|
||||
: mode === 'layout_dialog' ? LayoutDialogApp
|
||||
: mode === 'swisstopo' ? SwisstopoApp
|
||||
: mode === 'osm' ? OsmApp
|
||||
: mode === 'kamera' ? KameraApp
|
||||
: mode === 'masse_settings' ? MasseSettingsApp
|
||||
: mode === 'kamera' ? CameraApp
|
||||
: mode === 'masse_settings' ? UnitsSettingsApp
|
||||
: mode === 'about' ? AboutApp
|
||||
: mode === 'text_editor' ? TextEditorApp
|
||||
: mode === 'elemente_uebersicht' ? ElementeUebersichtApp
|
||||
: mode === 'elemente_properties' ? ElementePropertiesApp
|
||||
: mode === 'elemente_uebersicht' ? ElementsOverviewApp
|
||||
: mode === 'elemente_properties' ? ElementPropertiesApp
|
||||
: App
|
||||
|
||||
document.addEventListener('contextmenu', e => e.preventDefault(), true)
|
||||
|
||||
Reference in New Issue
Block a user