@hazem3500/komo 0.0.0-stage → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/NODE.md +80 -0
- package/NOTICE.md +45 -0
- package/README.md +411 -2
- package/cli/agent.mjs +615 -0
- package/cli/config.mjs +34 -0
- package/cli/index.mjs +405 -0
- package/cli/project.mjs +136 -0
- package/cli/workflow.mjs +42 -0
- package/dist/MorphingMenu.d.ts +30 -0
- package/dist/PointerIcon.d.ts +2 -0
- package/dist/SmileIcon.d.ts +2 -0
- package/dist/THIRD_PARTY_NOTICES.txt +387 -0
- package/dist/accent-picker-F5UI2NP7.js +1 -0
- package/dist/accent-picker.d.ts +4 -0
- package/dist/accent.d.ts +2 -0
- package/dist/account-usage.d.ts +2 -0
- package/dist/action-menu.d.ts +10 -0
- package/dist/agent-prompt.d.ts +6 -0
- package/dist/agent-prompt.js +1 -0
- package/dist/anchors.d.ts +48 -0
- package/dist/api.d.ts +39 -0
- package/dist/approved-sites.d.ts +9 -0
- package/dist/card-motion.d.ts +8 -0
- package/dist/chunk-7YUXYVQA.js +1 -0
- package/dist/chunk-I7AUKTXE.js +1 -0
- package/dist/chunk-JQVBZ2BB.js +1 -0
- package/dist/chunk-Q7WSWLMV.js +1 -0
- package/dist/chunk-Y2GRLW4Q.js +1 -0
- package/dist/chunk-YZP3KX4Y.js +1 -0
- package/dist/chunk-Z2ABBESO.js +1 -0
- package/dist/config.d.ts +11 -0
- package/dist/connect-project.d.ts +14 -0
- package/dist/connection-issue.d.ts +7 -0
- package/dist/design-comment-QPTW66JO.js +1 -0
- package/dist/design-comment.d.ts +42 -0
- package/dist/design-export-BRBI7S3V.js +1 -0
- package/dist/design-export.d.ts +8 -0
- package/dist/design-mode-styles.d.ts +1 -0
- package/dist/design-mode.d.ts +19 -0
- package/dist/design-mode.js +1 -0
- package/dist/design-panel-BPVXFERR.js +1 -0
- package/dist/design-panel-styles.d.ts +1 -0
- package/dist/design-panel.d.ts +33 -0
- package/dist/design-tokens.d.ts +76 -0
- package/dist/dom.d.ts +20 -0
- package/dist/drawer-expand-motion.d.ts +46 -0
- package/dist/drawer-presence.d.ts +4 -0
- package/dist/edge-sidebar.d.ts +39 -0
- package/dist/emoji-history.d.ts +8 -0
- package/dist/emoji.d.ts +1 -0
- package/dist/floating-drag.d.ts +9 -0
- package/dist/icon-markup.d.ts +4 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +1 -0
- package/dist/inline-text-editor-F7B6FQQ3.js +1 -0
- package/dist/inline-text-editor.d.ts +70 -0
- package/dist/lazy-accent-picker.d.ts +4 -0
- package/dist/lazy-toolbar.d.ts +19 -0
- package/dist/message-links.d.ts +2 -0
- package/dist/morphing-menu-styles.d.ts +1 -0
- package/dist/node.mjs +2607 -0
- package/dist/onboarding-NO3RQWR7.js +1 -0
- package/dist/onboarding.d.ts +6 -0
- package/dist/optimistic.d.ts +26 -0
- package/dist/page.d.ts +2 -0
- package/dist/picker-WQN53YJ5.js +1 -0
- package/dist/pin-direction.d.ts +12 -0
- package/dist/pin-stacks.d.ts +3 -0
- package/dist/polling.d.ts +10 -0
- package/dist/postgres/001_initial.sql +128 -0
- package/dist/postgres/002_thread_tags.sql +1 -0
- package/dist/postgres/003_project_webhooks.sql +4 -0
- package/dist/project-management.d.ts +2 -0
- package/dist/react-inspector.d.ts +43 -0
- package/dist/react-props-panel-XSMDR2CY.js +1 -0
- package/dist/react-props-panel.d.ts +39 -0
- package/dist/react.d.ts +38 -0
- package/dist/react.js +1 -0
- package/dist/reaction-picker.d.ts +1 -0
- package/dist/review-layout.d.ts +23 -0
- package/dist/setup-browser.d.ts +1 -0
- package/dist/setup.d.ts +4 -0
- package/dist/setup.js +1 -0
- package/dist/shortcuts.d.ts +15 -0
- package/dist/static-svg.d.ts +3 -0
- package/dist/styles.d.ts +1 -0
- package/dist/tag-editor-VDPZ6CO3.js +1 -0
- package/dist/tag-editor.d.ts +12 -0
- package/dist/toolbar-runtime-2SIUE6SQ.js +1 -0
- package/dist/toolbar-runtime.d.ts +5 -0
- package/dist/types.d.ts +163 -0
- package/package.json +114 -4
- package/server/digest.ts +8 -0
- package/server/import-project.ts +164 -0
- package/server/index.ts +1656 -0
- package/server/migrations/0001_comments.sql +54 -0
- package/server/migrations/0002_identity.sql +2 -0
- package/server/migrations/0003_accent_color.sql +1 -0
- package/server/migrations/0004_workspaces.sql +46 -0
- package/server/migrations/0005_domains.sql +6 -0
- package/server/migrations/0006_member_budget.sql +16 -0
- package/server/migrations/0007_revisions.sql +17 -0
- package/server/migrations/0008_starter_comments.sql +1 -0
- package/server/migrations/0009_retention.sql +19 -0
- package/server/migrations/0010_reviewer_history.sql +1 -0
- package/server/migrations/0011_project_management.sql +52 -0
- package/server/migrations/0012_setup_sites.sql +3 -0
- package/server/migrations/0013_project_sites.sql +8 -0
- package/server/migrations/0014_project_site_removals.sql +3 -0
- package/server/migrations/0015_query_efficiency.sql +6 -0
- package/server/migrations/0016_thread_tags.sql +1 -0
- package/server/migrations/0017_project_webhooks.sql +4 -0
- package/server/node-webhooks.ts +126 -0
- package/server/project-management.ts +387 -0
- package/server/project-sites.ts +117 -0
- package/server/secrets.d.ts +24 -0
- package/server/setup-client.txt +2932 -0
- package/server/setup-page.ts +14 -0
- package/server/tsconfig.json +12 -0
- package/server/validation.ts +220 -0
- package/server/webhooks.ts +206 -0
- package/server/workspaces.ts +168 -0
- package/src/emoji.ts +7 -0
- package/src/page.ts +4 -0
- package/src/types.ts +166 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Lué Studio
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/NODE.md
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Run the komo API in a Node container
|
|
2
|
+
|
|
3
|
+
The browser widget and CLI use the same API protocol on Cloudflare and Node. This option uses PostgreSQL and does not change an existing Worker/D1 deployment. It starts one API service for your preview sites; each project key maps to one repository and an allowlist of site origins.
|
|
4
|
+
|
|
5
|
+
## Configure
|
|
6
|
+
|
|
7
|
+
1. In your website repository, install komo and generate the Node project:
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npm install @hazem3500/komo
|
|
11
|
+
npx komo init --node \
|
|
12
|
+
--endpoint https://comments.example.com \
|
|
13
|
+
--origin https://pr-42.preview.example.com \
|
|
14
|
+
--preview-origin 'https://pr-*.preview.example.com' \
|
|
15
|
+
--repo your-org/your-repo \
|
|
16
|
+
--branch-scope \
|
|
17
|
+
--google-client-id YOUR_GOOGLE_CLIENT_ID
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
This writes `.komo/node.env` and `.komo/owner-key`, adds them to `.gitignore`, and configures the existing widget and agent CLI. The preview wildcard matches one hostname label under a domain you control. The endpoint is the **public API origin**, not a preview URL. For local development it may use HTTP localhost. Keep the generated owner key private.
|
|
21
|
+
|
|
22
|
+
2. Provide a PostgreSQL database with a dedicated user allowed to create tables and triggers. Replace the `DATABASE_URL` and `GOOGLE_CLIENT_SECRET` placeholders in `.komo/node.env`. Add the printed `https://comments.example.com/auth/google/callback` URL to the Google OAuth client's authorized redirect URIs. In Cloud Run, supply the same variables through service configuration and a secret store. Do not commit the environment file. The Node runtime does not enable hosted provisioning (`KOMO_HOSTED`).
|
|
23
|
+
|
|
24
|
+
To enable signed webhooks, generate a 32-byte encryption key with `openssl rand -base64 32` and provide `KOMO_WEBHOOK_KEYS` through your secret manager as `{"current":"key-id","keys":{"key-id":"BASE64_KEY"}}`. Never store the key in source control or expose it in logs. Webhook endpoints must use HTTPS on port 443; the Node sender rejects private or mixed DNS answers and pins the vetted public address for the request.
|
|
25
|
+
|
|
26
|
+
3. Build this repository's image with `docker build -f Dockerfile.node -t komo-node .`. Run it with the generated variables and HTTPS in front. For a local check, set `PUBLIC_URL=http://localhost:8080` in `.komo/node.env` and run `docker run --env-file /path/to/your-website/.komo/node.env -p 8080:8080 komo-node`. For Cloud Run, set the container port to 8080. Cloud SQL or another reachable PostgreSQL service can be used through `DATABASE_URL`; the database connection URL must be reachable from the container. Startup applies the bundled schema under a database lock. `/health` returns `{"ok":true}` after migrations succeed.
|
|
27
|
+
|
|
28
|
+
4. Open the owner-claim URL printed by `komo init --node` and sign in with Google. The setup page claims ownership once; the owner can then manage approved sites and private access. The generated widget config contains your project key. Use the same project key and branch in the bot CLI.
|
|
29
|
+
|
|
30
|
+
For a guest-only project, you can skip the generator and set `DATABASE_URL`, `PUBLIC_URL`, and `PROJECTS` directly. For example, `PROJECTS={"review":{"repo":"your-org/your-repo","origins":["https://pr-*.preview.example.com"],"allowGuests":true}}`. Anyone on an approved site can then comment; do not use guest-only mode for confidential reviews. `PROJECTS` must remain on one line in an env file. Google sign-in requires both `GOOGLE_CLIENT_ID` and `GOOGLE_CLIENT_SECRET`.
|
|
31
|
+
|
|
32
|
+
### Serve under a path
|
|
33
|
+
|
|
34
|
+
If a reverse proxy serves komo under a path, include it in `PUBLIC_URL`, for example `PUBLIC_URL=https://labs.example.com/komo`. Sign-in links and redirect URLs then include `/komo`. Requests are accepted whether or not the proxy strips the path. Register OAuth redirect URIs with the path, for example `https://labs.example.com/komo/auth/google/callback`.
|
|
35
|
+
|
|
36
|
+
### Google Cloud Identity-Aware Proxy (IAP) sign-in
|
|
37
|
+
|
|
38
|
+
Behind a Google Cloud load balancer with IAP, komo can trust IAP's signed identity instead of running its own Google OAuth client. This is off by default. To turn it on:
|
|
39
|
+
|
|
40
|
+
- **`KOMO_IAP_AUDIENCE`:** set it to the IAP backend audience, `/projects/PROJECT_NUMBER/global/backendServices/SERVICE_ID`. komo then verifies the `x-goog-iap-jwt-assertion` header against Google's IAP signing keys, checking the issuer, audience and expiry. It mints a session for that Google account, so `GOOGLE_CLIENT_ID` and `GOOGLE_CLIENT_SECRET` aren't needed.
|
|
41
|
+
- **`KOMO_AUTH_PREFIX`** (optional, for example `/backoffice`): serves the sign-in handlers only under this prefix, so the load balancer can protect just that path with IAP. The handlers are `PREFIX/auth/google/authorize` (the browser popup) and `POST PREFIX/auth/iap/session?project=KEY` (agents exchange their IAP identity for a project session). The same handlers without the prefix return 404.
|
|
42
|
+
|
|
43
|
+
While IAP is on:
|
|
44
|
+
|
|
45
|
+
- Google is the only sign-in method. Guest and GitHub sign-in are refused.
|
|
46
|
+
- Reading or writing comments requires a session from an IAP-verified account.
|
|
47
|
+
- Missing, forged, expired or wrong-audience assertions are rejected.
|
|
48
|
+
- The rest of the API stays outside IAP: the widget calls it cross-origin with the bearer session from the popup.
|
|
49
|
+
|
|
50
|
+
Make sure clients can't reach the prefixed handlers without going through IAP. The signed assertion check rejects forged headers either way.
|
|
51
|
+
|
|
52
|
+
## Connect a preview and bot
|
|
53
|
+
|
|
54
|
+
Use the existing widget options; only its API endpoint changes:
|
|
55
|
+
|
|
56
|
+
```js
|
|
57
|
+
initKomo({
|
|
58
|
+
endpoint: "https://comments.example.com",
|
|
59
|
+
project: "YOUR_GENERATED_PROJECT_KEY",
|
|
60
|
+
repo: "your-org/your-repo",
|
|
61
|
+
scope: "branch",
|
|
62
|
+
branch: "pr-42",
|
|
63
|
+
});
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Point the existing CLI at the same endpoint, project, repository, and branch. `komo login` uses Google when configured; `KOMO_TOKEN` accepts a previously issued session token for a non-interactive bot. Store that token as a secret. It has the same permissions as its user. For example:
|
|
67
|
+
|
|
68
|
+
```sh
|
|
69
|
+
komo comments list --endpoint https://comments.example.com --project YOUR_GENERATED_PROJECT_KEY --repo your-org/your-repo --branch pr-42 --origin https://pr-42.preview.example.com
|
|
70
|
+
komo comments reply THREAD_ID --body "Fixed and verified" --endpoint https://comments.example.com --project YOUR_GENERATED_PROJECT_KEY --repo your-org/your-repo --branch pr-42 --origin https://pr-42.preview.example.com
|
|
71
|
+
komo comments resolve THREAD_ID --endpoint https://comments.example.com --project YOUR_GENERATED_PROJECT_KEY --repo your-org/your-repo --branch pr-42 --origin https://pr-42.preview.example.com
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
The `--origin` value must be one approved preview origin. Each PR needs the same branch value in widget and bot to isolate its feedback. On the same project, an owner can edit approved sites after Google sign-in. Wildcard origins match one hostname label only, so `pr-*.preview.example.com` does not allow arbitrary subdomains of `example.com`.
|
|
75
|
+
|
|
76
|
+
## Database boundary
|
|
77
|
+
|
|
78
|
+
The API's storage contract is `prepare().bind().first()/all()/run()` plus atomic `batch()`. `server/postgres.ts` implements it with a PostgreSQL pool, parameterized statements, and one transaction per batch. `server/postgres/001_initial.sql` mirrors the current SQLite schema and its quota, revision, retention, and deletion triggers. Add a numbered PostgreSQL migration whenever the D1 schema changes. Existing D1 migrations and the Worker entry point stay separate. The Node server runs the same request handler; transport and storage are the only new adapters.
|
|
79
|
+
|
|
80
|
+
The Node process runs retention cleanup after requests, as the self-hosted Worker does. It handles `SIGTERM` by closing its HTTP listener and database pool. Database connection failures prevent startup; request errors return the existing API error format. Rate limits use the direct socket IP by default. Behind a trusted reverse proxy, set `KOMO_PROXY_HOPS` to the number of proxy-added `X-Forwarded-For` entries before the app; check your load balancer’s forwarding behavior for the correct value. Only enable this when requests cannot bypass that proxy. Client-supplied `CF-Connecting-IP` is ignored.
|
package/NOTICE.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Morphing menu source
|
|
2
|
+
|
|
3
|
+
`src/MorphingMenu.tsx` (compiled into the shared `dist` chunks) and `src/morphing-menu-styles.ts` use the copyable component and CSS published by Danny Williams at https://dannyjpwilliams.com/playground/morphing-menu/, retrieved 2026-09-15. The page explicitly instructs users to copy these files and customize the actions. Credit for that component remains with Danny Williams.
|
|
4
|
+
|
|
5
|
+
Local changes: CSS injection into Shadow DOM, composed-path outside-click detection, review-specific actions and appearance.
|
|
6
|
+
|
|
7
|
+
## Icons
|
|
8
|
+
|
|
9
|
+
Review icons use the official `@untitledui/icons` package from https://github.com/untitleduico/icons. The dependency supplies the original SVG artwork. No icon artwork is copied into this package.
|
|
10
|
+
|
|
11
|
+
## Interaction reference
|
|
12
|
+
|
|
13
|
+
Component highlighting, compact growing composers, pin previews, and target-aware feedback copying were informed by Mesurer (https://github.com/ibelick/mesurer), by Julien Thibeaut, reviewed at version 0.1.5. These interactions are implemented here against the shared comments API and use Untitled UI icons.
|
|
14
|
+
|
|
15
|
+
## Floating drag and edge snapping
|
|
16
|
+
|
|
17
|
+
`src/floating-drag.ts` and `resizeEdgeBox` adapt the gesture geometry, velocity smoothing, edge thresholds, and edge resize from https://github.com/tjcages/panels (`src/hooks/use-drag-resize.ts`).
|
|
18
|
+
|
|
19
|
+
The edge sidebar (`sidebar: "edge"`) reuses that drag math and follows the panels floating-panel pattern: a panel that docks to a viewport edge, parks off-screen while closed, and peeks out at the edge while collapsed (`src/edge-sidebar.ts` and the `--edge-park-x` / `--edge-peek-x` offsets). The open animation is the drawer expand: the same compress-then-spring, with the comment rows staggering in. The drawer stays at the bottom of the sidebar as tabs. The account dialog sits inside that panel, inset from the sides and top, with no scrim. Background mode keeps the viewport account column and overlay.
|
|
20
|
+
|
|
21
|
+
MIT License
|
|
22
|
+
|
|
23
|
+
Copyright (c) 2026 tjcages
|
|
24
|
+
|
|
25
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
26
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
27
|
+
in the Software without restriction, including without limitation the rights
|
|
28
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
29
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
30
|
+
furnished to do so, subject to the following conditions:
|
|
31
|
+
|
|
32
|
+
The above copyright notice and this permission notice shall be included in all
|
|
33
|
+
copies or substantial portions of the Software.
|
|
34
|
+
|
|
35
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
36
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
37
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
38
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
39
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
40
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
41
|
+
SOFTWARE.
|
|
42
|
+
|
|
43
|
+
## Bundled dependencies
|
|
44
|
+
|
|
45
|
+
The production build includes selected Motion and picker modules. Their original licenses are collected in `dist/THIRD_PARTY_NOTICES.txt`. React and the official icon package remain external dependencies.
|
package/README.md
CHANGED
|
@@ -1,3 +1,412 @@
|
|
|
1
|
-
#
|
|
1
|
+
# komo
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/@hazem3500/komo)
|
|
4
|
+
[](https://www.npmjs.com/package/@hazem3500/komo)
|
|
5
|
+
|
|
6
|
+
> This is a maintained fork of [tjcages/komo](https://github.com/tjcages/komo), published as `@hazem3500/komo`. Install it with `npm install @hazem3500/komo`.
|
|
7
|
+
|
|
8
|
+
Comments for your website. Point at an element, mark an area, and leave feedback your team can reply to, resolve, or copy into a coding agent.
|
|
9
|
+
|
|
10
|
+
Framework independent. Shared persistence. Google identities and guest reviewers.
|
|
11
|
+
|
|
12
|
+
[Website](https://komo.offbr.co) · [Documentation](https://komo.offbr.co/install/) · [Playground](https://komo.offbr.co/playground/)
|
|
13
|
+
|
|
14
|
+
Review actions update immediately while saving in the background. Failed writes roll back with a notice; unsaved comment text is retained.
|
|
15
|
+
|
|
16
|
+
## Install
|
|
17
|
+
|
|
18
|
+
```sh
|
|
19
|
+
npm install @hazem3500/komo
|
|
20
|
+
npx @hazem3500/komo init
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Requires Node.js 22 or newer. The unscoped `komo` package on npm is unrelated; use `@hazem3500/komo`.
|
|
24
|
+
|
|
25
|
+
The setup command detects your Git repository and generates `komo.config.js` before sign-in. Mount that helper in your app:
|
|
26
|
+
|
|
27
|
+
```js
|
|
28
|
+
import { initKomo } from './komo.config.js';
|
|
29
|
+
initKomo();
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Keep the setup terminal open and start your app in another terminal. Choose **Connect komo** in your app’s sidebar. Google sign-in opens in a separate window; komo creates the project and returns you to feedback on your site. The CLI saves the final public settings to `.komo/project.json`, updates the helper, and prints an inline configuration. Use `--origin` for a different dev port or deployed site. If interrupted or expired, run `komo init` again to resume; completed projects are never overwritten.
|
|
33
|
+
|
|
34
|
+
Hosted onboarding is available through the dedicated komo service. Self-hosting is available through the same CLI.
|
|
35
|
+
|
|
36
|
+
## Usage
|
|
37
|
+
|
|
38
|
+
Mount after the page has loaded:
|
|
39
|
+
|
|
40
|
+
```js
|
|
41
|
+
import { initKomo } from '@hazem3500/komo';
|
|
42
|
+
initKomo({ project: 'YOUR_PROJECT_KEY' });
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Optional configuration stays at the call site:
|
|
46
|
+
|
|
47
|
+
```js
|
|
48
|
+
initKomo({
|
|
49
|
+
project: 'YOUR_PROJECT_KEY',
|
|
50
|
+
enabled: import.meta.env.DEV || import.meta.env.PUBLIC_PREVIEW === 'true',
|
|
51
|
+
pageRoot: document.querySelector('#app'),
|
|
52
|
+
});
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### React
|
|
56
|
+
|
|
57
|
+
With React 18.2 or 19, call the hook once near your app root:
|
|
58
|
+
|
|
59
|
+
```tsx
|
|
60
|
+
'use client'; // Required for Next.js App Router client components.
|
|
61
|
+
import { useKomo } from '@hazem3500/komo/react';
|
|
62
|
+
|
|
63
|
+
export function Komo() {
|
|
64
|
+
useKomo({ project: 'YOUR_PROJECT_KEY' });
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Render `<Komo />` in your app or layout. The hook handles mounting, cleanup, and React Strict Mode. Inline configuration objects are supported; unchanged values do not restart komo. Changing configuration remounts it, and `enabled: false` removes it. Memoize callback options with `useCallback` and pass stable DOM elements for `pageRoot` or `drawerContainer`. Mount only one hook per page; do not combine it with a separate `initKomo()` call. Server rendering does not mount the tool.
|
|
70
|
+
|
|
71
|
+
The **endpoint is the comments API**, not your website or preview URL. Hosted komo defaults to `https://komo.offbr.co`. Google sign-in returns to `https://komo.offbr.co/auth/google/callback`, regardless of the website embedding komo. For self-hosting, pass your Worker or Node API URL as `endpoint`. Your current page comes from the browser. Setup detects repository metadata from Git; include its printed `repo` value to enrich agent prompts.
|
|
72
|
+
|
|
73
|
+
Comments are shared across deployments by default. Use `pnpm exec komo init --branch-scope` to separate them by branch. For automatic branch detection, import from the optional generated `komo.config.js` helper and run `komo sync` before builds. It detects the current branch from deployment environment variables or Git; set `KOMO_BRANCH` if neither is available. It fails rather than silently grouping unknown branches.
|
|
74
|
+
|
|
75
|
+
## Features
|
|
76
|
+
|
|
77
|
+
- Point and area comments anchored to page elements.
|
|
78
|
+
- On-demand design mode for temporary typography, color, spacing, border, and layout previews; copy the resulting CSS or attach a comment to the selected element.
|
|
79
|
+
- Replies, reactions, author-only editing, resolution, and undo.
|
|
80
|
+
- Group tags: add them while writing a comment or on an open thread, with suggestions from tags already in the project.
|
|
81
|
+
- Safe HTTP(S) links and simple Markdown links in comment messages.
|
|
82
|
+
- Searchable sidebar, draggable dock, and Google or guest profiles.
|
|
83
|
+
- Dismiss the toolbar for the current tab with ×; restore it with Shift+K or `controller.open()`. Set `showToolbar: false` to start hidden.
|
|
84
|
+
- Keyboard shortcuts: C to comment, V to browse, D to toggle Design mode, Shift+K to restore. Remap or disable them with `shortcuts`.
|
|
85
|
+
- Copy open feedback with selectors, source paths, page URLs, replies, and geometry into an agent.
|
|
86
|
+
- Hosted Cloudflare storage, or your own Worker/D1 or Node/PostgreSQL service.
|
|
87
|
+
|
|
88
|
+
Design mode edits the rendered page only. Preview styles and inline text edits are temporary and reset when the page refreshes; styles are restored when the Komo widget is destroyed. Use **Copy CSS** to apply a rule in your site stylesheet; Komo does not write source files. Double-click text, or use **Edit text**, to edit it inline. Edits accept plain text only and change text in place: nested markup, links, and buttons keep their structure. Text marked `data-private` or `data-sensitive`, form fields, and text inside graphics, media, or frames stay read-only. An explicitly registered React `textProp` routes edits through the app's state callback. The inspector can select an iframe as one element but cannot edit its isolated contents. Elements inside open or closed shadow roots are not traversed; select the component host instead.
|
|
89
|
+
|
|
90
|
+
The design inspector loads as a separate optional browser chunk when Design mode is first opened, so the default widget load does not pay its bundle cost. The package size check measures this chunk separately from the core bundle.
|
|
91
|
+
|
|
92
|
+
The inspector takes direct UX/UI inspiration from [DialKit](https://www.dialkit.dev/): a compact dark floating panel, collapsible Color/Typography/Layout/Border groups, horizontal live sliders with editable numeric values, and color swatches paired with CSS color inputs. Group collapse state survives selection and Undo/Reset. Each slider gesture is one undoable edit; numeric fields remain available for precise keyboard entry. No DialKit runtime dependency is added.
|
|
93
|
+
|
|
94
|
+
Design mode also takes interaction cues from established design tools: select a visible element, keep its controls contextual, and make each change easy to reverse. Figma places editable layer properties and file-local styles or variables beside the selected layer; its Dev Mode provides inspection and measurements. pen.dev pairs an IDE design-mode toggle with a canvas, layer list, contextual property panel, and undo/redo. OpenPencil synchronizes layer-tree and canvas selection and offers contextual typography/layout controls, variables, and undoable operations.
|
|
95
|
+
|
|
96
|
+
### Opt-in React prop inspection
|
|
97
|
+
|
|
98
|
+
React teams can register selected component roots for typed prop previews through the `./react` entry. A build-time TypeScript Compiler API pass generates the prop manifest from an explicitly marked function component and props type; the inspector edits only strings, numbers, booleans, and literal unions. Functions, `ReactNode`, objects, generics, nullable unions, and any unsupported type remain read-only. This metadata is a safe allowlist, not runtime discovery of every React component; `defaultProps` and every React component form are not inferred.
|
|
99
|
+
|
|
100
|
+
The app provides an `onChange` callback that updates its own state. Komo previews those values through React's normal render cycle, offers undo/reset, and can prefill an explicit comment with the proposed before/after values and source path. Posting still requires the user to submit the comment. Komo never accesses React Fiber internals or writes source files. Preview prop changes reset on refresh. A component may opt one leaf render into direct text editing by setting `textProp` to the name of an allowed string prop; Komo does not infer which child text belongs to a prop. See [`examples/react-props`](./examples/react-props/README.md) for the generated fixture and limitations.
|
|
101
|
+
|
|
102
|
+
### Opt-in design tokens
|
|
103
|
+
|
|
104
|
+
`useKomoDesignTokens` can register a bounded list of named CSS custom properties from a host-owned source. Each entry supplies its token path, DTCG scalar type, CSS variable, eligible element properties, and an explicit selector for the estimated theme-preview scope. Komo does not scan arbitrary computed styles or infer a complete semantic system. The first adapter uses the Design Tokens Format Module 2025.10 naming/type vocabulary; it is not a general DTCG file importer, and composite typography tokens are not editable.
|
|
105
|
+
|
|
106
|
+
Compatible registered tokens appear inside each existing style control, next to the color, spacing, typography, or radius value they affect. The chooser filters by that control's CSS property; it never guesses a token by name, matches computed values, or snaps arbitrary values to a scale. A control reads **Connected** only when the element's winning declaration (inline, or a same-origin stylesheet rule ranked by `!important`, specificity, and order) references the registered `var(--token)`. Typing a value, or choosing **Custom value**, records a custom override that keeps the original token as provenance; choosing another token rebinds the preview, and choosing the original token again removes the override. The comment proposal lists each change once, with its token before and after. Undo and Reset restore the original declaration and token provenance. Cross-origin stylesheets are skipped (their properties read as not connected), and cascade layers are ranked as if unlayered. For registered React components, a build-generated string prop can opt into one allowlisted `styleProperty` mapping (for example `brandColor` → `color`); the token reference then goes through the component's supplied React state callback and stays in the proposed prop diff. Unsupported prop types remain read-only.
|
|
107
|
+
|
|
108
|
+
Shared theme edits remain secondary under **Edit shared theme values**. Their explicit Preview action changes the registered variable temporarily and reports the author-declared impact selector/count. This is not a persisted design-system edit; it resets on refresh, widget destruction, or Reset. Selectors are estimates, not proof that every matching element consumes the variable. Both element and shared changes remain previews until the user explicitly submits a comment; Komo does not write CSS or React source. Enable registration only in a trusted development/demo environment, and exclude confidential values from the token list.
|
|
109
|
+
|
|
110
|
+
This approach uses React Developer Tools as a reference for component props but avoids depending on its browser DevTools UI. TypeScript's compiler can generate type metadata before type erasure, while a host callback keeps live previews within the app's public state model. React 19 is exercised by the fixture; the adapter uses public hooks and DOM refs and does not inspect version-specific React internals.
|
|
111
|
+
|
|
112
|
+
Puck is a useful reference for the **explicit component contract**: its config registers renderable components and declares typed editor fields, defaults, nested object/array fields, and global/component/per-instance edit permissions. Komo borrows the opt-in registration, typed allowlist, contextual controls, and fail-closed edit permissions. It does not reuse Puck's editor/runtime: Puck owns a structured page document and supports component insertion, nesting, drag-and-drop, and publishing, while Komo edits a temporary preview of the host app's existing React state and turns proposed values into a comment. A Puck dependency would add a full page-builder interaction/data model and runtime cost where Komo needs a small optional inspector; Puck's MIT license permits reuse, but licensing is not the deciding constraint. For the first version, nested object/array, slot, custom, and dynamic fields remain read-only until the adapter can represent and validate them safely without implying broader source persistence.
|
|
113
|
+
|
|
114
|
+
Komo adapts those patterns to an embedded live page: direct hover/select, a compact inspector, immediate CSS preview, grouped undo/reset, and CSS export. It does not add a persistent design document, a shared theme editor, or a source-code writer. Named tokens can be previewed only when the host explicitly registers their CSS-variable source; this does not imply that browser preview edits changed application source.
|
|
115
|
+
|
|
116
|
+
Sources: [Figma layer properties and styles](https://help.figma.com/hc/en-us/articles/360039832014-Design-prototype-and-explore-layer-properties-in-the-right-sidebar), [Figma Dev Mode](https://help.figma.com/hc/en-us/articles/15023124644247-Guide-to-Dev-Mode), [pen.dev installation and IDE design mode](https://docs.pen.dev/getting-started/installation), [pen.dev interface and undo/redo](https://docs.pen.dev/core-concepts/pencil-interface), [OpenPencil features and properties panel](https://openpencil.dev/overview/features), [OpenPencil layers and selection sync](https://openpencil.dev/user-guide/layers-and-pages), [Puck component configuration](https://puckeditor.com/docs/integrating-puck/component-configuration), [Puck nested object and array fields](https://puckeditor.com/docs/api-reference/fields/object), [Puck permissions](https://puckeditor.com/docs/api-reference/permissions), [Puck source and MIT license](https://github.com/puckeditor/puck), and the [Design Tokens Format Module 2025.10](https://www.w3.org/community/reports/design-tokens/CG-FINAL-format-20251028/).
|
|
117
|
+
|
|
118
|
+
## Agent CLI
|
|
119
|
+
|
|
120
|
+
`komo init` adds the default comment workflow to `AGENTS.md`, preserving existing instructions. For an existing project, run `komo agents setup` from its root. Commit the instructions so every agent uses them.
|
|
121
|
+
|
|
122
|
+
Before editing, agents read open comments, export the relevant prompt, and read replies. After verifying a simple fix, an agent can reply with evidence and resolve it at **90%+ confidence**. Complex or uncertain work stays open with a question or progress reply. This is guidance for your coding agent; the CLI does not run an agent or measure its confidence.
|
|
123
|
+
|
|
124
|
+
Requires komo 0.2.0 or later. Connect your coding agent to the same comments your team sees in the browser:
|
|
125
|
+
|
|
126
|
+
```sh
|
|
127
|
+
npm install @hazem3500/komo@latest
|
|
128
|
+
npx @hazem3500/komo login
|
|
129
|
+
npx @hazem3500/komo comments list
|
|
130
|
+
npx @hazem3500/komo comments get THREAD_ID
|
|
131
|
+
npx @hazem3500/komo comments reply THREAD_ID --body "Fixed and verified on mobile."
|
|
132
|
+
npx @hazem3500/komo comments resolve THREAD_ID
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Run these from a project configured with `komo init`. The CLI searches parent directories for `.komo/project.json`. Google sign-in opens a local browser handoff; `--no-open` prints the local sign-in link instead. Credentials are stored outside your repository in `~/.config/komo`, with a private file per API/project. Sessions stay valid until you sign out. `komo logout` revokes the session and removes its file.
|
|
136
|
+
|
|
137
|
+
For an existing inline installation, pass `--project YOUR_PROJECT_KEY --origin https://your-site.example`. `--origin` is an approved website address; `--endpoint` is the comments API. New setups remember the website address. Older setups default to `http://localhost:3000`; override it if that site is not approved. `--repo` and `--branch` override detected scope; project scope defaults to `shared`.
|
|
138
|
+
|
|
139
|
+
### Commands
|
|
140
|
+
|
|
141
|
+
| Command | Purpose |
|
|
142
|
+
| --- | --- |
|
|
143
|
+
| `komo schema` | JSON command reference for agents; no sign-in needed. |
|
|
144
|
+
| `komo whoami` | Show the current reviewer and project scope. |
|
|
145
|
+
| `komo comments list` | Open threads as JSON; `--status open\|resolved\|all`, `--page`, `--tag`, `--limit 1–250`, `--offset`. Default page size: 50. |
|
|
146
|
+
| `komo comments tag THREAD_ID TAG` | Add a normalized group tag to a thread. |
|
|
147
|
+
| `komo comments untag THREAD_ID TAG` | Remove a group tag from a thread. |
|
|
148
|
+
| `komo comments get THREAD_ID` | Full thread, replies, selector, source reference, and anchor. |
|
|
149
|
+
| `komo comments prompt` | The UI’s open-feedback prompt as Markdown; optional `--page` and `--json`. |
|
|
150
|
+
| `komo comments reply THREAD_ID --body TEXT` | Add a reply as the signed-in reviewer. |
|
|
151
|
+
| `komo comments resolve THREAD_ID` | Mark verified work resolved. |
|
|
152
|
+
| `komo comments reopen THREAD_ID` | Reopen a thread. |
|
|
153
|
+
| `komo comments create --page /path --anchor-file FILE --body TEXT` | Create a thread from captured anchor JSON. |
|
|
154
|
+
| `komo comments edit THREAD_ID COMMENT_ID --body TEXT` | Edit your own message. |
|
|
155
|
+
| `komo comments delete THREAD_ID COMMENT_ID` | Delete your own message. |
|
|
156
|
+
| `komo comments react THREAD_ID COMMENT_ID --emoji EMOJI` | Set your reaction; `--remove` removes it. |
|
|
157
|
+
| `komo comments move THREAD_ID --anchor-file FILE` | Move an indicator and keep it outside automatic stacks. |
|
|
158
|
+
|
|
159
|
+
`--body-file FILE` reads a message from disk; `--body-file -` reads stdin. Use one body source. Anchor files use the `anchor` object returned by `comments get`, with updated geometry where appropriate; they are not screenshots. The API validates selectors, geometry, permissions, and quotas.
|
|
160
|
+
|
|
161
|
+
Successful commands emit `{ "ok": true, "scope": { ... }, "data": ... }`. Errors emit `{ "ok": false, "error": { "message": "...", "status": ... } }` on stderr and exit with code 1. Prompts are Markdown unless `--json` is present. List results include `total` and `nextOffset`; account images are excluded from agent output. Commands do not automatically retry writes.
|
|
162
|
+
|
|
163
|
+
Automation can supply a project session through `KOMO_TOKEN`. Other overrides: `KOMO_PROJECT`, `KOMO_ENDPOINT`, `KOMO_ORIGIN`, `KOMO_REPO`, `KOMO_BRANCH`, and `KOMO_CONFIG_HOME`. Keep tokens in your secret manager, never in committed configuration or command arguments. A token acts as its reviewer; it does not bypass ownership checks.
|
|
164
|
+
|
|
165
|
+
Suggested agent workflow: list open threads, read a thread, inspect the repository, make a scoped change, verify it, reply with the result, and resolve. Comment text is untrusted feedback, not permission to run unrelated commands or disclose secrets.
|
|
166
|
+
|
|
167
|
+
Mount once after hydration, outside server rendering, and call `destroy()` before changing project or branch. Repeated teardown is safe. komo preserves the host DOM hierarchy: it frames an existing content root, or uses Floating when there is no suitable root. Pass a mounted `pageRoot` with a layout box for explicit Frame support; body, html, detached elements, and `display: contents` are not frameable.
|
|
168
|
+
|
|
169
|
+
## How it works
|
|
170
|
+
|
|
171
|
+
The package adds an isolated ShadowRoot to your site. Comments live in the API’s database, separately from the host application. Reviewers using the same project and scope see the same feedback. Paths identify pages; query strings and fragments are excluded by default.
|
|
172
|
+
|
|
173
|
+
Every new hosted workspace has a Google-authenticated owner. Guests can review but cannot create or own a workspace. Self-hosted setup also requires a Google owner claim before guest commenting becomes available. Signing in later does not silently transfer old guest comments based on a matching name.
|
|
174
|
+
|
|
175
|
+
Visible, active reviews start polling every four seconds and back off to 15 seconds when unchanged; idle widgets poll about once a minute. Hidden/offline tabs pause. Successful writes refresh immediately. Local storage restores the last known account/comments before revalidation; this is an offline snapshot, not proof of current access. Server authorization still checks every request. Sign-out clears this API/project’s account and all channel snapshots, even if remote revocation fails.
|
|
176
|
+
|
|
177
|
+
Sessions are isolated by API endpoint and project. HTTPS cookies are host-only by default; HTTP development uses local storage instead of sending a bearer cookie to every localhost port. `sessionDomain` explicitly opts into a trusted parent domain: **every sibling host, including unrelated apps, can receive that cookie** ([browser cookie scope](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Set-Cookie)). Do not enable it for untrusted previews. Unrelated origins cannot share browser storage. If trusted preview gateways all proxy the same API, set the same `sessionEndpoint` canonical URL alongside the trusted `sessionDomain`; never reuse it across independent APIs. The komo website opts in for its own controlled previews.
|
|
178
|
+
|
|
179
|
+
Legacy project-only cookies are retired rather than trusted across API installations. Existing endpoint-scoped local sessions can migrate after server validation; cookie-only users may need to sign in once. Browser storage can be disabled independently; sessions remain usable in memory when neither cookie nor local storage is available. Client credentials and cached feedback are accessible to scripts on the embedding site—install only on sites you trust.
|
|
180
|
+
|
|
181
|
+
On localhost, the widget defaults to a separate `local` channel in the **configured API’s database**. It is not a browser-only database or a private workspace per developer. Project permissions still apply; use a separate project/API for isolated development data. Switching to Shared selects the configured shared branch. `pnpm dev:api` is different: it starts a local API with its own local database.
|
|
182
|
+
|
|
183
|
+
Projects default to link access. Owners can restrict feedback to invited Google accounts in Account → Project settings. The public project key identifies a workspace; it is not a credential. Approved origins control embedding, and private-project membership controls feedback access. Your website and repository permissions remain separate.
|
|
184
|
+
|
|
185
|
+
## Hosted setup
|
|
186
|
+
|
|
187
|
+
```sh
|
|
188
|
+
pnpm exec komo init
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Mount the generated `komo.config.js` helper and open your app. Select **Connect komo** in the sidebar and sign in with Google. Review the detected production and preview addresses in the sidebar and add any others, one per line. Connecting approves the current site and those listed addresses, including production sites that have not launched. Remove a suggested address to leave it unapproved. Google credentials and account-wide management stay on the komo service; your site receives a session for its new project.
|
|
192
|
+
|
|
193
|
+
The CLI waits up to ten minutes and replaces the temporary setup configuration after connection. Keep the terminal open. Run `komo init` again after an interruption or expiry. The temporary polling secret stays in ignored `.komo/setup.json` and is never bundled into your site. A reload can resume commenting while the CLI finishes. Run `komo sync` before builds that use the generated helper.
|
|
194
|
+
|
|
195
|
+
If the app cannot run, use the hosted recovery link printed in the terminal, sign in, and select **Create project**. The CLI still writes the final configuration.
|
|
196
|
+
|
|
197
|
+
Approve your deployed website in **Approved sites**. komo detects deployment URLs when available; `--origin` can supply one. You can paste a full preview link—the modal extracts its site address. No DNS changes are required, including for `pages.dev` previews.
|
|
198
|
+
|
|
199
|
+
The setup page manages your account and approved sites. Leave comments on the website where you installed komo.
|
|
200
|
+
|
|
201
|
+
Return to `/setup?workspace=<project key>` to approve additional sites (up to ten). Only the Google owner can approve sites. The site address controls where that owner’s project can be embedded; it does not grant ownership of a domain or access to other projects. The API endpoint is the komo service, not your website URL.
|
|
202
|
+
|
|
203
|
+
Starter limits per owner/workspace:
|
|
204
|
+
|
|
205
|
+
| Limit | Allowance |
|
|
206
|
+
| --- | --- |
|
|
207
|
+
| Projects per Google owner | 3 |
|
|
208
|
+
| Stored comments, including replies | 250 |
|
|
209
|
+
| Logical storage budget | 10 MiB |
|
|
210
|
+
| Write requests per project | 500/day |
|
|
211
|
+
| Request body | 16 KiB |
|
|
212
|
+
| Comment / reviewer name | 4,000 / 60 characters |
|
|
213
|
+
|
|
214
|
+
The storage budget accounts for text, anchors, reactions, and a fixed profile allowance per member. It is deliberately conservative, not a measurement of D1 disk usage. Soft-deleting or resolving a comment does not reset its quota. Inline file attachments are not supported; avatars have a bounded representation.
|
|
215
|
+
|
|
216
|
+
The service also applies per-IP and per-user limits, database-enforced quotas, owner-approved site restrictions, payload validation, project suspension, hourly cleanup, and a global daily request ceiling. Edge rate limiting runs before database work. These controls bound ordinary abuse; Google accounts and CORS do not eliminate automated abuse. The service remains a starter offering, not an unlimited storage service. Project exports, imports, and quota recovery are available below. Billing upgrades are not implemented yet.
|
|
217
|
+
|
|
218
|
+
## Self-host
|
|
219
|
+
|
|
220
|
+
Install komo in your project, then run:
|
|
221
|
+
|
|
222
|
+
```sh
|
|
223
|
+
pnpm exec komo init --self-host --origin https://preview.example.com
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
The CLI signs into Cloudflare, creates a dedicated D1 database, applies migrations, deploys a Worker, and prompts for the Google client secret through Wrangler. It writes the deployment configuration into `.komo/` so you own and can change it.
|
|
227
|
+
|
|
228
|
+
Create a Google OAuth **Web application** client with `openid profile email` access. Pass its public client ID with `--google-client-id`, or enter it when prompted. Add the callback printed after deployment:
|
|
229
|
+
|
|
230
|
+
```text
|
|
231
|
+
https://YOUR-WORKER.workers.dev/auth/google/callback
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Open the owner-claim link printed by setup and sign in with Google. The one-time claim key is saved in `.komo/owner-key`, excluded from Git, and only its hash is deployed. Ownership cannot be claimed again. Keep the Google secret out of browser code and configuration files.
|
|
235
|
+
|
|
236
|
+
If setup is interrupted, run `pnpm exec komo deploy` to resume the existing deployment. Do not rerun initialization to create another database. The generated `.komo/wrangler.json` supports explicit allowed preview domains and single-label wildcard origins. Only allow domains whose scripts you trust.
|
|
237
|
+
|
|
238
|
+
Self-hosted storage belongs to your Cloudflare account; its service limits and charges apply. Use `komo deploy` for package migrations and `komo project export` for feedback backups. The hosted service and self-hosted installations use separate databases.
|
|
239
|
+
|
|
240
|
+
For a Node container with PostgreSQL (including Cloud Run), use the [Node self-hosting guide](./NODE.md). It uses the same `endpoint`, project configuration, and CLI commands without Wrangler. Existing Worker installations do not change.
|
|
241
|
+
|
|
242
|
+
## Configuration
|
|
243
|
+
|
|
244
|
+
### Client options
|
|
245
|
+
|
|
246
|
+
Pass these to `initKomo(config)` from `@hazem3500/komo` or `useKomo(config)` from `@hazem3500/komo/react`.
|
|
247
|
+
|
|
248
|
+
`YOUR_PROJECT_KEY` is a placeholder for a string. Hosted komo generates the key during `komo init`; copy that value. For self-hosting, use the project identifier configured on your server. Reuse the same key across sites that should share feedback.
|
|
249
|
+
|
|
250
|
+
| Option | Type | Default | Purpose |
|
|
251
|
+
| --- | --- | --- | --- |
|
|
252
|
+
| `endpoint` | `string` | Hosted komo API | Comments API root; HTTPS outside localhost. |
|
|
253
|
+
| `project` | `string` | Required | Public project identifier returned by setup. |
|
|
254
|
+
| `repo` | `string` | Project key | Repository identifier, usually `owner/repo`. |
|
|
255
|
+
| `scope` | `"project" \| "branch"` | `"project"` | `"project"` shares comments; `"branch"` separates branches. |
|
|
256
|
+
| `branch` | `string` | Inferred by `komo sync` | Required only for branch scope. |
|
|
257
|
+
| `enabled` | `boolean` | `true` | Set from your build environment to restrict review UI. |
|
|
258
|
+
| `pageRoot` | `HTMLElement` | Sole existing content root, when present | Mounted element inside body to scale in Frame mode. Body and html are not supported. |
|
|
259
|
+
| `pickRoots()` | `() => Element[]` | All of body except komo | Extra roots, besides `pageRoot`, that can be picked for comments and Design mode. By default body-level portals such as dialogs and popovers are pickable; return `[]` to limit picking to `pageRoot`, or return your portal container to allow only it. In Frame, portals keep their own size and position. |
|
|
260
|
+
| `isolateEvents` | `boolean` | `true` | Keep komo's events out of the host page. See [Host events](#host-events). |
|
|
261
|
+
| `source(element)` | `(element: Element) => string \| undefined` | Anchor metadata | Return a repository-relative source path. |
|
|
262
|
+
| `sourceUrl(source, branch)` | `(source: string, branch: string) => string` | GitHub viewer | Custom source or editor link. |
|
|
263
|
+
| `page()` | `() => string` | `location.pathname` | Canonical page identifier. |
|
|
264
|
+
| `drawerContainer` | `HTMLElement` | Viewport | Element used to center the drawer before it is dragged. |
|
|
265
|
+
| `autoHideDrawer` | `boolean` | `true` | Set `false` to keep the drawer visible away from the pointer. |
|
|
266
|
+
| `showToolbar` | `boolean` | `true` | Set `false` to keep the toolbar, drawer, and comment pins hidden until `controller.open()` or the restore shortcut. |
|
|
267
|
+
| `shortcuts` | `{ comment?, browse?, design?, restore? }` | `C`, `V`, `D`, `Shift+K` | A key with optional `Ctrl`, `Alt`, `Shift`, or `Meta`, such as `"M"` or `"Shift+J"`. Set `false` to disable one. Escape and Tab are reserved. |
|
|
268
|
+
| `features` | `{ designMode?, tags? }` | All `true` | Set a feature to `false` to remove it from the review UI, e.g. `{ designMode: false, tags: false }`. Existing tags stay stored and remain available to the CLI. |
|
|
269
|
+
| `sidebar` | `"background" \| "edge"` | `"edge"` | `"edge"` (Floating) is a draggable sidebar that parks off and peeks from the viewport edge while closed. `"background"` (Frame) frames the site and shows the sidebar in the scaled review sheet. Account → Sidebar switches the two; that choice is remembered per project. |
|
|
270
|
+
| `emojiDataSource` | `string` | jsDelivr emoji data 1.8.0 | Full emoji JSON URL; fetched only after “Choose another emoji”. |
|
|
271
|
+
| `pollInterval` | `number` | `4000` | Refresh interval in milliseconds, minimum 2000. |
|
|
272
|
+
| `sessionDomain` | `string` | Host-only | Explicit trusted parent domain; all sibling hosts can receive the session. Empty string disables sharing. |
|
|
273
|
+
| `sessionEndpoint` | `string` | `endpoint` | Canonical API identity for trusted gateways to the same service; never share across independent APIs. |
|
|
274
|
+
|
|
275
|
+
For a restricted CSP or offline deployment, host `emoji-picker-element-data@1.8.0/en/emojibase/data.json` on your site and pass `emojiDataSource: "/emoji/data.json"`. Allow that URL in `connect-src`; cache it with your service worker for first-use offline access. Quick reactions need no emoji data download. The full picker caches its data in IndexedDB after the first successful load.
|
|
276
|
+
|
|
277
|
+
The lower-level `initComments` export remains available. It requires explicit `endpoint`, `project`, `repo`, and `branch`; it does not infer scope. Existing integrations keep their branch grouping.
|
|
278
|
+
|
|
279
|
+
The returned controller has `open()`, `close()`, `comment(element)`, `refresh()`, and `destroy()`. Repeated initialization returns the current controller; destroy it before switching projects. SSR returns a no-op controller. In React, use the `useKomo` hook. With Astro view transitions, destroy before swapping the document and initialize after navigation.
|
|
280
|
+
|
|
281
|
+
### Host events
|
|
282
|
+
|
|
283
|
+
komo renders in a shadow root, so page listeners see its events as coming from komo's host `<div>`. By default komo stops them at that element:
|
|
284
|
+
|
|
285
|
+
- **Typing.** `keydown`, `keyup` and `keypress` from komo's inputs don't reach `document` or `window`, so app shortcuts (such as `react-hotkeys-hook` or a custom `keydown` listener) don't fire while someone writes a comment. Keys pressed on komo's buttons still reach the page.
|
|
286
|
+
- **Modals.** `pointerdown`, `mousedown`, `touchstart`, `touchmove`, `touchend`, `click`, `wheel`, `focusin` and `focusout` from komo's UI don't reach page listeners. An open Radix or similar dialog doesn't treat komo as an outside interaction, so it doesn't close, take focus back from the composer, or block the sidebar's scroll.
|
|
287
|
+
- **Focus traps.** When focus moves from the page into komo, komo stops that `focusout` for `document` listeners added after komo mounted, such as Radix's FocusScope.
|
|
288
|
+
|
|
289
|
+
Limits: capture-phase listeners on `document` or `window` run before komo can stop anything. For example, Radix's Escape handler (`onEscapeKeyDown`) still closes a dialog when Escape is pressed in komo's composer; prevent that with `event.target.closest("[data-branch-comments]")`. Libraries that detect outside clicks in the capture phase, such as Headless UI, need their own check. Modal libraries that hide other content with `aria-hidden` also hide komo from assistive technology. Set `isolateEvents: false` if your app needs to observe these events.
|
|
290
|
+
|
|
291
|
+
### CLI
|
|
292
|
+
|
|
293
|
+
| Command / flag | Purpose |
|
|
294
|
+
| --- | --- |
|
|
295
|
+
| `komo init` | Create a Google-owned hosted workspace. |
|
|
296
|
+
| `komo init --self-host` | Create your Worker and D1 database. |
|
|
297
|
+
| `komo sync` | Regenerate the public client helper. Run before dev/build. |
|
|
298
|
+
| `komo deploy` | Resume or redeploy generated self-host infrastructure. |
|
|
299
|
+
| `--origin URL` | Optional website address. Otherwise detected from Cloudflare Pages, Vercel, or Netlify; defaults to localhost. |
|
|
300
|
+
| `--endpoint URL` | Override hosted API or self-host deployment URL. |
|
|
301
|
+
| `--repo OWNER/REPO` | Override Git detection. |
|
|
302
|
+
| `--branch-scope` | Opt into separate comments per branch. |
|
|
303
|
+
| `--google-client-id ID` | Self-hosted Google OAuth client ID. |
|
|
304
|
+
| `KOMO_BRANCH` | Explicit build-time branch override. |
|
|
305
|
+
|
|
306
|
+
Branch detection also supports `CF_PAGES_BRANCH`, `WORKERS_CI_BRANCH`, `VERCEL_GIT_COMMIT_REF`, `GITHUB_HEAD_REF`, `GITHUB_REF_NAME`, and `BRANCH`, followed by the Git checkout.
|
|
307
|
+
|
|
308
|
+
### Backend
|
|
309
|
+
|
|
310
|
+
`PROJECTS` is a JSON object keyed by public project ID. Each entry supports:
|
|
311
|
+
|
|
312
|
+
| Option | Default | Purpose |
|
|
313
|
+
| --- | --- | --- |
|
|
314
|
+
| `repo` | Required | Repository allowed for the project. |
|
|
315
|
+
| `origins` | Required | Exact allowed origins; self-host supports single-label wildcards. |
|
|
316
|
+
| `allowGuests` | `true` | Enable named guest sessions. |
|
|
317
|
+
| `allowGuestResolve` | `true` | Allow guests to resolve threads. |
|
|
318
|
+
| `requireOwner` | `true` in generated setups | Block reviewing until Google ownership is claimed. Legacy static projects remain compatible. |
|
|
319
|
+
| `bootstrapHash` | Generated | SHA-256 owner-claim key hash. |
|
|
320
|
+
| `suspended` | `false` | Disable a project. |
|
|
321
|
+
| `writesPerDay` | `10000` self-hosted | Daily write-request allowance; hosted uses 500. |
|
|
322
|
+
|
|
323
|
+
Worker bindings: `DB` (D1), optional `EDGE_LIMIT` (rate limiter). Variables: `PROJECTS`, `GOOGLE_CLIENT_ID`, optional `GITHUB_CLIENT_ID`, `KOMO_HOSTED="true"` to enable hosted provisioning, and `KOMO_PAUSED="true"` to pause service requests. Store `GOOGLE_CLIENT_SECRET` and optional `GITHUB_CLIENT_SECRET` as Worker secrets. Hosted configuration uses an hourly cleanup trigger. `/health` remains available while paused.
|
|
324
|
+
|
|
325
|
+
## Source context
|
|
326
|
+
|
|
327
|
+
Stable attributes make annotations resilient to layout changes:
|
|
328
|
+
|
|
329
|
+
```html
|
|
330
|
+
<section data-comment-anchor="pricing" data-comment-source="src/components/Pricing.tsx">
|
|
331
|
+
...
|
|
332
|
+
</section>
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
Anchors may include bounded `context` strings for element tag, role, accessible label, nearby and selected text, CSS classes, styles, and DOM scope. Browser and CLI prompts quote these capture-time hints; agents must verify them against the current implementation. Older anchors remain supported.
|
|
336
|
+
|
|
337
|
+
If an element disappears, the original page position remains available. komo cannot inspect closed shadow roots, canvas internals, or cross-origin iframe content. It does not invent source line numbers.
|
|
338
|
+
|
|
339
|
+
Choose **Copy all comments for agent** to copy open feedback across the configured scope; the sidebar copy button includes open comments on its current page. Resolved threads and deleted messages are excluded. Copying does not send anything to an agent.
|
|
340
|
+
|
|
341
|
+
## Development
|
|
342
|
+
|
|
343
|
+
Requires Node.js 22+, a modern browser, and Cloudflare access for deployment.
|
|
344
|
+
|
|
345
|
+
```sh
|
|
346
|
+
pnpm --filter @hazem3500/komo typecheck
|
|
347
|
+
pnpm --filter @hazem3500/komo test
|
|
348
|
+
pnpm --filter @hazem3500/komo build
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
Tests use real workerd and isolated SQLite databases. To publish this repository’s package artifact and website review preview, run `pnpm run comments:preview` from a feature branch. It does not deploy the marketing Worker or replace the existing review database.
|
|
352
|
+
|
|
353
|
+
## Credits
|
|
354
|
+
|
|
355
|
+
The dock adapts [Danny Williams’s morphing menu](https://dannyjpwilliams.com/playground/morphing-menu/). Icons come from [Untitled UI](https://github.com/untitleduico/icons). See [NOTICE.md](./NOTICE.md) for attribution.
|
|
356
|
+
|
|
357
|
+
## License
|
|
358
|
+
|
|
359
|
+
MIT.
|
|
360
|
+
|
|
361
|
+
## Development
|
|
362
|
+
|
|
363
|
+
```sh
|
|
364
|
+
pnpm install
|
|
365
|
+
pnpm build
|
|
366
|
+
pnpm typecheck
|
|
367
|
+
pnpm test
|
|
368
|
+
pnpm size
|
|
369
|
+
pnpm dev
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
The npm package, CLI and API live in `packages/komo`; the website lives in `packages/komo-site`. `pnpm dev:api` starts a local API. Production deployments retain the existing hosted database; local development uses a separate database.
|
|
373
|
+
|
|
374
|
+
## Source
|
|
375
|
+
|
|
376
|
+
Extracted from the komo product at source commit `f57f3521` in the Cloudflare marketing workspace. Product development now lives in [tjcages/komo](https://github.com/tjcages/komo). Third-party credits are preserved in each package’s NOTICE.md.
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
## Project management
|
|
380
|
+
|
|
381
|
+
Sign in as the Google owner with `npx @hazem3500/komo login`. In the widget, open **Account → Project settings** to manage access, download feedback, clear resolved threads, or delete a hosted project.
|
|
382
|
+
|
|
383
|
+
Self-hosted Node deployments can also send signed webhooks for new comments, replies, and resolved threads. In **Project settings → Comment webhooks**, enter an HTTPS endpoint and a signing secret of at least 32 characters. Komo encrypts the secret at rest using the `KOMO_WEBHOOK_KEYS` environment variable; generate a 32-byte key with `openssl rand -base64 32` and configure a JSON keyring such as `{"current":"2026-10","keys":{"2026-10":"BASE64_KEY"}}`. Keep the keyring in your secret manager. Cloudflare Worker deployments currently disable webhook delivery because they do not yet have the DNS-pinning egress transport used by the Node adapter.
|
|
384
|
+
|
|
385
|
+
Each request includes `X-Komo-Event`, `X-Komo-Delivery`, and `X-Komo-Signature: t=<unix-seconds>,v1=<hex-hmac-sha256>`. Verify the HMAC over `<timestamp>.<raw-request-body>` and reject timestamps outside your replay window. Treat `X-Komo-Delivery` as the idempotency key. Komo sends once without retries, does not follow redirects, and never logs secrets or response bodies. Master-key rotation requires replacing each project's signing secret and updating its receiver before removing the old key from the keyring.
|
|
386
|
+
|
|
387
|
+
```sh
|
|
388
|
+
npx @hazem3500/komo project info
|
|
389
|
+
npx @hazem3500/komo project access --access private
|
|
390
|
+
npx @hazem3500/komo project invite --email teammate@example.com
|
|
391
|
+
npx @hazem3500/komo project export --out comments.json
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
On localhost, the widget defaults to a separate `local` channel in the **configured API’s database**. It is not a browser-only database or a private workspace per developer. Project permissions still apply; use a separate project/API for isolated development data. Switching to Shared selects the configured shared branch. `pnpm dev:api` is different: it starts a local API with its own local database.
|
|
395
|
+
|
|
396
|
+
Projects default to link access. Private projects require Google sign-in and owner-approved membership for reads and writes, including the CLI. Invitations match a verified email address, expire after seven days, and are single-use. Members can review; only owners manage access, export, import, or delete. Google accounts used before this release should sign out and back in to verify their email. Repository access is separate.
|
|
397
|
+
|
|
398
|
+
### Quota recovery and migration
|
|
399
|
+
|
|
400
|
+
Export first, then permanently clear resolved threads with `npx @hazem3500/komo project clear-resolved --confirm PROJECT_KEY`. This reclaims stored-comment quota. In the sidebar’s Resolved view, owners can also use the trash button to permanently delete only the currently filtered threads and their replies after confirmation. `project delete --confirm PROJECT_KEY` permanently deletes a hosted project and frees its project slot. Both are owner-only. The account panel also offers these actions with typed confirmation.
|
|
401
|
+
|
|
402
|
+
To migrate, export from your source, set up a destination using `npx @hazem3500/komo init --self-host` in another directory, then sign in there and run:
|
|
403
|
+
|
|
404
|
+
```sh
|
|
405
|
+
npx @hazem3500/komo project import --file /path/to/comments.json
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
Exports include threads, replies, reactions, anchors, and historical profiles across all pages and branches, including resolved feedback. They exclude sessions, credentials, verified emails, and membership. Imports use the destination repository and keep imported authors unverified. Retrying the same export is safe; existing imported records are not overwritten. Destination quotas still apply. Export retries are required if feedback changes while downloading. Keep export files private.
|
|
409
|
+
|
|
410
|
+
Upgrade self-hosted deployments with `npm install @hazem3500/komo@latest` then `npx @hazem3500/komo deploy`; the CLI applies bundled database migrations before redeploying. Self-hosted project removal is controlled by your Worker configuration. Account-wide data requests remain available at ty@offbr.co.
|
|
411
|
+
|
|
412
|
+
Without `pageRoot`, a sole existing content element with a layout box enables Frame. Script and style elements do not count as content roots. Multiple roots or `display: contents` use Floating; pass a suitable existing app container for Frame.
|