Archiv-Formular mit Titel-Feld; Panel-Fenster einzeilige Overlay-Titelbar; Header name@pool ohne Chip
- Archivieren: Titel-Feld, vorbelegt mit der ersten Ueberschrift (panel_title_cmd), bestimmt Frontmatter-title und Dateinamen-Slug; archive_panel (MCP) nimmt title ebenfalls - Panel-/Archiv-Fenster (macOS): TitleBarStyle::Overlay + hidden_title, Ampel in der Topbar (84px), eigener Schliessen-Knopf dort aus - Terminal-Header: Pool direkt am Projektnamen als name@pool, Chip und Pipe entfernt
This commit is contained in:
@@ -288,6 +288,7 @@ fn invoke_handlers() -> impl Fn(tauri::ipc::Invoke<tauri::Wry>) -> bool + Send +
|
||||
commands::set_apikey,
|
||||
commands::panel_archive_dir_cmd,
|
||||
commands::panel_archive_cmd,
|
||||
commands::panel_title_cmd,
|
||||
commands::set_archive_home_cmd,
|
||||
commands::change_archive_home_cmd,
|
||||
commands::clear_archive_home_cmd,
|
||||
|
||||
@@ -448,6 +448,7 @@ pub(crate) fn clear_archive_home_cmd(project: String) -> Result<(), String> {
|
||||
pub(crate) fn panel_archive_cmd(
|
||||
project: String,
|
||||
dir: Option<String>,
|
||||
title: Option<String>,
|
||||
folder: Option<String>,
|
||||
description: Option<String>,
|
||||
tags: Option<Vec<String>>,
|
||||
@@ -470,6 +471,7 @@ pub(crate) fn panel_archive_cmd(
|
||||
None => None,
|
||||
};
|
||||
let meta = ArchiveMeta {
|
||||
title: title.filter(|t| !t.trim().is_empty()),
|
||||
folder,
|
||||
description: description.filter(|d| !d.trim().is_empty()),
|
||||
tags: tags.unwrap_or_default(),
|
||||
@@ -477,6 +479,12 @@ pub(crate) fn panel_archive_cmd(
|
||||
archive_panel_content(&project, dir.as_deref(), &meta).map(|p| p.display().to_string())
|
||||
}
|
||||
|
||||
/// Titel-Vorbelegung fürs Archiv-Formular: erste Überschrift des Entwurfs.
|
||||
#[tauri::command]
|
||||
pub(crate) fn panel_title_cmd(project: String) -> String {
|
||||
crate::domain::archive::panel_title(&project)
|
||||
}
|
||||
|
||||
/// Dokumentliste des Archiv-Index (frisch gescannt) fürs Panel.
|
||||
#[tauri::command]
|
||||
pub(crate) fn archive_docs_cmd(
|
||||
|
||||
@@ -180,6 +180,8 @@ pub(crate) fn remove_archive_permission(
|
||||
/// Metadaten beim Archivieren: Unterordner im Archiv-Home plus Frontmatter-Felder.
|
||||
#[derive(Default)]
|
||||
pub(crate) struct ArchiveMeta {
|
||||
/// Titel aus dem Archiv-Formular; ohne ihn gilt die erste Überschrift.
|
||||
pub(crate) title: Option<String>,
|
||||
/// Unterordner relativ zum Archiv-Home (wird angelegt).
|
||||
pub(crate) folder: Option<String>,
|
||||
/// Einzeiler fürs Frontmatter.
|
||||
@@ -220,7 +222,7 @@ pub(crate) fn archive_panel_content(
|
||||
.map_err(|e| e.to_string())?
|
||||
.as_secs();
|
||||
let (stamp, iso) = utc_stamp(secs);
|
||||
let title = first_line(&text);
|
||||
let title = meta.title.clone().unwrap_or_else(|| first_line(&text));
|
||||
let (path, mut file) = create_unique(&dir, &stamp, &slugify(&title))?;
|
||||
// Frontmatter trägt den Anzeigenamen, nicht die Projekt-ID.
|
||||
let name = crate::domain::project::display_name_in(&Paths::real(), project)?;
|
||||
@@ -365,6 +367,12 @@ pub(crate) fn strip_stamp(stem: &str) -> &str {
|
||||
}
|
||||
|
||||
/// Titelzeile: erste Überschrift (## …) oder sonst erste nicht-leere Zeile.
|
||||
/// Titel des aktuellen Panel-Entwurfs (erste Überschrift bzw. erste Zeile) —
|
||||
/// Vorbelegung für das Titel-Feld im Archiv-Formular.
|
||||
pub(crate) fn panel_title(project: &str) -> String {
|
||||
first_line(&fs::read_to_string(panel_file(project)).unwrap_or_default())
|
||||
}
|
||||
|
||||
fn first_line(text: &str) -> String {
|
||||
let mut fallback: Option<&str> = None;
|
||||
for line in text.lines() {
|
||||
|
||||
@@ -223,6 +223,12 @@ fn tool_def(name: &str) -> Value {
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"title": {
|
||||
"type": "string",
|
||||
"description":
|
||||
"Titel des Dokuments; ohne ihn gilt die erste Überschrift \
|
||||
des Entwurfs.",
|
||||
},
|
||||
"folder": {
|
||||
"type": "string",
|
||||
"description":
|
||||
@@ -516,6 +522,7 @@ fn call_archive(req: &Value) -> Value {
|
||||
let project = std::env::var("AI_CONTROL_PROJECT").unwrap_or_default();
|
||||
let args = &req["params"]["arguments"];
|
||||
let meta = crate::domain::archive::ArchiveMeta {
|
||||
title: args["title"].as_str().map(str::to_string),
|
||||
folder: args["folder"].as_str().map(str::to_string),
|
||||
description: args["description"].as_str().map(str::to_string),
|
||||
tags: args["tags"]
|
||||
|
||||
@@ -811,6 +811,13 @@ pub async fn open_panel_window(
|
||||
None => builder.center(),
|
||||
};
|
||||
|
||||
// macOS: Titelbar als Overlay über der eigenen Kopfleiste — eine Zeile,
|
||||
// wie im Terminal-Fenster; die Topbar in panel.html ist Drag-Region.
|
||||
#[cfg(target_os = "macos")]
|
||||
let builder = builder
|
||||
.title_bar_style(tauri::TitleBarStyle::Overlay)
|
||||
.hidden_title(true);
|
||||
|
||||
// Linux/GNOME: keine GTK-Deko — eigene Kopfleiste in panel.html, wie beim
|
||||
// Terminal-Fenster.
|
||||
#[cfg(target_os = "linux")]
|
||||
|
||||
+13
-1
@@ -9,6 +9,7 @@
|
||||
import { t } from "./messages";
|
||||
|
||||
export interface ArchiveFormMeta {
|
||||
title?: string;
|
||||
folder?: string;
|
||||
description?: string;
|
||||
tags: string[];
|
||||
@@ -26,6 +27,8 @@ export interface ArchiveFolderNode {
|
||||
export interface ArchiveFormOptions {
|
||||
/// Vorhandene Archiv-Ordner (Pfad + Titel) für den Zielordner-Baum.
|
||||
folders?(): Promise<ArchiveFolderNode[]>;
|
||||
/// Titel-Vorbelegung (erste Überschrift des Entwurfs), geladen beim Öffnen.
|
||||
title?(): Promise<string>;
|
||||
/// „Auf Platte legen": Entwurf als Datei an frei gewähltem Pfad ablegen.
|
||||
onSave?(): void;
|
||||
}
|
||||
@@ -72,6 +75,8 @@ export function initArchiveForm(
|
||||
caption.textContent = t("archiveForm.title");
|
||||
box.append(caption);
|
||||
|
||||
const title = field(t("archiveForm.titleLabel"), t("archiveForm.docTitle"));
|
||||
|
||||
// --- Zielordner: durchlaufbarer Baum ------------------------------------
|
||||
// Logische Sicht des Archivs (Knotentitel plus Pfad an der Zeile), startet
|
||||
// eingeklappt. Die gewählte Zeile ist die Anzeige des Ziels — kein zweites
|
||||
@@ -231,6 +236,7 @@ export function initArchiveForm(
|
||||
document.body.append(form);
|
||||
|
||||
const meta = (): ArchiveFormMeta => ({
|
||||
title: title.value.trim() || undefined,
|
||||
folder: selected || undefined,
|
||||
description: desc.value.trim() || undefined,
|
||||
tags: tags.value
|
||||
@@ -242,6 +248,7 @@ export function initArchiveForm(
|
||||
const close = () => {
|
||||
form.hidden = true;
|
||||
selected = "";
|
||||
title.value = "";
|
||||
desc.value = "";
|
||||
tags.value = "";
|
||||
document.removeEventListener("keydown", onKey);
|
||||
@@ -254,7 +261,12 @@ export function initArchiveForm(
|
||||
const open = () => {
|
||||
form.hidden = false;
|
||||
document.addEventListener("keydown", onKey);
|
||||
desc.focus();
|
||||
title.focus();
|
||||
// Vorbelegung: erste Überschrift des Entwurfs, änderbar vor der Übernahme.
|
||||
void opts.title?.().then((v) => {
|
||||
title.value = v;
|
||||
title.select();
|
||||
});
|
||||
// Frisch laden: zwischen zwei Dialogen können Ordner entstanden sein.
|
||||
// Scheitert das Laden, steht der Grund im Kasten — ein leerer Kasten
|
||||
// ohne Erklärung war der Fehler der Vorversion.
|
||||
|
||||
@@ -227,6 +227,8 @@ const de = {
|
||||
},
|
||||
archiveForm: {
|
||||
title: "Entwurf archivieren",
|
||||
titleLabel: "Titel",
|
||||
docTitle: "Titel im Archiv",
|
||||
folderLabel: "Zielordner im Archiv",
|
||||
expand: "Auf-/zuklappen",
|
||||
descriptionLabel: "Beschreibung",
|
||||
@@ -513,6 +515,8 @@ const en: typeof de = {
|
||||
},
|
||||
archiveForm: {
|
||||
title: "Archive draft",
|
||||
titleLabel: "Title",
|
||||
docTitle: "Title in the archive",
|
||||
folderLabel: "Target folder in the archive",
|
||||
expand: "Expand/collapse",
|
||||
descriptionLabel: "Description",
|
||||
|
||||
@@ -107,6 +107,14 @@ body {
|
||||
:root[data-platform="mac"] .winbtn.osctl {
|
||||
display: none;
|
||||
}
|
||||
/* macOS: Ampel liegt als Overlay links in der Topbar — Platz lassen,
|
||||
eigenen Schließen-Knopf aus (wie im Terminal-Header). */
|
||||
:root[data-platform="mac"] .panel-topbar {
|
||||
padding-left: 84px;
|
||||
}
|
||||
:root[data-platform="mac"] .panel-topbar .winbtn.close {
|
||||
display: none;
|
||||
}
|
||||
/* Resize-Zonen für das dekorationslose Fenster (nur Linux). */
|
||||
#resize-grips {
|
||||
display: none;
|
||||
|
||||
@@ -134,6 +134,7 @@ const archiveForm = initArchiveForm(
|
||||
await invoke<string>("panel_archive_cmd", {
|
||||
project,
|
||||
dir,
|
||||
title: meta.title ?? null,
|
||||
folder: meta.folder ?? null,
|
||||
description: meta.description ?? null,
|
||||
tags: meta.tags,
|
||||
@@ -148,6 +149,7 @@ const archiveForm = initArchiveForm(
|
||||
// Ordner-Vorschläge aus dem Archiv; „Auf Platte legen" schreibt den
|
||||
// Entwurf an einen frei gewählten Pfad (ohne Archiv, ohne Frontmatter).
|
||||
folders: () => invoke("archive_folders", { project }),
|
||||
title: () => invoke("panel_title_cmd", { project }),
|
||||
onSave: async () => {
|
||||
try {
|
||||
const { save } = await import("@tauri-apps/plugin-dialog");
|
||||
|
||||
@@ -86,12 +86,15 @@ header .panel-tabs[hidden] {
|
||||
margin: 0 12px;
|
||||
align-self: center;
|
||||
}
|
||||
#pool {
|
||||
background: var(--line);
|
||||
/* Pool als schlichter Anhang: name@pool, ohne Chip; die negativen
|
||||
Ränder heben das Header-gap (10px) auf — bündig am Namen. */
|
||||
#pool-sep {
|
||||
color: var(--muted);
|
||||
border-radius: 999px;
|
||||
padding: 4px 12px;
|
||||
font-size: 11px;
|
||||
margin-left: -10px;
|
||||
}
|
||||
#pool {
|
||||
color: var(--muted);
|
||||
margin-left: -10px;
|
||||
}
|
||||
/* Eigene Fensterknöpfe (Linux): rechtsbündig, volle Header-Höhe. */
|
||||
#winbtns {
|
||||
|
||||
@@ -304,6 +304,7 @@ const archiveForm = initArchiveForm(
|
||||
await invoke<string>("panel_archive_cmd", {
|
||||
project,
|
||||
dir,
|
||||
title: meta.title ?? null,
|
||||
folder: meta.folder ?? null,
|
||||
description: meta.description ?? null,
|
||||
tags: meta.tags,
|
||||
@@ -319,6 +320,7 @@ const archiveForm = initArchiveForm(
|
||||
// Ordner-Vorschläge aus dem Archiv; „Auf Platte legen" schreibt den
|
||||
// Entwurf an einen frei gewählten Pfad (ohne Archiv, ohne Frontmatter).
|
||||
folders: () => invoke("archive_folders", { project }),
|
||||
title: () => invoke("panel_title_cmd", { project }),
|
||||
onSave: async () => {
|
||||
try {
|
||||
const { save } = await import("@tauri-apps/plugin-dialog");
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@
|
||||
<header data-tauri-drag-region>
|
||||
<img id="project-icon" hidden data-tauri-drag-region />
|
||||
<span id="project-name" data-tauri-drag-region></span>
|
||||
<span class="tab-sep" id="pool-sep"></span>
|
||||
<span id="pool-sep">@</span>
|
||||
<span id="pool"></span>
|
||||
<!-- Tabs kommen aus der Modul-Registry (panel-wiring). -->
|
||||
<div class="panel-tabs" id="panel-tabs"></div>
|
||||
|
||||
Reference in New Issue
Block a user