refactor: convert to bun-workspaces monorepo (desktop/ + www/)

- Move the Tauri app (React frontend + Rust backend) into desktop/ via
  git mv — configs unchanged (relative paths: tauri.conf.json, vite.config.ts)
- Root package.json becomes a private bun workspace container with
  orchestration scripts; desktop package renamed gridline-desktop
- Scaffold www/ with Astro 7 + Tailwind v4 (hand-wired vite plugin,
  static output, ready for Dokploy)
- Update release.yml for the new layout: projectPath: desktop,
  desktop/src-tauri resource paths, rust-cache workspace path
- Update README + AGENTS.md structure trees and dev commands
- Verified: vitest (1074), cargo test (354), desktop build, tauri dev
  (window launches), tauri build (dmg + app bundles), Astro build
This commit is contained in:
2026-08-16 19:31:53 +08:00
parent d752642b4d
commit 6854d889c5
400 changed files with 857 additions and 149 deletions
@@ -0,0 +1,101 @@
import { useState } from "react";
import { AnimatedModal } from "../ui/AnimatedModal";
import { Input } from "../ui/Input";
import { Button } from "../ui/Button";
import { useQueryStore } from "../../stores/queryStore";
interface SaveQueryDialogProps {
open: boolean;
onClose: () => void;
connectionId: string;
queryText: string;
}
export function SaveQueryDialog({
open,
onClose,
connectionId,
queryText,
}: SaveQueryDialogProps) {
const [name, setName] = useState("");
const [folder, setFolder] = useState("");
const [error, setError] = useState("");
const [saving, setSaving] = useState(false);
const saveCurrentQuery = useQueryStore((s) => s.saveCurrentQuery);
const handleSave = async () => {
const trimmed = name.trim();
if (!trimmed) {
setError("Name is required");
return;
}
setSaving(true);
setError("");
try {
await saveCurrentQuery({
connectionId,
name: trimmed,
queryText,
folder: folder.trim(),
});
// Reset form on success
setName("");
setFolder("");
onClose();
} catch (e) {
setError(e instanceof Error ? e.message : "Save failed");
} finally {
setSaving(false);
}
};
const handleClose = () => {
setError("");
setName("");
setFolder("");
onClose();
};
if (!open) return null;
return (
<AnimatedModal open={open} onClose={handleClose}>
<div className="w-80">
<h3 className="font-heading text-text text-lg mb-1">Save Query</h3>
<p className="text-sm text-text-muted mb-4">Save the current query for later use.</p>
<div className="flex flex-col gap-3 mb-4">
<div>
<label className="block text-xs text-text-muted mb-1">Name</label>
<Input
value={name}
placeholder="Query name"
onChange={(v) => { setName(v); if (error) setError(""); }}
aria-label="Query name"
/>
</div>
<div>
<label className="block text-xs text-text-muted mb-1">Folder (optional)</label>
<Input
value={folder}
placeholder="Folder"
onChange={setFolder}
aria-label="Folder"
/>
</div>
</div>
{error && (
<p className="text-red-400 text-xs mb-3">{error}</p>
)}
<div className="flex justify-end gap-2">
<Button variant="ghost" onClick={handleClose}>Cancel</Button>
<Button variant="primary" onClick={handleSave} disabled={saving}>
{saving ? "Saving..." : "Save"}
</Button>
</div>
</div>
</AnimatedModal>
);
}