@cssints/devtools 0.1.0 → 0.3.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/README.md +70 -11
- package/dist/opencode.js +30 -0
- package/dist/overlay.js +2036 -1913
- package/package.json +4 -3
- package/src/core/cascade.ts +9 -1
- package/src/core/index.ts +3 -3
- package/src/core/prompt.ts +102 -8
- package/src/core/session.ts +18 -0
package/README.md
CHANGED
|
@@ -33,6 +33,11 @@ docs/API.md, "Debugging: the devtools overlay"; the design in docs/research/devt
|
|
|
33
33
|
(a `cssints` button and Alt+Shift+C). The first open loads both; a call shows as `file:line` and its source, with no link
|
|
34
34
|
to an editor. The table has no path of the machine that built it. Sizes are logged by the build (this site: table 70 kB,
|
|
35
35
|
overlay 190 kB, the entry +0.85 kB).
|
|
36
|
+
- **Readable classes, dev only.** The dev server adds `cs-<owner>[-<label>]` to each `cn(...)` and `cv({...})`:
|
|
37
|
+
`const logo = cn(...)` inside `function Landing()` gives `cs-Landing-logo`, at the top of `landing/page.ts`
|
|
38
|
+
`cs-page-logo`, `<div class={cn(...)}>` in `Button` `cs-Button`. The panel shows it first in its bar and in the hover
|
|
39
|
+
tag; the prompt's `Classes:` line lists it. It is not in the table, the sheet or the hashes, and a build has none.
|
|
40
|
+
docs/API.md, "Debugging: the devtools overlay".
|
|
36
41
|
- **The data is the compiler's.** The table holds the sheet as the server renders it, each class's rule, plugin,
|
|
37
42
|
tokens and sites, and the place of every call. The core parses the sheet; it never reads the page's CSSOM for
|
|
38
43
|
cssints' rules.
|
|
@@ -54,15 +59,68 @@ cssints({ devtools: true, opencode: true });
|
|
|
54
59
|
cssints({ devtools: true, opencode: { agent: "build", skills: ["cssints"] } });
|
|
55
60
|
```
|
|
56
61
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
62
|
+
The panel has an OpenCode box whenever the option is on, picked element or not. It is pinned: at the bottom of the panel in
|
|
63
|
+
a left or right dock, at its right edge (20 rem wide, to the right of the inspection) in a top or bottom dock, and in the
|
|
64
|
+
detached window by the window's shape (wider than tall: the right edge); the inspection beside it scrolls, the box does not.
|
|
65
|
+
The box has a title row (`OpenCode`, and a dot with `connected` or `not connected`), the input (Enter sends, Shift+Enter is a
|
|
66
|
+
new line) and one status line:
|
|
67
|
+
|
|
68
|
+
- Not connected: how to connect. Start OpenCode (or `opencode serve --service`) and run `/cssints-devtools` in a session; the
|
|
69
|
+
dev server finds the running service by itself; a prompt without an attached session opens a new one in the repository
|
|
70
|
+
folder; `@opencode/client` must be in the app's devDependencies. When the server has its own message (the client is not
|
|
71
|
+
installed), it follows.
|
|
72
|
+
- Connected: `connected to <service address> · attached session <id> · <state>`, or `no session attached: a prompt starts a
|
|
73
|
+
new one`. The id is the session `/cssints-devtools` attached to this dev server (truncated; the full id is the title); the
|
|
74
|
+
state is `running`, `completed`, `failed` or `interrupted`.
|
|
75
|
+
|
|
76
|
+
The page asks the dev server whether a service is found (a probe: it looks, it creates no session) when the panel opens, when
|
|
77
|
+
the box gets focus and after a send that found none, so starting OpenCode later shows up without a reload.
|
|
78
|
+
|
|
79
|
+
### The command `/cssints-devtools`
|
|
80
|
+
|
|
81
|
+
The box does not choose a session: an OpenCode command attaches the session it runs in to a dev server. Load the plugin of
|
|
82
|
+
this package in the repository of the app (OpenCode loads `.ts` files of `.opencode/plugins/`; a package subpath in
|
|
83
|
+
`plugins` of opencode.jsonc is not loaded in 2.0.27, nor is a folder with only a `package.json`):
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
// .opencode/plugins/cssints-devtools.ts
|
|
87
|
+
export { default } from "@cssints/devtools/opencode";
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Then, in an OpenCode session:
|
|
91
|
+
|
|
92
|
+
- `/cssints-devtools`: one dev server of this repository with `opencode` on runs: this session is attached to it. Several
|
|
93
|
+
run: they are listed (id, address, root) and none is attached. None: it says so.
|
|
94
|
+
- `/cssints-devtools <id or part of it>` picks one (the id is the Vite root relative to the repository, `/` as `-`, so
|
|
95
|
+
`docs` picks `apps-docs`); `/cssints-devtools off [id]` detaches this session (or that server).
|
|
96
|
+
|
|
97
|
+
Each outcome is a message in the session (`ctx.session.synthetic`, which also gives the model a turn to acknowledge it). The
|
|
98
|
+
prompts of an attached server's panel are sent to that session, which is not created again; a session that is gone makes the
|
|
99
|
+
send fail with a message that says to run `/cssints-devtools` again. The rendezvous is files, not a route (the ask channel is
|
|
100
|
+
the HMR socket, which Vite's token protects): `<repository>/node_modules/.cache/cssints-devtools/<id>.json` (`root`, `pid`,
|
|
101
|
+
`url`) is written by the dev server while it runs, and `<id>.session.json` by the command. A dev server that crashed leaves a
|
|
102
|
+
file with a dead pid, which the command ignores and removes. The plugin imports `cssints/devtools-link`, the module both
|
|
103
|
+
sides share.
|
|
104
|
+
|
|
105
|
+
Without an attached session, Enter starts a session on the running OpenCode V2 service (`Service.discover` of
|
|
106
|
+
`@opencode/client`; the page never talks to it) in the nearest folder up from the Vite root that has a `.git`. The prompt is built by `src/core/prompt.ts`:
|
|
107
|
+
|
|
108
|
+
- `App:` (added by the dev server, on top): the Vite root relative to the repository (`.` when it is the repository) and the
|
|
109
|
+
address the server listens on, as file paths in the prompt are relative to that root;
|
|
110
|
+
- your instruction, then `Page:` (path, query and hash, not the origin), `Title:` and `Display:` (viewport and pixel ratio,
|
|
111
|
+
the `class` and `data-*` attributes of `<html>`, `prefers-color-scheme` and `prefers-reduced-motion`; with an element, its
|
|
112
|
+
rendered size and position and whether it is hovered, focused or disabled);
|
|
113
|
+
- with an element picked: its tag, DOM path, text, classes, `id`, `role`, `aria-label`, `href`, `src`, `alt`, `type`, `name`,
|
|
114
|
+
`placeholder` and `title`, the `data-*` attributes of it and of its three nearest ancestors, the `css.*` calls that style it
|
|
115
|
+
(`file:line` and the call's source), the winner of each property and what it beat, and the tokens it reads. Never a `value`,
|
|
116
|
+
a cookie or storage.
|
|
117
|
+
|
|
118
|
+
Every list and value is cut, the server cuts the whole at 16,000 characters from the end (the `App:` line, the instruction
|
|
119
|
+
and the header come first and stay). With no service running the box says how to connect and keeps your text. The
|
|
120
|
+
page and the server talk over the HMR channel (`cssints:devtools:ask`, `asked`, `link`, `linked`), not a route. A build has
|
|
121
|
+
none of it. The idea, and the calls to the V2 client, follow
|
|
122
|
+
[brendonovich/vite-plugin-opencode](https://github.com/Brendonovich/vite-plugin-opencode) (MIT, Brendan Allan), which marks the
|
|
123
|
+
markup with Babel and so needs no table; here the table is the context.
|
|
66
124
|
|
|
67
125
|
## Layout
|
|
68
126
|
|
|
@@ -98,6 +156,7 @@ the calls to the V2 client. No code is copied. The overlay is built with [Solid]
|
|
|
98
156
|
bun run test # test/core.mjs (the cascade on happy-dom: hover vs media, @container, @supports, @scope,
|
|
99
157
|
# an unlayered rule, frame's kernel, within(), grouping), test/build.mjs (nothing in a build
|
|
100
158
|
# without the option; with "build": the table, the lazy chunk, the loader's size; apps/docs/test/browser.mjs opens it in Chrome),
|
|
101
|
-
# test/opencode.mjs (the dev server and a fake OpenCode service: the prompt as sent, the agent, the directory, the status, no service)
|
|
102
|
-
|
|
159
|
+
# test/opencode.mjs (the dev server and a fake OpenCode service: the prompt as sent, the agent, the directory, the status, no service; the link probe, with and without the service and the client; an attached session, a detached one, two dev servers, a session that is gone, stale files),
|
|
160
|
+
# test/opencode-plugin.mjs (the command on a fake plugin context: none, one, several, a pick, off, a stale pid)
|
|
161
|
+
bun run test:browsers # builds dist/overlay.js, then the demo in Chrome, Firefox and WebKit (test/browsers.mjs; each dock: the panel on its side, a pick beside it, a drag of the grip, the button after Esc, the dock and sizes after a reload; detach: the pop-up with the panel, a pick in the page followed live, the choice after a reload, closing re-docks, a blocked pop-up), then the OpenCode box in Chrome (test/ask-browser.mjs: with nothing picked, pinned in each dock and in the window, the connect text, a service started later found on focus, the attached session and its title; screenshots in test/out/)
|
|
103
162
|
```
|
package/dist/opencode.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { attach as e, gitRoot as t, servers as n, sessionOf as r } from "cssints/devtools-link";
|
|
2
|
+
//#region src/opencode.ts
|
|
3
|
+
var i = "cssints-devtools", a = (e, t) => t.map((t) => {
|
|
4
|
+
let n = r(e, t.id);
|
|
5
|
+
return `- ${t.id} ${t.url} ${t.root}${n ? ` (session ${n})` : ""}`;
|
|
6
|
+
}).join("\n"), o = (t, o, s) => {
|
|
7
|
+
let c = n(t), [l = "", ...u] = s.trim().split(/\s+/), d = l === "off" ? u.join(" ") : l, f = d ? c.filter((e) => e.id.includes(d)) : c, p = f.filter((e) => e.id === d), m = p.length ? p : f;
|
|
8
|
+
if (l === "off") {
|
|
9
|
+
let n = m.filter((e) => d ? !0 : r(t, e.id) === o);
|
|
10
|
+
return n.forEach((n) => e(t, n.id)), n.length ? `Detached from ${n.map((e) => e.id).join(", ")}: its devtools panel starts a new session per prompt again.` : d ? `No running dev server matches "${d}".\n${a(t, c)}` : "This session is not attached to any dev server.";
|
|
11
|
+
}
|
|
12
|
+
if (!c.length) return `No cssints dev server with \`devtools: true, opencode: true\` runs in ${t}. Start one, then run /${i} again.`;
|
|
13
|
+
let [h, g] = m;
|
|
14
|
+
return h ? g ? `${m.length} dev servers run${d ? ` that match "${d}"` : ""}; none is attached. Run /${i} <id> to pick one:\n${a(t, m)}` : (e(t, h.id, o), `Attached this session (${o}) to the dev server ${h.id} (${h.url}, ${h.root}). The prompts of its devtools panel now go to this session. /${i} off detaches.`) : `No running dev server matches "${d}". Running:\n${a(t, c)}`;
|
|
15
|
+
}, s = {
|
|
16
|
+
id: "cssints.devtools",
|
|
17
|
+
setup(e) {
|
|
18
|
+
let n = t(e.location.directory);
|
|
19
|
+
return e.command.transform((t) => t.add({
|
|
20
|
+
name: i,
|
|
21
|
+
description: "Attach this session to the devtools of a cssints dev server (off detaches)",
|
|
22
|
+
execute: ({ sessionID: t, prompt: r }) => e.session.synthetic({
|
|
23
|
+
sessionID: t,
|
|
24
|
+
text: `${o(n, t, r.text)}\n\n(A notice from the /${i} command: acknowledge it in one short sentence and run no tools.)`
|
|
25
|
+
}).then(() => void 0)
|
|
26
|
+
})).then((e) => () => e.dispose());
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
//#endregion
|
|
30
|
+
export { i as COMMAND, s as default, o as run };
|