Repository navigation
feat(editor): inline editing with formatting toolbar, canvas UX fixes, and dashboard cross-tab sync #126
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
peter6055
merged 23 commits into
main
from
feat/resolve-inline-editing-2-feature-improvement
Aug 7, 2026
Merged
feat(editor): inline editing with formatting toolbar, canvas UX fixes, and dashboard cross-tab sync #126
Changes from all commits
Commits
Show all changes
23 commits
Select commit
Hold shift + click to select a range
dd88264
feat: WYSIWYG inline editing with rich text support
peter6055 afa0b56
fix: remove browser-added line wraps at word-boundary characters
peter6055 731a8a3
fix: toolbar state updates and line break round-trip issues
peter6055 2e0e7e6
refactor: remove inline styling toolbar from WYSIWYG editor
peter6055 fb469b6
style: fix prettier formatting
peter6055 4bd31e4
fix(telemetry): persist usage analytics and debug reporting toggles i…
peter6055 6c5d270
remove: incorrect config and file
peter6055 c3e2b1c
feat(editor): add formatting toolbar to inline text editor with usage…
peter6055 9d27c1c
fix: match flowchart inline editor text layout to Mermaid output
peter6055 d9cbfdc
fix(editor): move inline toolbar closer and preserve alignment on for…
peter6055 07b4fa4
chore(testing): add random-port test server infra and Playwright glob…
peter6055 16b5dad
feat(dashboard): auto-load newly created diagrams across tabs
peter6055 225e42d
fix(editor): force copy cursor on diagram nodes in comment mode
peter6055 7b7a2f1
fix(editor): improve inline editor double-click handling and shape co…
peter6055 df229f7
feat(editor): vertically center inline editor text
peter6055 9358d41
fix(security): sanitize HTML before storing in diagram source
peter6055 eb4e42a
fix(security): handle unclosed HTML comments in node fallback
peter6055 6fdc68e
fix(security): address all 13 CodeRabbit review comments on PR #126
peter6055 7c3ff71
fix: address remaining CodeRabbit review comments on PR #126
peter6055 a1594b5
fix(editor): handle collapsed ranges next to element children in form…
peter6055 8b1eb75
Merge branch 'main' into feat/resolve-inline-editing-2-feature-improv…
peter6055 89498e5
style: reformat files for consistent prettier output
peter6055 176a979
fix(security): address CodeQL and Copilot review feedback on PR #126
peter6055 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -63,3 +63,6 @@ data/ | |
| .opencode/ | ||
|
|
||
| .config/ | ||
|
|
||
| # local test-server build artifacts | ||
| tmp/ | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| # Browser Automation: In-App Browser MCP vs Playwright | ||
|
|
||
| ## Decision Rule | ||
|
|
||
| - **Prefer project Playwright** (`npx playwright test`, or a short standalone script against | ||
| `npm run test:dev`) for any real UI/feature validation. It is the reliable path in this runtime. | ||
| - Use the **in-app Browser MCP** only for quick exploratory reconnaissance (a few clicks/screenshots). | ||
| - If the Browser MCP hangs or times out, **switch to Playwright immediately — do not keep retrying | ||
| the MCP session.** A wedged MCP session usually stays wedged until the browser process is killed. | ||
|
|
||
| ## Agent Responsibilities (browser testing) | ||
|
|
||
| - **Own the session lifecycle.** The agent starts, monitors, and cleans up its own browser session and | ||
| test server. Never leave orphaned Chromium processes or `livemaid-test` tmux sessions behind. | ||
| - **Never touch the user's server** (port 3434, tmux `livemaid`). Only the agent's test server | ||
| (`npm run test:dev`) may be started/stopped, and only via tmux `livemaid-test`. | ||
| - **Don't retry a wedged MCP session.** One hang is enough: switch to project Playwright immediately | ||
| instead of burning time/timeouts on further `snapshot`/`click`/`stop` calls. | ||
| - **Clean up after crashes.** Kill stale Chromium and clear `SingletonLock`/`SingletonSocket` from the | ||
| browser profile before relaunching (commands below). | ||
| - **Warm routes before navigating** with the MCP so cold-compile timeouts don't trigger hangs. | ||
| - **Use explicit, generous navigation settings** (`waitUntil: "domcontentloaded"`, 60–120s timeout) | ||
| rather than MCP defaults. | ||
| - **Verify, then close.** Close the browser context and kill the test server when validation is done | ||
| so later agents don't inherit wedged state. | ||
|
|
||
| ## Why the Browser MCP Hangs (observed in this environment) | ||
|
|
||
| 1. **Cold compile vs short navigation timeout (main trigger).** | ||
| Next.js/Turbopack's first compile of a route can take 30–40s+ (observed `GET / 200 in 37.5s`). | ||
| The Browser MCP `goto` defaults to `waitUntil: "load"` with a ~30s timeout, so navigation to a | ||
| not-yet-compiled route times out and leaves the session in a bad state. | ||
|
|
||
| 2. **Dead session after timeout/abort.** | ||
| Once a navigation times out or the user aborts mid-navigation, the CDP connection is wedged but | ||
| the MCP wrapper still treats the session as open. Subsequent `snapshot`, `click`, or `stop` calls | ||
| then hang waiting on `body`/CDP indefinitely. | ||
|
|
||
| 3. **Stale persistent-profile lock.** | ||
| `/root/.opencode/browser-profile/` can retain `SingletonLock` / `SingletonSocket` from a crashed | ||
| Chromium (e.g. a headed launch that died on missing `$DISPLAY`), blocking or flaking later launches. | ||
|
|
||
| 4. **Headed mode without an X server.** | ||
| `$DISPLAY` is empty in this runtime. Headed launch fails immediately | ||
| (`Missing X server or $DISPLAY`). `Xvfb` is installed but not managed by the MCP. Headless works. | ||
|
|
||
| ## Workarounds | ||
|
|
||
| - **Warm the route before navigating.** `curl -s -o /dev/null -w "%{http_code}" --max-time 90 http://localhost:PORT/` | ||
| forces the cold compile; then the MCP `goto` completes quickly. | ||
| - **Use relaxed navigation.** `waitUntil: "domcontentloaded"` with a 60–120s timeout instead of the | ||
| default `load`/30s. | ||
| - **Recover a wedged session:** kill stale Chromium, then clear the singleton locks before relaunch: | ||
| ```bash | ||
| pkill -f "chrome.*opencode/browser-profile" | ||
| rm -f /root/.opencode/browser-profile/SingletonLock \ | ||
| /root/.opencode/browser-profile/SingletonSocket \ | ||
| /root/.opencode/browser-profile/SingletonCookie | ||
| ``` | ||
| - **Prefer Playwright specs.** The project suite already handles cold compiles correctly: | ||
| `scripts/playwright-global-setup.mjs` waits for "Ready in", uses a random port + unique `distDir`, | ||
| and specs use generous timeouts/retries. Cold-compile flakiness on first `page.goto` still exists | ||
| (passes on retry); if it matters, add a warm-up `page.goto` before the assertions. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| import net from "node:net"; | ||
|
|
||
| const DEFAULT_MIN = 20000; | ||
| const DEFAULT_MAX = 30000; | ||
| const MAX_ATTEMPTS = 20; | ||
|
|
||
| function randomPort(min = DEFAULT_MIN, max = DEFAULT_MAX) { | ||
| return Math.floor(Math.random() * (max - min + 1)) + min; | ||
| } | ||
|
|
||
| function isPortAvailable(port) { | ||
| return new Promise((resolve) => { | ||
| const server = net.createServer(); | ||
| server.once("error", () => resolve(false)); | ||
| server.once("listening", () => server.close(() => resolve(true))); | ||
| server.listen(port, "0.0.0.0"); | ||
| }); | ||
| } | ||
|
|
||
| export async function getRandomPort(min = DEFAULT_MIN, max = DEFAULT_MAX) { | ||
| for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) { | ||
| const port = randomPort(min, max); | ||
| if (await isPortAvailable(port)) { | ||
| return port; | ||
| } | ||
| } | ||
| throw new Error( | ||
| `Could not find a free port in range ${min}-${max} after ${MAX_ATTEMPTS} attempts`, | ||
| ); | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.