diff --git a/apps/app/package.json b/apps/app/package.json index a166836..b7f8fc3 100644 --- a/apps/app/package.json +++ b/apps/app/package.json @@ -18,6 +18,7 @@ "@ace/ui": "workspace:*", "@xterm/addon-fit": "catalog:ui", "@xterm/xterm": "catalog:ui", + "agentation": "catalog:ui", "electrobun": "catalog:desktop", "react": "catalog:ui", "react-dom": "catalog:ui" diff --git a/apps/app/public/agentation-license.txt b/apps/app/public/agentation-license.txt new file mode 100644 index 0000000..4dc169a --- /dev/null +++ b/apps/app/public/agentation-license.txt @@ -0,0 +1,27 @@ +PolyForm Shield License 1.0.0 + +Copyright (c) 2026 Benji Taylor + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to use, +copy, modify, and distribute the Software, subject to the following conditions: + +1. You may not use the Software to provide a product or service that competes + with the Software or any product or service offered by the Licensor that + includes the Software. + +2. You may not remove or obscure any licensing, copyright, or other notices + included in the Software. + +3. If you distribute the Software or any derivative works, you must include a + copy of this license. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +For more information, see https://polyformproject.org/licenses/shield/1.0.0 diff --git a/apps/app/src/annotations.tsx b/apps/app/src/annotations.tsx new file mode 100644 index 0000000..b28db7a --- /dev/null +++ b/apps/app/src/annotations.tsx @@ -0,0 +1,86 @@ +import { Agentation } from "agentation"; +import { useEffect, useState } from "react"; + +const LAUNCHER = 'button[aria-label="Start feedback mode"], button[aria-label="Exit"]'; + +// Ace owns visibility; Agentation's session hide setting would disable the native shortcut. +sessionStorage.removeItem("agentation-session-toolbar-hidden"); + +export default function Annotations() { + const [container, setContainer] = useState(null); + useEffect(() => { + // Keep feedback inside the active dialog's focus and accessibility boundary. + function followDialog() { + const dialogs = document.querySelectorAll( + '[data-slot="dialog-content"][data-open]', + ); + setContainer(dialogs.item(dialogs.length - 1)); + } + const observer = new MutationObserver(followDialog); + observer.observe(document.body, { + childList: true, + subtree: true, + attributes: true, + attributeFilter: ["data-open"], + }); + followDialog(); + return () => observer.disconnect(); + }, []); + + useEffect(() => { + let shadow: ShadowRoot | undefined; + let pending = location.hash === "#annotations-toggle"; + if (pending) history.replaceState(null, "", location.pathname + location.search); + + function toggle() { + if (!shadow) { + pending = !pending; + return; + } + shadow.querySelector(LAUNCHER)!.click(); + } + + // Agentation 3.1.2 has no controlled-open API. Keep it mounted so toggling retains feedback. + const style = document.createElement("style"); + style.textContent = ` + [data-agentation-toolbar]:has(button[aria-label="Start feedback mode"][aria-expanded="false"]) { + display: none; + } + [data-agentation-settings-panel] div:has(> div > input[aria-label="Hide Until Restart"]) { + display: none; + } + `; + function attach() { + const root = document.querySelector(".ace-annotations")?.shadowRoot; + if (!root) return; + if (!root.querySelector(LAUNCHER)) { + observer.observe(root, { childList: true, subtree: true }); + return; + } + shadow = root; + root.append(style); + root.host.setAttribute("data-ace-ready", ""); + observer.disconnect(); + if (pending) toggle(); + } + const observer = new MutationObserver(attach); + observer.observe(document.body, { childList: true, subtree: true }); + window.addEventListener("ace:annotations-toggle", toggle); + attach(); + return () => { + observer.disconnect(); + window.removeEventListener("ace:annotations-toggle", toggle); + shadow?.host.removeAttribute("data-ace-ready"); + style.remove(); + }; + }, []); + + return ( + + ); +} diff --git a/apps/app/src/app.tsx b/apps/app/src/app.tsx index 56fc410..7936226 100644 --- a/apps/app/src/app.tsx +++ b/apps/app/src/app.tsx @@ -1,4 +1,4 @@ -import { useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react"; +import { lazy, Suspense, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react"; import { Button, @@ -45,6 +45,8 @@ import { Settings } from "./settings"; import { SetupProject } from "./setup-project"; import { UpdateNotice } from "./updates"; +const Annotations = lazy(() => import("./annotations")); + // Older versions saved an inline GitHub item per project; Ace now opens items on GitHub. localStorage.removeItem(`ace:github-targets:${host.url}`); @@ -692,6 +694,11 @@ export function App() { /> )} {desktop && window.dispatchEvent(new Event("ace:updates"))} />} + {desktop && ( + + + + )} diff --git a/apps/app/src/styles.css b/apps/app/src/styles.css index d2f5fc4..046165d 100644 --- a/apps/app/src/styles.css +++ b/apps/app/src/styles.css @@ -130,3 +130,7 @@ body { :root[data-layout-resize="none"] * { cursor: not-allowed !important; } + +.ace-annotations:not([data-ace-ready]) { + visibility: hidden; +} diff --git a/apps/desktop/src/index.ts b/apps/desktop/src/index.ts index 8d684d7..3be073b 100644 --- a/apps/desktop/src/index.ts +++ b/apps/desktop/src/index.ts @@ -164,6 +164,12 @@ ApplicationMenu.setApplicationMenu([ action: "channels-toggle", accelerator: "CmdOrCtrl+Shift+b", }, + { type: "divider" }, + { + label: "Toggle Annotations", + action: "annotations-toggle", + accelerator: "CmdOrCtrl+Shift+a", + }, ], }, ]); @@ -217,6 +223,7 @@ Electrobun.events.on("application-menu-clicked", ({ data }) => { "prs", "nav-toggle", "channels-toggle", + "annotations-toggle", ].includes( data.action, ) diff --git a/bun.lock b/bun.lock index 190d40f..0aa187d 100644 --- a/bun.lock +++ b/bun.lock @@ -20,6 +20,7 @@ "@ace/ui": "workspace:*", "@xterm/addon-fit": "catalog:ui", "@xterm/xterm": "catalog:ui", + "agentation": "catalog:ui", "electrobun": "catalog:desktop", "react": "catalog:ui", "react-dom": "catalog:ui", @@ -176,6 +177,7 @@ "@webgpu/types": "0.1.69", "@xterm/addon-fit": "0.11.0", "@xterm/xterm": "6.0.0", + "agentation": "3.1.2", "class-variance-authority": "0.7.1", "clsx": "2.1.1", "comark": "0.3.2", @@ -817,6 +819,8 @@ "agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="], + "agentation": ["agentation@3.1.2", "", { "peerDependencies": { "react": ">=18.0.0", "react-dom": ">=18.0.0" }, "optionalPeers": ["react", "react-dom"] }, "sha512-ZZr6PRgX3ussGbvC9NVh5vFfKKFkQyHL6KuxOfBAAM/mJS3bM2XyhZ7wLPakDwU3pqkzq7u6vEfLikFsCOu5+Q=="], + "ajv": ["ajv@8.20.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA=="], "ajv-formats": ["ajv-formats@3.0.1", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ=="], diff --git a/docs/desktop.md b/docs/desktop.md index 8d795be..007ffcc 100644 --- a/docs/desktop.md +++ b/docs/desktop.md @@ -16,6 +16,11 @@ channel in the selected project. Switching projects restores each project's sele Settings live in the account menu and Cmd+comma. Cmd+B toggles navigation; Cmd+Shift+B toggles the channel sidebar. Project paths are scoped to their host until repository-based identity lands. +Cmd+Shift+A (View → Toggle Annotations) opens or closes Agentation's feedback toolbar in the +desktop app. Its collapsed launcher stays hidden. Select an element, add a note, and copy the +feedback into a channel's composer. Saved annotations remain available when the toolbar reopens. +The desktop bundle includes Agentation's license at `Contents/Resources/app/web/agentation-license.txt`. + ## Process and installation contract - The desktop connects to Ace Helper. Closing or quitting the desktop leaves hosting available. diff --git a/package.json b/package.json index 1b13d92..4f83ded 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "@types/react": "19.2.14", "@types/react-dom": "19.2.3", "@webgpu/types": "0.1.69", + "agentation": "3.1.2", "class-variance-authority": "0.7.1", "clsx": "2.1.1", "comark": "0.3.2",