Skip to content

Commit b4d3bc1

Browse files
committed
update
1 parent d77ff8f commit b4d3bc1

6 files changed

Lines changed: 95 additions & 174 deletions

File tree

README.MD

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,12 @@ Planning notes: [scripts/ProjectPlan.md](scripts/ProjectPlan.md)
1414

1515
### Development mode
1616

17-
- Frontend: Next.js on `http://localhost:3000`
18-
- Backend: FastAPI on `http://127.0.0.1:8000`
19-
- Recommended start command: `./scripts/dev.sh`
17+
- Launches a real Tauri desktop window instead of a browser tab
18+
- Frontend still runs from the Next.js dev server on `http://localhost:3000`
19+
- Backend runs from the FastAPI dev server on `http://127.0.0.1:8000`
20+
- Recommended start command: `./scripts/dev-tauri.sh`
21+
- In this mode, the Tauri shell talks to the fixed dev backend on port `8000`
22+
- The packaged Python sidecar is not used in debug mode
2023

2124
### Desktop (packaged) mode
2225

@@ -36,7 +39,7 @@ MarkdownReader/
3639
├── backend/ # FastAPI app and routers
3740
├── frontend/ # Next.js + Tauri desktop shell
3841
├── markdown_reader/ # Existing Python business logic (preserved)
39-
├── scripts/dev.sh # One-command dev startup
42+
├── scripts/dev-tauri.sh # Recommended native dev startup
4043
├── tests/ # Unit/integration tests
4144
├── README_OLD.MD # Legacy tkinter-era documentation
4245
└── pyproject.toml
@@ -76,17 +79,17 @@ cp frontend/.env.local.example frontend/.env.local
7679
### 3) Start services
7780

7881
```bash
79-
./scripts/dev.sh
82+
./scripts/dev-tauri.sh
8083
```
8184

8285
Open:
8386

84-
- Frontend: http://localhost:3000
8587
- Backend docs: http://127.0.0.1:8000/docs
88+
- Tauri native app window: launched automatically by `./scripts/dev-tauri.sh`
8689

8790
### 4) Stop services
8891

89-
Press `Ctrl+C` in the terminal running `./scripts/dev.sh`.
92+
Press `Ctrl+C` in the terminal running `./scripts/dev-tauri.sh`.
9093

9194
---
9295

