Skip to content
Open
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
7 changes: 7 additions & 0 deletions .Jules/palette.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
## 2026-05-05 - [Icon Preservation during Feedback]
**Learning:** Using `textContent` to update button labels (e.g., from "Copy" to "Copied!") destructively removes any child elements, including decorative SVGs.
**Action:** When updating button text for feedback, either target a specific text-only child node or use `innerHTML` while preserving the SVG's `outerHTML` (e.g., `btn.innerHTML = btn.querySelector('svg').outerHTML + ' Copied!'`).

## 2026-05-05 - [Accessibility Syncing]
**Learning:** Temporary UI status changes (like "Copied!") must be mirrored in `aria-label` to ensure screen reader users receive the update.
**Action:** Always synchronize `aria-label` with temporary text feedback, and ensure the original label is restored after the timeout.
92 changes: 46 additions & 46 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,8 @@
<aside class="sidebar" id="sidebar">
<div class="sidebar-header">
<div class="sidebar-logo">AI <span>Studio</span></div>
<button class="sidebar-collapse-btn" id="sidebar-collapse-btn" title="Collapse">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M11 19l-7-7 7-7"/><path d="M18 19l-7-7 7-7" opacity="0.4"/></svg>
<button class="sidebar-collapse-btn" id="sidebar-collapse-btn" title="Collapse sidebar" aria-label="Collapse sidebar">
<svg aria-hidden="true" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M11 19l-7-7 7-7"/><path d="M18 19l-7-7 7-7" opacity="0.4"/></svg>
</button>
</div>
<button class="new-chat-btn" id="new-chat-btn">
Expand Down Expand Up @@ -175,8 +175,8 @@

<div id="app">
<header>
<button class="hamburger-btn" id="hamburger-btn" title="Toggle sidebar">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="18" x2="21" y2="18"/></svg>
<button class="hamburger-btn" id="hamburger-btn" title="Toggle sidebar" aria-label="Toggle sidebar">
<svg aria-hidden="true" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="18" x2="21" y2="18"/></svg>
</button>
<div class="logo-dot"></div>
<h1>AI <span>Studio</span></h1>
Expand Down Expand Up @@ -232,16 +232,16 @@ <h3>Start a Conversation</h3>
</div>
<div class="input-row">
<input type="file" id="file-input" multiple accept="image/*,.pdf,.txt,.md,.csv,.js,.py,.ts,.html,.css,.json" style="display:none">
<button class="attach-btn" id="attach-btn" title="Attach file or image (images, PDFs, text)">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48"/></svg>
<button class="attach-btn" id="attach-btn" title="Attach file or image (images, PDFs, text)" aria-label="Attach file">
<svg aria-hidden="true" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48"/></svg>
</button>
<button class="mic-btn" id="voice-input-btn" title="Voice input">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"/><path d="M19 10v2a7 7 0 0 1-14 0v-2"/><line x1="12" y1="19" x2="12" y2="23"/><line x1="8" y1="23" x2="16" y2="23"/></svg>
<button class="mic-btn" id="voice-input-btn" title="Voice input" aria-label="Voice input">
<svg aria-hidden="true" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"/><path d="M19 10v2a7 7 0 0 1-14 0v-2"/><line x1="12" y1="19" x2="12" y2="23"/><line x1="8" y1="23" x2="16" y2="23"/></svg>
</button>
<textarea id="chatInput" rows="1" placeholder="Send a message…"></textarea>
<button class="send-btn" id="chatSendBtn">➤</button>
<button class="stop-btn" id="chatStopBtn" title="Stop generation">
<svg width="12" height="12" viewBox="0 0 24 24" fill="currentColor"><rect x="3" y="3" width="18" height="18" rx="2"/></svg>
<button class="send-btn" id="chatSendBtn" aria-label="Send message">➤</button>
<button class="stop-btn" id="chatStopBtn" title="Stop generation" aria-label="Stop generation">
<svg aria-hidden="true" width="12" height="12" viewBox="0 0 24 24" fill="currentColor"><rect x="3" y="3" width="18" height="18" rx="2"/></svg>
</button>
</div>
<p class="hint">Ctrl+Enter to send · Shift+Enter for newline · ? for shortcuts</p>
Expand Down Expand Up @@ -306,20 +306,20 @@ <h3>Start a Conversation</h3>
<div class="ide-topbar-actions">

