@cssints/devtools 0.0.0-stage → 0.2.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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Alexei Accio
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/README.md CHANGED
@@ -1,3 +1,103 @@
1
- # Temporary Holding Version
1
+ # @cssints/devtools
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ A dev overlay inspector for cssints, built on what the dev server knows of every class: an element's classes grouped
4
+ by the `css.*` call that made them, the winner per property by the sheet's own rules (layer, specificity, order, the
5
+ conditions that hold now), and each winner checked against the browser's computed style.
6
+
7
+ ```ts
8
+ // vite.config.ts of the app; @cssints/devtools in its devDependencies
9
+ import cssints from "cssints/vite";
10
+ import { defineConfig } from "vite";
11
+
12
+ export default defineConfig({ plugins: [cssints({ devtools: true })] });
13
+ ```
14
+
15
+ Alt+Shift+C or the `cssints` button (bottom right) opens it. Hover previews an element, a click picks it; ↑ parent,
16
+ ↓ first child, ← → siblings; Esc stops picking, then closes. The four buttons in the panel header (◧ ⬒ ⬓ ◨) dock it
17
+ left, top, bottom or right (the default); drag the strip on its inner edge to resize it (at least 200 px, and a strip of
18
+ the page stays). The side and the two sizes are kept in `localStorage` (`cssints-devtools:dock`) per origin. The panel
19
+ covers the page beside it, and picking works on the rest of the page; the page is not pushed aside. The ⧉ button detaches
20
+ the panel into a window of its own (`window.open`, a same-origin pop-up, not the Document Picture-in-Picture API: that
21
+ one is Chromium only). It is the same panel: the page keeps picking (hover, click, the highlight) and the window follows
22
+ the picked element live. Closing the window, ⧉ again or a dock button puts the panel back; Esc and the `cssints` button
23
+ close both. The choice is kept with the dock, and the next open (a click or the shortcut: a pop-up needs one) opens the
24
+ window again. If the browser blocks the pop-up, the docked panel says so. The panel and the cascade rule are described in
25
+ docs/API.md, "Debugging: the devtools overlay"; the design in docs/research/devtools.md, "Our devtools".
26
+
27
+ ## Rules
28
+
29
+ - **Dev only, unless asked.** `devtools: true` adds a plugin with `apply: "serve"`: an HTML script tag, the module
30
+ `virtual:cssints/devtools` and two HMR events (`cssints:devtools:get` → `cssints:devtools`, and
31
+ `cssints:devtools:stale` after each transform). A build has none of it.
32
+ - **In a build: `devtools: "build"`.** The table is a JSON asset, the overlay a chunk of its own, and the entry holds a loader
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
+ 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
+ overlay 190 kB, the entry +0.85 kB).
36
+ - **The data is the compiler's.** The table holds the sheet as the server renders it, each class's rule, plugin,
37
+ tokens and sites, and the place of every call. The core parses the sheet; it never reads the page's CSSOM for
38
+ cssints' rules.
39
+ - **The browser decides the conditions.** At-rules (`@media`, `@container`, `@supports`, `@scope`) are evaluated for
40
+ the element by a probe sheet; states by `Element.matches`.
41
+ - **A winner is checked, not claimed.** A childless clone in place gets the winner inline; when its computed value
42
+ differs, the panel names the cause (inline style, a rule of another sheet, an unlayered rule, an animation) or marks
43
+ a layout-dependent value as unverified. A custom property is checked with its declaration as the page's sheet has
44
+ it, since a transformer (LightningCSS) reprints it and an unregistered one computes to its tokens as written.
45
+ - **Isolated.** A custom element with a shadow root and its own cssints sheet, built ahead into `dist/overlay.js`
46
+ (Solid, Effect and the core bundled): an app needs neither Solid nor a second CSS file. No tokens in its styles:
47
+ `@property` is ignored inside a shadow root.
48
+
49
+ ## Ask OpenCode
50
+
51
+ ```ts
52
+ // dev only, off by default; @opencode/client in the app's devDependencies
53
+ cssints({ devtools: true, opencode: true });
54
+ cssints({ devtools: true, opencode: { agent: "build", skills: ["cssints"] } });
55
+ ```
56
+
57
+ With an element picked, the panel has a prompt box (Enter sends, Shift+Enter is a new line). The dev server starts a session
58
+ on the running OpenCode V2 service (`Service.discover` of `@opencode/client`; the page never talks to it) in the nearest
59
+ folder up from the Vite root that has a `.git`, with a prompt of `src/core/prompt.ts`: your instruction, the element (tag,
60
+ DOM path, text, classes), the `css.*` calls that style it (`file:line` and the call's source), the winner of each property
61
+ and what it beat, and the tokens it reads; every list is cut. The box then shows `running · <session id>`, and `completed`,
62
+ `failed` or `interrupted`; with no service running it says so and keeps your text. The page and the server talk over the
63
+ HMR channel (`cssints:devtools:ask`, `cssints:devtools:asked`), not a route. A build has none of it. The idea, and the
64
+ calls to the V2 client, follow [brendonovich/vite-plugin-opencode](https://github.com/Brendonovich/vite-plugin-opencode)
65
+ (MIT, Brendan Allan), which marks the markup with Babel and so needs no table; here the table is the context.
66
+
67
+ ## Layout
68
+
69
+ - `src/core`: Effect code, no overlay in it (an extension could host it): `table.ts` (the decoded table and its
70
+ model), `css.ts` (sheet parser, specificity, states), `cascade.ts` (`inspect`), `page.ts` (the `Page` service),
71
+ `dom.ts` (its browser layer), `session.ts` (the protocol and the state), `prompt.ts` (the prompt for OpenCode).
72
+ - `src/ui`: the Solid overlay (`overlay.tsx`, `mount`) and its cssints styles (`styles.ts`).
73
+
74
+ ## Browsers
75
+
76
+ Chrome, Firefox and WebKit, checked by `test:browsers` on apps/demo's dev server.
77
+
78
+ ## Limits
79
+
80
+ - `@scope` proximity is not ranked; a logical and a physical property are two properties.
81
+ - An element inside another shadow root is picked as its host.
82
+ - A page that Vite does not serve as HTML (server rendering) gets no entry.
83
+ - A reload closes the window; in the `"build"` mode the panel opens without a click, so a kept window is blocked until the
84
+ next click on ⧉. Only one window is kept. The window has no Document Picture-in-Picture variant.
85
+ - The panel does not float and has no shortcut to switch the dock; it covers the page where it sits, so an element under
86
+ it is picked from the arrow keys or after a dock to another side.
87
+ - The panel re-inspects every 600 ms while open: each pass adds and removes a probe `<style>` and attribute.
88
+
89
+ ## Credits
90
+
91
+ The prompt box that starts an OpenCode session follows
92
+ [brendonovich/vite-plugin-opencode](https://github.com/Brendonovich/vite-plugin-opencode) (MIT, Brendan Allan): the idea and
93
+ the calls to the V2 client. No code is copied. The overlay is built with [Solid](https://www.solidjs.com) (MIT).
94
+
95
+ ## Tests
96
+
97
+ ```bash
98
+ bun run test # test/core.mjs (the cascade on happy-dom: hover vs media, @container, @supports, @scope,
99
+ # an unlayered rule, frame's kernel, within(), grouping), test/build.mjs (nothing in a build
100
+ # 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
+ 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 prompt box in Chrome (test/ask-browser.mjs)
103
+ ```