@mhmo91/schmancy 0.9.26 → 0.9.28
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/custom-elements.json +0 -42
- package/dist/agent/schmancy.agent.js +4 -6157
- package/dist/agent/schmancy.agent.js.map +1 -1
- package/dist/agent/schmancy.manifest.json +0 -12
- package/dist/handover/agent-runtime-followups.md +1 -1
- package/dist/handover/agent-runtime-v1.md +3 -3
- package/dist/handover/agent-runtime-v2-loopback.md +5 -5
- package/package.json +1 -1
- package/src/agent/agent-bundle.test.ts +54 -71
- package/src/agent/agent-entry.ts +18 -1
- package/types/src/agent/agent-entry.d.ts +18 -1
- package/types/src/agent/helpers.d.ts +26 -0
- package/types/src/agent/schmancy-skill.d.ts +2 -1
- package/src/agent/helpers.ts +0 -161
- package/src/agent/index.ts +0 -1
- package/src/agent/schmancy-skill.ts +0 -71
|
@@ -102,18 +102,6 @@
|
|
|
102
102
|
}
|
|
103
103
|
]
|
|
104
104
|
},
|
|
105
|
-
{
|
|
106
|
-
"kind": "javascript-module",
|
|
107
|
-
"path": "agent/schmancy-skill.ts",
|
|
108
|
-
"declarations": [
|
|
109
|
-
{
|
|
110
|
-
"kind": "class",
|
|
111
|
-
"name": "SchmancySkill",
|
|
112
|
-
"tagName": "schmancy-skill",
|
|
113
|
-
"description": "Self-describing runtime helper. Drop `<schmancy-skill></schmancy-skill>` once on a page and `window.schmancy.help('schmancy-button')` returns the machine-readable entry for any tag. Renders nothing."
|
|
114
|
-
}
|
|
115
|
-
]
|
|
116
|
-
},
|
|
117
105
|
{
|
|
118
106
|
"kind": "javascript-module",
|
|
119
107
|
"path": "area/area.component.ts",
|
|
@@ -203,7 +203,7 @@ The manifest already has everything needed; the package is just the JSON-RPC wra
|
|
|
203
203
|
|
|
204
204
|
**Problem.** `handover/agent-runtime-v1.md` had `<PENDING>` placeholders that we manually replaced with `0.9.13` after the first publish. Future handover docs will have the same issue.
|
|
205
205
|
|
|
206
|
-
**Fix.** A build step that substitutes `0.9.
|
|
206
|
+
**Fix.** A build step that substitutes `0.9.28` in `handover/**/*.md` against `package.json`'s `version` field on every build. `dist/handover/**/*.md` gets the rendered version; the source stays templated.
|
|
207
207
|
|
|
208
208
|
**Effort:** ~30 min (one sed step or a tiny script).
|
|
209
209
|
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
## The URLs you asked for
|
|
8
8
|
|
|
9
9
|
```
|
|
10
|
-
https://esm.sh/@mhmo91/schmancy/agent@0.9.
|
|
11
|
-
https://esm.sh/@mhmo91/schmancy/agent/manifest@0.9.
|
|
10
|
+
https://esm.sh/@mhmo91/schmancy/agent@0.9.28
|
|
11
|
+
https://esm.sh/@mhmo91/schmancy/agent/manifest@0.9.28
|
|
12
12
|
```
|
|
13
13
|
|
|
14
14
|
`0.9.13` is the first release containing `/agent`; every subsequent publish serves the same subpath. `npm view @mhmo91/schmancy version` always returns the current pin if you want to float forward.
|
|
@@ -20,7 +20,7 @@ One script tag. No bundler, no bare specifiers, no npm install.
|
|
|
20
20
|
```html
|
|
21
21
|
<!doctype html>
|
|
22
22
|
<script type="module">
|
|
23
|
-
import { $dialog, theme } from 'https://esm.sh/@mhmo91/schmancy/agent@0.9.
|
|
23
|
+
import { $dialog, theme } from 'https://esm.sh/@mhmo91/schmancy/agent@0.9.28';
|
|
24
24
|
</script>
|
|
25
25
|
<schmancy-theme root scheme="dark">
|
|
26
26
|
<schmancy-surface type="solid" fill="all">
|
|
@@ -12,7 +12,7 @@ Four separable PRs, each shippable on its own:
|
|
|
12
12
|
| # | Title | Branch | Impact on your probe |
|
|
13
13
|
|---|---|---|---|
|
|
14
14
|
| 2 | CI smoke-test gate | `feat/ci-gate-and-version-templating` | None user-facing. `window.schmancy.help()` regressions now fail-closed at publish time instead of shipping silently. |
|
|
15
|
-
| 9 | `0.9.
|
|
15
|
+
| 9 | `0.9.28` templating for handover docs | same branch as #2 | None user-facing. Future handover docs will have live esm.sh URLs instead of `<PENDING>` placeholders. |
|
|
16
16
|
| 3 | JSDoc backfill (46 components) | `feat/jsdoc-batch-{1,2,3}-*` | **This is what you'll notice.** Every form-control, container, and overlay/nav component now ships `@summary`, `@example`, and `@platform` tags in its manifest entry. `window.schmancy.help('schmancy-button')` returns a non-empty `summary`, a copy-pastable `examples[]`, and a `platformPrimitive` hint for graceful degradation. |
|
|
17
17
|
| 1 | Lazy vendor chunks | `feat/lazy-{typewriter,code-highlight,qr-scanner}` | Pages that don't render `<schmancy-code>`, `<schmancy-qr-scanner>`, or `<schmancy-typewriter>` no longer fetch `vendor-highlight`, `vendor-jsqr`, or the typewriter chunk on first paint. ~68 KB gzipped saved on cold starts for typical prototypes. |
|
|
18
18
|
|
|
@@ -21,18 +21,18 @@ The only one that changes the shape of `window.schmancy` is **#3**. The others a
|
|
|
21
21
|
## Pinned URLs (live once the PRs merge)
|
|
22
22
|
|
|
23
23
|
```
|
|
24
|
-
https://esm.sh/@mhmo91/schmancy/agent@0.9.
|
|
25
|
-
https://esm.sh/@mhmo91/schmancy/agent/manifest@0.9.
|
|
24
|
+
https://esm.sh/@mhmo91/schmancy/agent@0.9.28
|
|
25
|
+
https://esm.sh/@mhmo91/schmancy/agent/manifest@0.9.28
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
-
`0.9.
|
|
28
|
+
`0.9.28` is substituted at publish time — see [`agent-runtime-followups.md`](./agent-runtime-followups.md) #9. Until the PRs land, continue pinning `@0.9.14` (the last published version at time of writing).
|
|
29
29
|
|
|
30
30
|
## Minimum loop-back test
|
|
31
31
|
|
|
32
32
|
```html
|
|
33
33
|
<!doctype html>
|
|
34
34
|
<script type="module">
|
|
35
|
-
import 'https://esm.sh/@mhmo91/schmancy/agent@0.9.
|
|
35
|
+
import 'https://esm.sh/@mhmo91/schmancy/agent@0.9.28';
|
|
36
36
|
</script>
|
|
37
37
|
|
|
38
38
|
<schmancy-theme root scheme="dark">
|
package/package.json
CHANGED
|
@@ -2,91 +2,74 @@ import { describe, expect, it } from 'vitest'
|
|
|
2
2
|
import './agent-entry'
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* Agent bundle = distribution test suite.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* The bundle exists so consumers can drop a single `<script type="module">`
|
|
8
|
+
* tag and get every `<schmancy-*>` element registered. These tests verify
|
|
9
|
+
* that contract, no more.
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
* (
|
|
13
|
-
*
|
|
11
|
+
* Earlier versions of this suite tested a `window.schmancy.help()` /
|
|
12
|
+
* `tokens()` / `findFor()` runtime introspection surface that targeted a
|
|
13
|
+
* hypothetical live-in-browser agent — a consumer that never materialised
|
|
14
|
+
* in any shipping product. That surface was removed; AI consumers read the
|
|
15
|
+
* static manifest at `dist/agent/schmancy.manifest.json` instead, which is
|
|
16
|
+
* the real integration point.
|
|
14
17
|
*/
|
|
15
|
-
describe('agent bundle —
|
|
18
|
+
describe('agent bundle — distribution', () => {
|
|
16
19
|
it('registers `schmancy-button` on import', () => {
|
|
17
20
|
expect(customElements.get('schmancy-button')).toBeDefined()
|
|
18
21
|
})
|
|
19
22
|
|
|
20
|
-
it('registers `schmancy-
|
|
21
|
-
expect(customElements.get('schmancy-
|
|
23
|
+
it('registers `schmancy-theme` on import', () => {
|
|
24
|
+
expect(customElements.get('schmancy-theme')).toBeDefined()
|
|
22
25
|
})
|
|
23
26
|
|
|
24
|
-
it('
|
|
25
|
-
|
|
26
|
-
const skill = document.createElement('schmancy-skill')
|
|
27
|
-
host.appendChild(skill)
|
|
28
|
-
document.body.appendChild(host)
|
|
29
|
-
|
|
30
|
-
await customElements.whenDefined('schmancy-skill')
|
|
31
|
-
// One microtask for connectedCallback to finish installing globals.
|
|
32
|
-
await new Promise(requestAnimationFrame)
|
|
33
|
-
|
|
34
|
-
expect(typeof window.schmancy).toBe('object')
|
|
35
|
-
expect(typeof window.schmancy?.help).toBe('function')
|
|
36
|
-
expect(typeof window.schmancy?.tokens).toBe('function')
|
|
37
|
-
expect(typeof window.schmancy?.capabilities).toBe('function')
|
|
38
|
-
|
|
39
|
-
host.remove()
|
|
27
|
+
it('registers `schmancy-surface` on import', () => {
|
|
28
|
+
expect(customElements.get('schmancy-surface')).toBeDefined()
|
|
40
29
|
})
|
|
41
30
|
|
|
42
|
-
it('
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
31
|
+
it('registers a substantive set of components on import', () => {
|
|
32
|
+
// Spot-check 20 commonly-used tags. If anything below this threshold
|
|
33
|
+
// regresses, the side-effect registration chain is broken.
|
|
34
|
+
const sampleTags = [
|
|
35
|
+
'schmancy-button',
|
|
36
|
+
'schmancy-input',
|
|
37
|
+
'schmancy-card',
|
|
38
|
+
'schmancy-dialog',
|
|
39
|
+
'schmancy-sheet',
|
|
40
|
+
'schmancy-list',
|
|
41
|
+
'schmancy-list-item',
|
|
42
|
+
'schmancy-typography',
|
|
43
|
+
'schmancy-icon',
|
|
44
|
+
'schmancy-icon-button',
|
|
45
|
+
'schmancy-form',
|
|
46
|
+
'schmancy-checkbox',
|
|
47
|
+
'schmancy-select',
|
|
48
|
+
'schmancy-autocomplete',
|
|
49
|
+
'schmancy-radio-group',
|
|
50
|
+
'schmancy-textarea',
|
|
51
|
+
'schmancy-switch',
|
|
52
|
+
'schmancy-divider',
|
|
53
|
+
'schmancy-details',
|
|
54
|
+
'schmancy-card-content',
|
|
55
|
+
]
|
|
56
|
+
const registered = sampleTags.filter(tag => customElements.get(tag) !== undefined)
|
|
57
|
+
expect(registered).toEqual(sampleTags)
|
|
53
58
|
})
|
|
54
59
|
|
|
55
|
-
it('
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
attributes?: Array<{ name: string; values?: string[] }>
|
|
63
|
-
}
|
|
64
|
-
expect(btn).toBeTruthy()
|
|
65
|
-
expect(btn.attributes?.length ?? 0).toBeGreaterThan(0)
|
|
66
|
-
const variant = btn.attributes?.find(a => a.name === 'variant')
|
|
67
|
-
expect(variant?.values).toContain('filled')
|
|
68
|
-
|
|
69
|
-
skill.remove()
|
|
60
|
+
it('exports the imperative service surface', async () => {
|
|
61
|
+
const mod = await import('./agent-entry')
|
|
62
|
+
expect(typeof mod.$dialog).toBe('object')
|
|
63
|
+
expect(typeof mod.$notify).toBe('object')
|
|
64
|
+
expect(typeof mod.sheet).toBe('object')
|
|
65
|
+
expect(typeof mod.theme).toBe('object')
|
|
66
|
+
expect(typeof mod.area).toBe('object')
|
|
70
67
|
})
|
|
71
68
|
|
|
72
|
-
it('`window.schmancy
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
const caps = window.schmancy!.capabilities()
|
|
79
|
-
expect(caps).toMatchObject({
|
|
80
|
-
popover: expect.any(Boolean),
|
|
81
|
-
declarativeShadowDom: expect.any(Boolean),
|
|
82
|
-
scopedRegistries: expect.any(Boolean),
|
|
83
|
-
trustedTypes: expect.any(Boolean),
|
|
84
|
-
cssRegisteredProperties: expect.any(Boolean),
|
|
85
|
-
elementInternalsAria: expect.any(Boolean),
|
|
86
|
-
formAssociated: expect.any(Boolean),
|
|
87
|
-
adoptedStyleSheets: expect.any(Boolean),
|
|
88
|
-
})
|
|
89
|
-
|
|
90
|
-
skill.remove()
|
|
69
|
+
it('does not install the legacy `window.schmancy` runtime surface', () => {
|
|
70
|
+
// Negative test — the runtime introspection layer was removed
|
|
71
|
+
// because no shipping consumer used it. If it comes back unintentionally,
|
|
72
|
+
// flag it here so the regression is visible.
|
|
73
|
+
expect((window as { schmancy?: unknown }).schmancy).toBeUndefined()
|
|
91
74
|
})
|
|
92
75
|
})
|
package/src/agent/agent-entry.ts
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent bundle entry — the single-URL ESM distribution.
|
|
3
|
+
*
|
|
4
|
+
* Importing this module side-effect-registers every `<schmancy-*>` custom
|
|
5
|
+
* element and exposes the imperative service surface ($dialog, sheet,
|
|
6
|
+
* $notify, etc.). The import is meant for standalone HTML files and
|
|
7
|
+
* one-page demos that want schmancy without a bundler.
|
|
8
|
+
*
|
|
9
|
+
* **Not** an agent-introspection runtime. Earlier versions installed a
|
|
10
|
+
* `window.schmancy` object with `help()` / `tokens()` / `findFor()` for
|
|
11
|
+
* a hypothetical "live agent in a browser" consumer that never materialised
|
|
12
|
+
* in any shipping product. AI consumers (Claude Design's source ingestion,
|
|
13
|
+
* IDE tooling, build pipelines) read the static manifest at
|
|
14
|
+
* `dist/agent/schmancy.manifest.json` — that's the actual integration point.
|
|
15
|
+
*
|
|
16
|
+
* The bundle stays as a distribution convenience; the runtime introspection
|
|
17
|
+
* surface was removed in favour of static-data consumption.
|
|
18
|
+
*/
|
|
1
19
|
import '../index'
|
|
2
|
-
import './schmancy-skill'
|
|
3
20
|
|
|
4
21
|
export {
|
|
5
22
|
$dialog,
|
|
@@ -1,4 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent bundle entry — the single-URL ESM distribution.
|
|
3
|
+
*
|
|
4
|
+
* Importing this module side-effect-registers every `<schmancy-*>` custom
|
|
5
|
+
* element and exposes the imperative service surface ($dialog, sheet,
|
|
6
|
+
* $notify, etc.). The import is meant for standalone HTML files and
|
|
7
|
+
* one-page demos that want schmancy without a bundler.
|
|
8
|
+
*
|
|
9
|
+
* **Not** an agent-introspection runtime. Earlier versions installed a
|
|
10
|
+
* `window.schmancy` object with `help()` / `tokens()` / `findFor()` for
|
|
11
|
+
* a hypothetical "live agent in a browser" consumer that never materialised
|
|
12
|
+
* in any shipping product. AI consumers (Claude Design's source ingestion,
|
|
13
|
+
* IDE tooling, build pipelines) read the static manifest at
|
|
14
|
+
* `dist/agent/schmancy.manifest.json` — that's the actual integration point.
|
|
15
|
+
*
|
|
16
|
+
* The bundle stays as a distribution convenience; the runtime introspection
|
|
17
|
+
* surface was removed in favour of static-data consumption.
|
|
18
|
+
*/
|
|
1
19
|
import '../index';
|
|
2
|
-
import './schmancy-skill';
|
|
3
20
|
export { $dialog, $notify, sheet, SchmancySheetPosition, schmancyContentDrawer, theme, area, lazy, createContext, select, selectItem, } from '../index';
|
|
4
21
|
export { $LitElement } from '../../mixins/index';
|
|
@@ -34,6 +34,7 @@ export type ElementEntry = {
|
|
|
34
34
|
name: string;
|
|
35
35
|
description?: string;
|
|
36
36
|
}>;
|
|
37
|
+
examples?: string[];
|
|
37
38
|
whenToUse?: string;
|
|
38
39
|
platformPrimitive?: {
|
|
39
40
|
tag: string;
|
|
@@ -58,6 +59,31 @@ export type ServiceEntry = {
|
|
|
58
59
|
};
|
|
59
60
|
export declare function help(tag?: string): unknown;
|
|
60
61
|
export declare function tokens(): string[];
|
|
62
|
+
export type FindForResult = {
|
|
63
|
+
tag: string;
|
|
64
|
+
score: number;
|
|
65
|
+
summary?: string;
|
|
66
|
+
examples?: string[];
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* Keyword search over the component manifest. Tokenizes the query the same
|
|
70
|
+
* way each component's `summary + description + examples` were tokenized,
|
|
71
|
+
* and returns the components with the most overlap. Tag-name token matches
|
|
72
|
+
* count for 3× a body-text match.
|
|
73
|
+
*
|
|
74
|
+
* Designed to catch the "I'm reaching for a custom component, what does
|
|
75
|
+
* schmancy ship?" gap without bringing in a vector-embedding dependency.
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* window.schmancy.findFor('status pill')
|
|
79
|
+
* // → [{ tag: 'schmancy-badge', score: 4, summary: '…', examples: [...] }]
|
|
80
|
+
*
|
|
81
|
+
* window.schmancy.findFor('initials avatar')
|
|
82
|
+
* // → [{ tag: 'schmancy-avatar', score: 5, ... }]
|
|
83
|
+
*
|
|
84
|
+
* window.schmancy.findFor('xyz123nonexistent') // → []
|
|
85
|
+
*/
|
|
86
|
+
export declare function findFor(query: string, limit?: number): FindForResult[];
|
|
61
87
|
export declare function platformPrimitive(tag: string): ElementEntry['platformPrimitive'] | null;
|
|
62
88
|
export declare function registeredTags(): string[];
|
|
63
89
|
export declare function a11yAudit(): Array<{
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a11yAudit, capabilities, help, manifest, platformPrimitive, registeredTags, tokens } from './helpers';
|
|
1
|
+
import { a11yAudit, capabilities, findFor, help, manifest, platformPrimitive, registeredTags, tokens } from './helpers';
|
|
2
2
|
declare global {
|
|
3
3
|
interface Window {
|
|
4
4
|
schmancy?: {
|
|
@@ -10,6 +10,7 @@ declare global {
|
|
|
10
10
|
registeredTags: typeof registeredTags;
|
|
11
11
|
a11yAudit: typeof a11yAudit;
|
|
12
12
|
capabilities: typeof capabilities;
|
|
13
|
+
findFor: typeof findFor;
|
|
13
14
|
};
|
|
14
15
|
}
|
|
15
16
|
}
|
package/src/agent/helpers.ts
DELETED
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
import manifest from 'virtual:schmancy-manifest'
|
|
2
|
-
|
|
3
|
-
export type Manifest = typeof manifest
|
|
4
|
-
|
|
5
|
-
export type ElementEntry = {
|
|
6
|
-
kind: 'class'
|
|
7
|
-
name: string
|
|
8
|
-
tagName?: string
|
|
9
|
-
description?: string
|
|
10
|
-
summary?: string
|
|
11
|
-
attributes?: Array<{
|
|
12
|
-
name: string
|
|
13
|
-
description?: string
|
|
14
|
-
type?: { text?: string }
|
|
15
|
-
default?: string
|
|
16
|
-
values?: string[]
|
|
17
|
-
}>
|
|
18
|
-
events?: Array<{ name: string; description?: string; type?: { text?: string } }>
|
|
19
|
-
slots?: Array<{ name: string; description?: string }>
|
|
20
|
-
cssProperties?: Array<{ name: string; description?: string }>
|
|
21
|
-
cssParts?: Array<{ name: string; description?: string }>
|
|
22
|
-
whenToUse?: string
|
|
23
|
-
platformPrimitive?: { tag: string; mode?: string; note?: string }
|
|
24
|
-
contexts?: { provides?: string[]; consumes?: string[] }
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export type ServiceEntry = {
|
|
28
|
-
kind: 'variable'
|
|
29
|
-
name: string
|
|
30
|
-
description?: string
|
|
31
|
-
summary?: string
|
|
32
|
-
service: true
|
|
33
|
-
methods?: Array<{ signature: string; summary?: string }>
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
type Declaration = ElementEntry | ServiceEntry | { kind: string; name: string; [key: string]: unknown }
|
|
37
|
-
|
|
38
|
-
function allDeclarations(): Declaration[] {
|
|
39
|
-
const out: Declaration[] = []
|
|
40
|
-
for (const mod of manifest.modules ?? []) {
|
|
41
|
-
const decls = (mod as unknown as { declarations?: Declaration[] }).declarations ?? []
|
|
42
|
-
for (const decl of decls) out.push(decl)
|
|
43
|
-
}
|
|
44
|
-
return out
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function elements(): ElementEntry[] {
|
|
48
|
-
return allDeclarations().filter(
|
|
49
|
-
(d): d is ElementEntry => d.kind === 'class' && typeof (d as ElementEntry).tagName === 'string',
|
|
50
|
-
)
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function services(): ServiceEntry[] {
|
|
54
|
-
return allDeclarations().filter((d): d is ServiceEntry => d.kind === 'variable' && (d as ServiceEntry).service === true)
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export function help(tag?: string): unknown {
|
|
58
|
-
if (!tag) {
|
|
59
|
-
return {
|
|
60
|
-
elements: elements().map(e => ({ tag: e.tagName, summary: e.summary ?? e.description })),
|
|
61
|
-
services: services().map(s => ({ name: s.name, summary: s.summary ?? s.description })),
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
const el = elements().find(e => e.tagName === tag)
|
|
65
|
-
if (el) return el
|
|
66
|
-
const svc = services().find(s => s.name === tag)
|
|
67
|
-
if (svc) return svc
|
|
68
|
-
return null
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export function tokens(): string[] {
|
|
72
|
-
return (manifest as { tokens?: string[] }).tokens ?? []
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export function platformPrimitive(tag: string): ElementEntry['platformPrimitive'] | null {
|
|
76
|
-
return elements().find(e => e.tagName === tag)?.platformPrimitive ?? null
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
export function registeredTags(): string[] {
|
|
80
|
-
return elements()
|
|
81
|
-
.map(e => e.tagName!)
|
|
82
|
-
.filter(tag => typeof customElements.get(tag) !== 'undefined')
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
export function a11yAudit(): Array<{
|
|
86
|
-
tag: string
|
|
87
|
-
role: string | null
|
|
88
|
-
ariaLabel: string | null
|
|
89
|
-
hasShadowRoot: boolean
|
|
90
|
-
formAssociated: boolean
|
|
91
|
-
}> {
|
|
92
|
-
const tags = new Set(elements().map(e => e.tagName!))
|
|
93
|
-
const report: Array<{
|
|
94
|
-
tag: string
|
|
95
|
-
role: string | null
|
|
96
|
-
ariaLabel: string | null
|
|
97
|
-
hasShadowRoot: boolean
|
|
98
|
-
formAssociated: boolean
|
|
99
|
-
}> = []
|
|
100
|
-
const all = document.querySelectorAll('*')
|
|
101
|
-
for (const node of Array.from(all)) {
|
|
102
|
-
const tag = node.tagName.toLowerCase()
|
|
103
|
-
if (!tags.has(tag)) continue
|
|
104
|
-
const ctor = customElements.get(tag) as (CustomElementConstructor & { formAssociated?: boolean }) | undefined
|
|
105
|
-
report.push({
|
|
106
|
-
tag,
|
|
107
|
-
role: node.getAttribute('role'),
|
|
108
|
-
ariaLabel: node.getAttribute('aria-label'),
|
|
109
|
-
hasShadowRoot: Boolean((node as Element & { shadowRoot?: ShadowRoot | null }).shadowRoot),
|
|
110
|
-
formAssociated: Boolean(ctor?.formAssociated),
|
|
111
|
-
})
|
|
112
|
-
}
|
|
113
|
-
return report
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
export type Capabilities = {
|
|
117
|
-
popover: boolean
|
|
118
|
-
declarativeShadowDom: boolean
|
|
119
|
-
scopedRegistries: boolean
|
|
120
|
-
trustedTypes: boolean
|
|
121
|
-
cssRegisteredProperties: boolean
|
|
122
|
-
elementInternalsAria: boolean
|
|
123
|
-
formAssociated: boolean
|
|
124
|
-
adoptedStyleSheets: boolean
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Runtime feature probe. Tells an agent which platform capabilities the
|
|
129
|
-
* current sandbox exposes, so it can adapt without assuming the sandbox CSP
|
|
130
|
-
* or browser version. Every check is feature-detect, not UA-sniff.
|
|
131
|
-
*/
|
|
132
|
-
export function capabilities(): Capabilities {
|
|
133
|
-
const anyTemplate = typeof HTMLTemplateElement !== 'undefined' ? HTMLTemplateElement.prototype : null
|
|
134
|
-
const anyElInternals = typeof ElementInternals !== 'undefined' ? ElementInternals.prototype : null
|
|
135
|
-
return {
|
|
136
|
-
popover: typeof HTMLElement !== 'undefined' && 'popover' in HTMLElement.prototype,
|
|
137
|
-
declarativeShadowDom: Boolean(anyTemplate && 'shadowRootMode' in anyTemplate),
|
|
138
|
-
scopedRegistries: (() => {
|
|
139
|
-
try {
|
|
140
|
-
// Native scoped registries require a constructible CustomElementRegistry.
|
|
141
|
-
// Pre-Safari-26/Chrome-146, the constructor throws Illegal constructor.
|
|
142
|
-
new (window as { CustomElementRegistry?: new () => unknown }).CustomElementRegistry!()
|
|
143
|
-
return true
|
|
144
|
-
} catch {
|
|
145
|
-
return false
|
|
146
|
-
}
|
|
147
|
-
})(),
|
|
148
|
-
trustedTypes: typeof (globalThis as { trustedTypes?: unknown }).trustedTypes !== 'undefined',
|
|
149
|
-
cssRegisteredProperties: typeof (window as { CSS?: { registerProperty?: unknown } }).CSS?.registerProperty === 'function',
|
|
150
|
-
elementInternalsAria: Boolean(anyElInternals && 'role' in anyElInternals),
|
|
151
|
-
formAssociated: typeof ElementInternals !== 'undefined',
|
|
152
|
-
adoptedStyleSheets: typeof Document !== 'undefined' && 'adoptedStyleSheets' in Document.prototype,
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
export function manifestUrl(): string {
|
|
157
|
-
const blob = new Blob([JSON.stringify(manifest)], { type: 'application/json' })
|
|
158
|
-
return URL.createObjectURL(blob)
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
export { manifest }
|
package/src/agent/index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './schmancy-skill'
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import { $LitElement } from '../../mixins/index'
|
|
2
|
-
import { html } from 'lit'
|
|
3
|
-
import { customElement } from 'lit/decorators.js'
|
|
4
|
-
import {
|
|
5
|
-
a11yAudit,
|
|
6
|
-
capabilities,
|
|
7
|
-
help,
|
|
8
|
-
manifest,
|
|
9
|
-
manifestUrl,
|
|
10
|
-
platformPrimitive,
|
|
11
|
-
registeredTags,
|
|
12
|
-
tokens,
|
|
13
|
-
} from './helpers'
|
|
14
|
-
|
|
15
|
-
declare global {
|
|
16
|
-
interface Window {
|
|
17
|
-
schmancy?: {
|
|
18
|
-
manifest: typeof manifest
|
|
19
|
-
manifestUrl: string
|
|
20
|
-
help: typeof help
|
|
21
|
-
tokens: typeof tokens
|
|
22
|
-
platformPrimitive: typeof platformPrimitive
|
|
23
|
-
registeredTags: typeof registeredTags
|
|
24
|
-
a11yAudit: typeof a11yAudit
|
|
25
|
-
capabilities: typeof capabilities
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
let installedUrl: string | null = null
|
|
31
|
-
|
|
32
|
-
function install() {
|
|
33
|
-
if (typeof window === 'undefined') return
|
|
34
|
-
if (installedUrl) return
|
|
35
|
-
installedUrl = manifestUrl()
|
|
36
|
-
window.schmancy = {
|
|
37
|
-
manifest,
|
|
38
|
-
manifestUrl: installedUrl,
|
|
39
|
-
help,
|
|
40
|
-
tokens,
|
|
41
|
-
platformPrimitive,
|
|
42
|
-
registeredTags,
|
|
43
|
-
a11yAudit,
|
|
44
|
-
capabilities,
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Self-describing runtime helper. Drop `<schmancy-skill></schmancy-skill>`
|
|
50
|
-
* once on a page and `window.schmancy.help('schmancy-button')` returns the
|
|
51
|
-
* machine-readable entry for any tag. Renders nothing.
|
|
52
|
-
*
|
|
53
|
-
* @element schmancy-skill
|
|
54
|
-
*/
|
|
55
|
-
@customElement('schmancy-skill')
|
|
56
|
-
export class SchmancySkill extends $LitElement() {
|
|
57
|
-
connectedCallback() {
|
|
58
|
-
super.connectedCallback()
|
|
59
|
-
install()
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
render() {
|
|
63
|
-
return html``
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
declare global {
|
|
68
|
-
interface HTMLElementTagNameMap {
|
|
69
|
-
'schmancy-skill': SchmancySkill
|
|
70
|
-
}
|
|
71
|
-
}
|