<!-- File ops -->
<button class="ide-tba-btn ide-tba-save" id="ide-save-btn" title="Save (Ctrl+S)">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<button class="ide-tba-btn ide-tba-save" id="ide-save-btn" title="Save (Ctrl+S)" aria-label="Save file">
<svg aria-hidden="true" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"/>
<polyline points="17 21 17 13 7 13 7 21"/>
<polyline points="7 3 7 8 15 8"/>
</svg>
</button>
<button class="ide-tba-btn ide-tba-run" id="ide-run-btn" title="Run Code (F5)">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
<button class="ide-tba-btn ide-tba-run" id="ide-run-btn" title="Run Code (F5)" aria-label="Run code">
<svg aria-hidden="true" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
<polygon points="5 3 19 12 5 21 5 3"/>
</svg>
</button>
<button class="ide-tba-btn" id="ide-preview-btn" title="Live Preview">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<button class="ide-tba-btn" id="ide-preview-btn" title="Live Preview" aria-label="Live preview">
<svg aria-hidden="true" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/>
<polyline points="15 3 21 3 21 9"/>
<line x1="10" y1="14" x2="21" y2="3"/>
Expand All @@ -329,31 +329,31 @@ <h3>Start a Conversation</h3>
<div class="ide-tba-sep"></div>

<!-- Editor tools -->
<button class="ide-tba-btn" id="ide-format-btn" title="Format (Prettier)">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<button class="ide-tba-btn" id="ide-format-btn" title="Format (Prettier)" aria-label="Format code">
<svg aria-hidden="true" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<line x1="21" y1="10" x2="7" y2="10"/><line x1="21" y1="6" x2="3" y2="6"/>
<line x1="21" y1="14" x2="3" y2="14"/><line x1="21" y1="18" x2="7" y2="18"/>
</svg>
</button>
<button class="ide-tba-btn" id="ide-wrap-btn" title="Toggle Word Wrap">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<button class="ide-tba-btn" id="ide-wrap-btn" title="Toggle Word Wrap" aria-label="Toggle word wrap">
<svg aria-hidden="true" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polyline points="17 10 21 10 21 19"/><line x1="3" y1="6" x2="21" y2="6"/>
<path d="M21 14H9"/><polyline points="9 10 5 14 9 18"/>
</svg>
</button>
<button class="ide-tba-btn" id="ide-search-btn" title="Search in Files (Ctrl+Shift+F)">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<button class="ide-tba-btn" id="ide-search-btn" title="Search in Files (Ctrl+Shift+F)" aria-label="Search in files">
<svg aria-hidden="true" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/>
</svg>
</button>
<button class="ide-tba-btn" id="ide-fold-btn" title="Fold All">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<button class="ide-tba-btn" id="ide-fold-btn" title="Fold All" aria-label="Fold all">
<svg aria-hidden="true" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<line x1="3" y1="8" x2="21" y2="8"/><line x1="3" y1="12" x2="21" y2="12"/>
<line x1="3" y1="16" x2="21" y2="16"/>
</svg>
</button>
<button class="ide-tba-btn" id="ide-gotoline-btn" title="Go to Line (Ctrl+G)">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<button class="ide-tba-btn" id="ide-gotoline-btn" title="Go to Line (Ctrl+G)" aria-label="Go to line">
<svg aria-hidden="true" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="12" x2="15" y2="12"/>
<polyline points="10 9 7 12 10 15"/>
</svg>
Expand All @@ -362,14 +362,14 @@ <h3>Start a Conversation</h3>
<div class="ide-tba-sep"></div>

