Initial commit — Dossier Rhino 8 Plugin
OpenStudio-Suite Architektur-Plugin fuer Rhino 8 (Mac): - Smart-Elemente: Wand, Decke, Dach (Pult/Sattel/Walm/Mansarde), Oeffnungen (Fenster/Tueren mit Rahmen + Sims + Glas + Fluegel), Treppen (gerade · L · Wendel mit Schrittmass-Validierung) - Live-Previews mit Step-Lines + Soll-Range-Clamping - Bidirektionale Selection-Sync zwischen Source-Linie und Volume - Geschoss-/Ebenen-Verwaltung mit OKFF-Persistenz - Layouts mit PDF-Export - Ausschnitte / Massstab / Override-Regeln - Petrol-Gruen Theme (Rapport-konform) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,174 @@
|
||||
// Verhindert ein extra Konsolen-Fenster auf Windows im Release-Build.
|
||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Command;
|
||||
|
||||
// Statisches Modul-Manifest — in die Binary einkompiliert, sodass die App
|
||||
// keine externe Datei zur Laufzeit braucht. Wer Module aendert: modules.json
|
||||
// im launcher-Root bearbeiten, dann neu bauen.
|
||||
const MODULES_JSON: &str = include_str!("../../modules.json");
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
struct Project {
|
||||
name: String,
|
||||
path: String,
|
||||
modules: Vec<String>,
|
||||
#[serde(rename = "lastOpened")]
|
||||
last_opened: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
struct ProjectConfig {
|
||||
name: String,
|
||||
modules: Vec<String>,
|
||||
#[serde(rename = "dossierVersion")]
|
||||
dossier_version: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
struct Settings {
|
||||
// App-Name oder absoluter Pfad zur Rhino-App. `open -a` akzeptiert beides:
|
||||
// "Rhinoceros 8" -> sucht in /Applications
|
||||
// "/Applications/Rhino 8.app" oder ein Beta-Build irgendwo anders.
|
||||
#[serde(rename = "rhinoApp")]
|
||||
rhino_app: String,
|
||||
}
|
||||
|
||||
impl Default for Settings {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
rhino_app: "Rhinoceros 8".into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn dossier_dir() -> PathBuf {
|
||||
// ~/Library/Application Support/Dossier auf macOS,
|
||||
// entsprechend Plattform-Pendants sonst.
|
||||
let dir = directories::ProjectDirs::from("ch", "gabrielevarano", "Dossier")
|
||||
.map(|p| p.data_dir().to_path_buf())
|
||||
.unwrap_or_else(|| PathBuf::from("."));
|
||||
fs::create_dir_all(&dir).ok();
|
||||
dir
|
||||
}
|
||||
|
||||
fn recent_path() -> PathBuf {
|
||||
dossier_dir().join("recent.json")
|
||||
}
|
||||
|
||||
fn settings_path() -> PathBuf {
|
||||
dossier_dir().join("settings.json")
|
||||
}
|
||||
|
||||
fn load_settings() -> Settings {
|
||||
let p = settings_path();
|
||||
if !p.exists() {
|
||||
return Settings::default();
|
||||
}
|
||||
fs::read_to_string(&p)
|
||||
.ok()
|
||||
.and_then(|raw| serde_json::from_str(&raw).ok())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn list_recent() -> Vec<Project> {
|
||||
let p = recent_path();
|
||||
if !p.exists() {
|
||||
return vec![];
|
||||
}
|
||||
let raw = fs::read_to_string(&p).unwrap_or_default();
|
||||
serde_json::from_str(&raw).unwrap_or_default()
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn save_recent(projects: Vec<Project>) -> Result<(), String> {
|
||||
let p = recent_path();
|
||||
let raw = serde_json::to_string_pretty(&projects).map_err(|e| e.to_string())?;
|
||||
fs::write(&p, raw).map_err(|e| format!("recent.json schreiben: {e}"))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn write_project_config(
|
||||
path3dm: String,
|
||||
name: String,
|
||||
modules: Vec<String>,
|
||||
) -> Result<(), String> {
|
||||
let p = Path::new(&path3dm);
|
||||
let dir = p
|
||||
.parent()
|
||||
.ok_or_else(|| format!("Kein gueltiger Ordner aus Pfad: {path3dm}"))?;
|
||||
let config_path = dir.join("dossier.project.json");
|
||||
let config = ProjectConfig {
|
||||
name,
|
||||
modules,
|
||||
dossier_version: env!("CARGO_PKG_VERSION").to_string(),
|
||||
};
|
||||
let raw = serde_json::to_string_pretty(&config).map_err(|e| e.to_string())?;
|
||||
fs::write(&config_path, raw).map_err(|e| format!("project-config schreiben: {e}"))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn read_project_config(path3dm: String) -> Result<Option<ProjectConfig>, String> {
|
||||
let p = Path::new(&path3dm);
|
||||
let dir = p.parent().ok_or_else(|| "Pfad ungueltig".to_string())?;
|
||||
let config_path = dir.join("dossier.project.json");
|
||||
if !config_path.exists() {
|
||||
return Ok(None);
|
||||
}
|
||||
let raw = fs::read_to_string(&config_path).map_err(|e| e.to_string())?;
|
||||
let cfg: ProjectConfig = serde_json::from_str(&raw).map_err(|e| e.to_string())?;
|
||||
Ok(Some(cfg))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn open_rhino(path3dm: String) -> Result<(), String> {
|
||||
// macOS: `open -a <app> <file>`. `<app>` kann ein App-Name (in /Applications
|
||||
// gesucht) oder ein absoluter Pfad zur .app sein. Aus den Settings — Default
|
||||
// "Rhinoceros 8", falls der User nichts angepasst hat.
|
||||
let settings = load_settings();
|
||||
Command::new("open")
|
||||
.args(["-a", &settings.rhino_app, &path3dm])
|
||||
.spawn()
|
||||
.map_err(|e| format!(
|
||||
"open-Befehl fehlgeschlagen ({}): {e}", settings.rhino_app
|
||||
))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn read_settings() -> Settings {
|
||||
load_settings()
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn save_settings(settings: Settings) -> Result<(), String> {
|
||||
let raw = serde_json::to_string_pretty(&settings).map_err(|e| e.to_string())?;
|
||||
fs::write(settings_path(), raw).map_err(|e| format!("settings.json schreiben: {e}"))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn read_modules_manifest() -> Result<serde_json::Value, String> {
|
||||
serde_json::from_str(MODULES_JSON).map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||
pub fn run() {
|
||||
tauri::Builder::default()
|
||||
.plugin(tauri_plugin_dialog::init())
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
list_recent,
|
||||
save_recent,
|
||||
write_project_config,
|
||||
read_project_config,
|
||||
open_rhino,
|
||||
read_modules_manifest,
|
||||
read_settings,
|
||||
save_settings
|
||||
])
|
||||
.run(tauri::generate_context!())
|
||||
.expect("Fehler beim Starten der Tauri-App");
|
||||
}
|
||||
Reference in New Issue
Block a user