frontend/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"private": true,
55
"scripts": {
66
"dev": "next dev --port 3000",
7+
"tauri:dev": "tauri dev",
78
"build": "next build",
89
"start": "next start --port 3000",
910
"lint": "next lint"

frontend/src-tauri/src/lib.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,17 @@ pub fn run() {
2424
.invoke_handler(tauri::generate_handler![get_backend_port])
2525
.setup(|app| {
2626
if cfg!(debug_assertions) {
27+
*app.state::<BackendPort>().0.lock().unwrap() = Some(8000);
2728
app.handle().plugin(
2829
tauri_plugin_log::Builder::default()
2930
.level(log::LevelFilter::Info)
3031
.build(),
31-
)?
32+
)?;
33+
34+
return Ok(());
3235
}
3336

34-
// Spawn sidecar and capture its stdout to learn which port it picked.
37+
// In packaged mode, spawn sidecar and capture its stdout to learn which port it picked.
3538
let sidecar = app
3639
.shell()
3740
.sidecar("markdown-reader-backend")

frontend/src/hooks/useEditor.ts

Lines changed: 18 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -73,12 +73,22 @@ export function useEditor() {
7373
// ── content change ─────────────────────────────────────────────────────────
7474
const handleContentChange = useCallback(
7575
(value: string | undefined) => {
76-
const v = value ?? "";
77-
if (v === activeTab.content) return;
78-
updateTab(activeTabId, { content: v, dirty: true });
79-
refreshPreview(v);
76+
if (typeof value !== "string") return;
77+
const v = value;
78+
let changed = false;
79+
80+
setTabs((prev) =>
81+
prev.map((t) => {
82+
if (t.id !== activeTabId) return t;
83+
if (t.content === v) return t;
84+
changed = true;
85+
return { ...t, content: v, dirty: true };
86+
})
87+
);
88+
89+
if (changed) refreshPreview(v);
8090
},
81-
[activeTab, activeTabId, updateTab, refreshPreview]
91+
[activeTabId, refreshPreview]
8292
);
8393

8494
// ── file ops ───────────────────────────────────────────────────────────────
@@ -129,27 +139,7 @@ export function useEditor() {
129139

130140
const saveFile = useCallback(
131141
async (filePath?: string) => {
132-
let path = filePath ?? activeTab.filePath;
133-
134-
// For tabs opened without absolute path, try to resolve an existing path
135-
// without opening any dialog.
136-
if (!path) {
137-
const byName = recentFiles.filter((p) => (p.split(/[/\\]/).pop() ?? "") === activeTab.label);
138-
if (byName.length === 1) {
139-
path = byName[0];
140-
} else {
141-
const candidates = [activeTab.label, `./${activeTab.label}`];
142-
for (const candidate of candidates) {
143-
try {
144-
await Files.read(candidate);
145-
path = candidate;
146-
break;
147-
} catch {
148-
// Keep trying other candidates.
149-
}
150-
}
151-
}
152-
}
142+
const path = filePath ?? activeTab.filePath;
153143

154144
if (path) {
155145
await Files.write(path, activeTab.content);
@@ -164,40 +154,10 @@ export function useEditor() {
164154
return;
165155
}
166156

167-
// Save As flow: ask user for a destination when the tab has no path yet.
168-
const rawLabel = activeTab.label.trim() || "Untitled";
169-
const suggestedName = /\.[A-Za-z0-9]+$/.test(rawLabel) ? rawLabel : `${rawLabel}.md`;
170-
171-
// Try Tauri native save dialog first (desktop mode).
172-
try {
173-
const { save } = await import("@tauri-apps/plugin-dialog");
174-
const target = await save({
175-
defaultPath: suggestedName,
176-
filters: [{ name: "Markdown", extensions: ["md", "markdown", "txt"] }],
177-
});
178-
179-
if (!target) return;
180-
181-
const resolvedPath = Array.isArray(target) ? target[0] : target;
182-
await Files.write(resolvedPath, activeTab.content);
183-
updateTab(activeTabId, {
184-
dirty: false,
185-
filePath: resolvedPath,
186-
browserHandle: null,
187-
label: resolvedPath.split(/[/\\]/).pop() ?? resolvedPath,
188-
});
189-
Files.addRecent(resolvedPath)
190-
.then(({ entries }) => setRecentFiles(entries))
191-
.catch(console.error);
192-
return;
193-
} catch {
194-
// Not running with Tauri dialog capability.
195-
}
196-
197-
// Outside Tauri dialog capability, do nothing silently.
157+
// No explicit path available: do nothing silently.
198158
return;
199159
},
200-
[activeTab, activeTabId, recentFiles, updateTab]
160+
[activeTab, activeTabId, updateTab]
201161
);
202162

203163
const newTab = useCallback(() => {

scripts/dev-tauri.sh

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
#!/usr/bin/env bash
2+
# dev-tauri.sh — Start FastAPI backend and Tauri desktop shell in development mode.
3+
4+
set -euo pipefail
5+
6+
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
7+
PYTHON="${ROOT}/.venv/bin/python"
8+
FRONTEND="${ROOT}/frontend"
9+
BACKEND_URL="http://127.0.0.1:8000"
10+
PACKAGED_SIDECAR_PATTERN="Markdown Reader.app/Contents/MacOS/markdown-reader-backend"
11+
12+
BACKEND_PID=""
13+
14+
is_port_in_use() {
15+
local port="$1"
16+
lsof -nP -iTCP:"${port}" -sTCP:LISTEN >/dev/null 2>&1
17+
}
18+
19+
cleanup() {
20+
echo ""
21+
echo "⏹ Stopping…"
22+
if [ -n "${BACKEND_PID}" ]; then
23+
kill "${BACKEND_PID}" 2>/dev/null || true
24+
fi
25+
}
26+
27+
trap cleanup SIGINT SIGTERM EXIT
28+
29+
if pgrep -f "${PACKAGED_SIDECAR_PATTERN}" >/dev/null 2>&1; then
30+
echo "▶ Found packaged sidecar backend process; stopping it for Tauri dev mode…"
31+
pkill -f "${PACKAGED_SIDECAR_PATTERN}" || true
32+
fi
33+
34+
if is_port_in_use 8000; then
35+
if curl -fsS "${BACKEND_URL}/api/health" >/dev/null 2>&1; then
36+
echo "▶ Reusing existing FastAPI backend on ${BACKEND_URL}"
37+
else
38+
echo "ERROR: Port 8000 is already in use, but it does not look like this app's backend."
39+
echo "Please stop the process using port 8000, then run ./scripts/dev-tauri.sh again."
40+
exit 1
41+
fi
42+
else
43+
echo "▶ Starting FastAPI backend on ${BACKEND_URL}"
44+
cd "$ROOT"
45+
"$PYTHON" -m uvicorn backend.main:app --host 127.0.0.1 --port 8000 --reload &
46+
BACKEND_PID=$!
47+
echo " Backend PID: $BACKEND_PID"
48+
if ! kill -0 "$BACKEND_PID" 2>/dev/null; then
49+
echo "ERROR: Backend failed to start."
50+
exit 1
51+
fi
52+
fi
53+
54+
echo ""
55+
echo "▶ Launching Tauri desktop shell in development mode …"
56+
echo " Frontend dev server will be started by Tauri via frontend/src-tauri/tauri.conf.json"
57+
echo " Backend is fixed at ${BACKEND_URL} in debug mode"
58+
echo ""
59+
60+
cd "$FRONTEND"
61+
npx tauri dev

scripts/dev.sh

Lines changed: 0 additions & 107 deletions
This file was deleted.

0 commit comments

Comments
 (0)