Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions src/components/ScreensPanel.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,31 @@ export function ScreensPanel({
overflow: "hidden",
}}
>
<button
onClick={() => {
if (navigator.clipboard) {
navigator.clipboard.writeText(contextMenu.screenId);
}
closeContextMenu();
}}
style={{
display: "block",
width: "100%",
padding: "9px 14px",
background: "none",
border: "none",
color: COLORS.text,
cursor: "pointer",
textAlign: "left",
fontFamily: FONTS.ui,
fontSize: 12,
}}
onMouseEnter={(e) => { e.currentTarget.style.background = COLORS.surfaceHover; }}
onMouseLeave={(e) => { e.currentTarget.style.background = "none"; }}
>
⧉ Copy Screen ID
</button>
<div style={{ borderTop: `1px solid ${COLORS.border}` }} />
<button
onClick={() => { onSetScopeRoot?.(contextMenu.screenId === scopeRoot ? null : contextMenu.screenId); closeContextMenu(); }}
style={{
Expand Down
46 changes: 44 additions & 2 deletions src/components/Sidebar.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { COLORS, FONTS, STATUS_CONFIG, STATUS_CYCLE, COMPONENT_CONFIG } from "../styles/theme";
import { useState } from "react";
import { SIDEBAR_WIDTH } from "../constants";
import { useEffect, useState } from "react";
import { COPY_FEEDBACK_MS, SIDEBAR_WIDTH } from "../constants";

export function Sidebar({ screen, screens, connections, onClose, onRename, onAddHotspot, onEditHotspot, onAddState, onSelectScreen, onUpdateStateName, onUpdateNotes, onUpdateCodeRef, onUpdateCriteria, onUpdateStatus, onUpdateTbd, onUpdateRoles, onSetComponent, isReadOnly }) {
const [draftNotes, setDraftNotes] = useState(screen.notes || "");
Expand All @@ -12,6 +12,13 @@ export function Sidebar({ screen, screens, connections, onClose, onRename, onAdd
const [tbdScreenId, setTbdScreenId] = useState(screen.id);
const [newRole, setNewRole] = useState("");
const [rolesScreenId, setRolesScreenId] = useState(screen.id);
const [idCopied, setIdCopied] = useState(false);

// Reset the "Copied!" flag when switching to a different screen so it
// never lingers from a previous screen's click.
useEffect(() => {
setIdCopied(false);
}, [screen.id]);

// Reset drafts when screen changes
if (screen.id !== notesScreenId) {
Expand Down Expand Up @@ -46,6 +53,14 @@ export function Sidebar({ screen, screens, connections, onClose, onRename, onAdd
? screens.filter((s) => s.componentId === screen.componentId && s.id !== screen.id).length
: 0;

const handleCopyId = () => {
if (!navigator.clipboard) return;
navigator.clipboard.writeText(screen.id).then(() => {
setIdCopied(true);
setTimeout(() => setIdCopied(false), COPY_FEEDBACK_MS);
});
};

return (
<div
style={{
Expand Down Expand Up @@ -116,6 +131,33 @@ export function Sidebar({ screen, screens, connections, onClose, onRename, onAdd
)}
</div>

{/* Screen ID chip — click to copy. Always visible (read-only-safe). */}
<button
onClick={handleCopyId}
title={idCopied ? "Copied!" : "Click to copy screen ID"}
style={{
display: "block",
width: "100%",
padding: "6px 12px",
marginBottom: 12,
background: COLORS.bg,
border: `1px solid ${COLORS.border}`,
borderRadius: 6,
color: idCopied ? COLORS.accentLight : COLORS.textMuted,
fontFamily: FONTS.mono,
fontSize: 11,
textAlign: "left",
cursor: "pointer",
letterSpacing: "0.02em",
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
boxSizing: "border-box",
}}
>
{idCopied ? "Copied!" : screen.id}
</button>

{/* TBD toggle */}
<div style={{ marginBottom: 12 }}>
<div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: screen.tbd ? 8 : 0 }}>
Expand Down
10 changes: 10 additions & 0 deletions src/pages/docs/userGuide.md
Original file line number Diff line number Diff line change
Expand Up @@ -873,6 +873,16 @@ When the MCP server starts, it opens a tiny HTTP listener on `localhost:3337`. T

If the user hasn't interacted with the app in the last 60 seconds, the tool returns `{ selection: null, reason: "no_recent_selection" }` so the agent knows to ask instead of acting on stale state.

### Copying screen IDs for agent prompts

When you ask an AI agent to "edit screen `s_xyz`" or "add a hotspot to `s_xyz`", you'll need that screen's ID. Drawd surfaces it in two places:

- **Sidebar** — Open a screen's inspector. The monospace ID chip under the screen name is clickable; one click copies it to your clipboard. The chip flips to `Copied!` to confirm.
- **Screens panel** — Right-click any screen row and choose `⧉ Copy Screen ID`. You don't need to select the screen first.

> [!TIP]
> Connection, hotspot, document, and data-model IDs are not yet copyable from the UI. For those, refer to the `.drawd` JSON or use the MCP server's `list_*` tools.

### Pre-loading a flow

Start the MCP server with an existing `.drawd` file pre-loaded:
Expand Down
Loading