<!-- Download + Terminal -->
<button class="ide-tba-btn" id="ide-dl-btn" title="Download File">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<button class="ide-tba-btn" id="ide-dl-btn" title="Download File" aria-label="Download file">
<svg aria-hidden="true" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
<polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/>
</svg>
</button>
<button class="ide-tba-btn" id="ide-terminal-btn" title="Toggle Terminal">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<button class="ide-tba-btn" id="ide-terminal-btn" title="Toggle Terminal" aria-label="Toggle terminal">
<svg aria-hidden="true" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polyline points="4 17 10 11 4 5"/><line x1="12" y1="19" x2="20" y2="19"/>
</svg>
</button>
Expand Down Expand Up @@ -435,7 +435,7 @@ <h3>Project IDE</h3>
<div style="display:flex;gap:6px;align-items:center">
<button class="ide-hdr-btn" id="ide-output-rerun">â–¶ Rerun</button>
<button class="ide-hdr-btn" id="ide-output-clear">Clear</button>
<button class="ide-hdr-btn" id="ide-output-close">✕</button>
<button class="ide-hdr-btn" id="ide-output-close" aria-label="Close output">✕</button>
</div>
</div>
<div class="ide-output-body">
Expand All @@ -453,7 +453,7 @@ <h3>Project IDE</h3>
</svg>
Terminal
</span>
<button class="ide-hdr-btn" id="ide-terminal-close" title="Close">✕</button>
<button class="ide-hdr-btn" id="ide-terminal-close" title="Close terminal" aria-label="Close terminal">✕</button>
</div>
<div class="ide-term-body" id="ide-term-body"></div>
</div>
Expand All @@ -464,13 +464,13 @@ <h3>Project IDE</h3>
<div class="ide-ai-panel" id="ide-ai-panel">
<div class="ide-ai-hdr">
<span class="ide-ai-title">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>
<svg aria-hidden="true" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>
AI Assistant
</span>
<button class="ide-hdr-btn" id="ide-ai-clear" title="Clear chat">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-2 14H7L5 6"/></svg>
<button class="ide-hdr-btn" id="ide-ai-clear" title="Clear chat" aria-label="Clear chat">
<svg aria-hidden="true" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-2 14H7L5 6"/></svg>
</button>
<button class="ide-hdr-btn" id="ide-ai-close">✕</button>
<button class="ide-hdr-btn" id="ide-ai-close" aria-label="Close AI panel">✕</button>
</div>
<div class="ide-ai-msgs" id="ide-ai-msgs">
<div class="ide-ai-welcome-msg">
Expand Down Expand Up @@ -702,14 +702,14 @@ <h3>Generate Images</h3>
<div class="canvas-header">
<span class="canvas-title" id="canvas-title">Artifact</span>
<div class="canvas-header-actions">
<button class="canvas-action-btn" id="canvas-copy-btn" title="Copy">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
<button class="canvas-action-btn" id="canvas-copy-btn" title="Copy" aria-label="Copy code">
<svg aria-hidden="true" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
</button>
<button class="canvas-action-btn" id="canvas-run-btn" title="Run code" style="display:none">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polygon points="5 3 19 12 5 21 5 3"/></svg>
<button class="canvas-action-btn" id="canvas-run-btn" title="Run code" aria-label="Run code" style="display:none">
<svg aria-hidden="true" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polygon points="5 3 19 12 5 21 5 3"/></svg>
</button>
<button class="canvas-action-btn" id="canvas-close-btn" title="Close">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
<button class="canvas-action-btn" id="canvas-close-btn" title="Close" aria-label="Close canvas">
<svg aria-hidden="true" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
</button>
</div>
</div>
Expand Down Expand Up @@ -1185,8 +1185,8 @@ <h2>About NeuralDock</h2>
<div class="shortcuts-modal">
<div class="shortcuts-header">
<span>Keyboard Shortcuts</span>
<button class="share-modal-close" id="shortcuts-close">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
<button class="share-modal-close" id="shortcuts-close" aria-label="Close shortcuts">
<svg aria-hidden="true" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
</button>
</div>
<div class="shortcuts-grid">
Expand All @@ -1204,7 +1204,7 @@ <h2>About NeuralDock</h2>
<!-- SHARE MODAL -->
<div class="share-modal-overlay" id="share-modal-overlay">
<div class="share-modal">
<div class="share-modal-title">Share Chat<button class="share-modal-close" id="share-modal-close"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg></button></div>
<div class="share-modal-title">Share Chat<button class="share-modal-close" id="share-modal-close" aria-label="Close share modal"><svg aria-hidden="true" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg></button></div>
<div class="share-modal-preview" id="share-preview"></div>
<div class="share-modal-actions">
<button class="share-btn primary" id="share-copy-btn"><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>Copy to Clipboard</button>
Expand Down
17 changes: 14 additions & 3 deletions js/chat.js
Original file line number Diff line number Diff line change
Expand Up @@ -384,10 +384,21 @@ function buildMsgActions(wrap, body, msg) {
const actions = document.createElement('div');
actions.className = 'msg-actions';

actions.appendChild(makeActionBtn('Copy', e => {
actions.appendChild(makeActionBtn('Copy', function(e) {
e.stopPropagation();
navigator.clipboard.writeText(body.innerText || body.textContent);
toast('Copied');
const btn = this;
navigator.clipboard.writeText(body.innerText || body.textContent).then(() => {
toast('Copied');
const oldText = btn.textContent;
const oldAria = btn.getAttribute('aria-label');
btn.textContent = 'Copied';
btn.setAttribute('aria-label', 'Copied');
setTimeout(() => {
btn.textContent = oldText;
if (oldAria) btn.setAttribute('aria-label', oldAria);
else btn.removeAttribute('aria-label');
}, 2000);
});
}));
actions.appendChild(makeActionBtn('↺ Rewrite', e => {
e.stopPropagation();
Expand Down
Loading