@hitslop/cli 0.1.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/README.md +21 -0
- package/dist/cli.js +1340 -0
- package/package.json +43 -0
- package/templates/svelte/.agents/skills/hitslop-authoring/SKILL.md +47 -0
- package/templates/svelte/.agents/skills/hitslop-authoring/references/storage-and-packages.md +22 -0
- package/templates/svelte/.agents/skills/hitslop-authoring/references/workflow.md +46 -0
- package/templates/svelte/.agents/skills/hitslop-design/SKILL.md +62 -0
- package/templates/svelte/.agents/skills/hitslop-design/references/bits-ui-styling.md +95 -0
- package/templates/svelte/.agents/skills/hitslop-design/references/object-families.md +37 -0
- package/templates/svelte/.agents/skills/hitslop-design/references/presentation-and-export.md +42 -0
- package/templates/svelte/AGENTS.md +18 -0
- package/templates/svelte/assets/theme.css +10 -0
- package/templates/svelte/index.html +2 -0
- package/templates/svelte/schema.ts +5 -0
- package/templates/svelte/src/App.svelte +32 -0
- package/templates/svelte/src/main.ts +9 -0
- package/templates/svelte/src/styles.css.ts +45 -0
- package/templates/svelte/src/theme-contract.css.ts +12 -0
- package/templates/svelte/vite.config.ts +5 -0
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@hitslop/cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Create, develop, build, register, and publish hitSlop apps.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/hitslop/hitslop.git",
|
|
9
|
+
"directory": "packages/cli"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://hitslop.app",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/hitslop/hitslop/issues"
|
|
14
|
+
},
|
|
15
|
+
"keywords": ["hitslop", "cli", "local-first", "mini-apps"],
|
|
16
|
+
"publishConfig": {
|
|
17
|
+
"access": "public"
|
|
18
|
+
},
|
|
19
|
+
"type": "module",
|
|
20
|
+
"bin": {
|
|
21
|
+
"slop": "./dist/cli.js"
|
|
22
|
+
},
|
|
23
|
+
"files": ["dist", "templates"],
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "bun ../../scripts/clean-dist.ts && bun build src/cli.ts --target bun --format esm --packages external --outfile dist/cli.js && /bin/chmod +x dist/cli.js",
|
|
26
|
+
"check": "tsc -p tsconfig.json",
|
|
27
|
+
"test": "bun run --cwd ../schema build && bun test"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@crustjs/core": "^0.0.19",
|
|
31
|
+
"@hitslop/schema": "0.1.0",
|
|
32
|
+
"@noble/ed25519": "^3.2.0",
|
|
33
|
+
"fast-png": "^8.0.0",
|
|
34
|
+
"fflate": "^0.8.3",
|
|
35
|
+
"postcss": "^8.5.6",
|
|
36
|
+
"vite": "^8.2.2",
|
|
37
|
+
"zod": "^4.5.2"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@types/bun": "latest",
|
|
41
|
+
"typescript": "^7.0.2"
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: hitslop-authoring
|
|
3
|
+
description: Create, preview, validate, build, register, or publish hitSlop authoring projects with the TypeScript CLI. Use for manifests, storage choices, package boundaries, capture, identity, and release workflow.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# hitSlop authoring
|
|
7
|
+
|
|
8
|
+
Read `manifest.json` first. Work on the authored source project, never a built
|
|
9
|
+
runtime document.
|
|
10
|
+
|
|
11
|
+
## Workflow
|
|
12
|
+
|
|
13
|
+
1. Fix the single job, categories, and initial window size in the manifest.
|
|
14
|
+
2. Choose no storage, JSON, SQLite, named media, or a deliberate combination.
|
|
15
|
+
3. Develop against the disposable browser fake and test live plus static capture states.
|
|
16
|
+
4. Validate and build a source-free, store-free runtime package.
|
|
17
|
+
5. Register a local master, create a writable copy, and test reopen/export.
|
|
18
|
+
6. Publish one signed immutable artifact.
|
|
19
|
+
|
|
20
|
+
Use `bun slop <command> <path>` inside the hitSlop repository. In a scaffolded
|
|
21
|
+
project use its Bun scripts, `slop`, or `bunx @hitslop/cli`.
|
|
22
|
+
|
|
23
|
+
Read [references/workflow.md](references/workflow.md) for commands, capture,
|
|
24
|
+
identity, and the definition of done. Read
|
|
25
|
+
[references/storage-and-packages.md](references/storage-and-packages.md) before
|
|
26
|
+
adding persistence or changing the runtime boundary.
|
|
27
|
+
|
|
28
|
+
## Non-negotiable package rules
|
|
29
|
+
|
|
30
|
+
- Runtime packages contain `manifest.json`, generated `app.html`, optional
|
|
31
|
+
`data.schema.json`, the canonical document Agent Skill, optional immutable `assets/`, optional host-owned
|
|
32
|
+
`stores/` in writable documents, and optional `QuickLook/` images.
|
|
33
|
+
- Svelte JSON stores require `{ schema, initial }`; default-export the Zod 4
|
|
34
|
+
schema from root `schema.ts` and attach that same export to the store.
|
|
35
|
+
- Authored templates and published artifacts contain no stores, source,
|
|
36
|
+
dependencies, build caches, editable stylesheets, SQLite sidecars, or
|
|
37
|
+
Finder-managed `Icon\r`.
|
|
38
|
+
- Storage is implicit and ID-free. Never add storage declarations or release
|
|
39
|
+
versions to the manifest.
|
|
40
|
+
- Treat `dist/<slug>.slop` as generated output.
|
|
41
|
+
- Publishing captures `QuickLook/Preview.png`, produces an exact 512×512
|
|
42
|
+
`QuickLook/Icon.png`, and signs one immutable ZIP.
|
|
43
|
+
- Publisher ownership comes from the local Ed25519 identity; back it up with
|
|
44
|
+
`slop identity export`.
|
|
45
|
+
|
|
46
|
+
When creating or substantially revising an interface, also use the local
|
|
47
|
+
`hitslop-design` skill.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Storage and package boundary
|
|
2
|
+
|
|
3
|
+
Use no persistence for a pure calculation. Use JSON for one compact object
|
|
4
|
+
graph, SQLite for queryable collections and transactional changes, and named
|
|
5
|
+
media for a small set of known file roles.
|
|
6
|
+
|
|
7
|
+
JSON writes replace the value atomically and may use an expected revision.
|
|
8
|
+
For Svelte, define a Zod 4 schema in root `schema.ts` and pass it with the
|
|
9
|
+
explicit initial value to `jsonStore`. Keep new fields backward-readable. Group
|
|
10
|
+
related SQLite statements in one host transaction and parameterize values. Never copy live WAL
|
|
11
|
+
or SHM files. Replace/remove named media through the host rather than treating
|
|
12
|
+
it as an arbitrary filesystem.
|
|
13
|
+
|
|
14
|
+
A template contains immutable `manifest.json`, generated `app.html`, optional
|
|
15
|
+
`data.schema.json`, the canonical `.agents/skills/hitslop-document` skill,
|
|
16
|
+
optional `assets/`, and capture images when registered/published. A writable document may lazily add `stores/data.json`,
|
|
17
|
+
`stores/data.sqlite`, `stores/media/`, and `stores/theme.css`.
|
|
18
|
+
The macOS host may add Finder `Icon\r` metadata locally.
|
|
19
|
+
|
|
20
|
+
Never ship source, `node_modules`, `.hitslop`, `dist` nesting, authoring skills,
|
|
21
|
+
`style.css`, `document.json`, seed stores, SQLite sidecars, env files, keys,
|
|
22
|
+
or Finder metadata in a template or published artifact.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Authoring workflow
|
|
2
|
+
|
|
3
|
+
## Commands
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
bunx @hitslop/cli init my-slop
|
|
7
|
+
bun run dev
|
|
8
|
+
bun run validate
|
|
9
|
+
bun run build
|
|
10
|
+
bun run register
|
|
11
|
+
bun run publish
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
`dev` is a disposable browser UI preview with in-memory JSON and forgiving
|
|
15
|
+
SQLite/media stubs. A reload clears preview state. Register a local master and
|
|
16
|
+
open a writable copy for real persistence, external-file, and native-window behavior.
|
|
17
|
+
|
|
18
|
+
`build` emits `dist/<slug>.slop`. Inspect the result. `register` adds an
|
|
19
|
+
immutable master under `~/.hitslop/templates`; open it to create a writable
|
|
20
|
+
copy and test persistence there.
|
|
21
|
+
|
|
22
|
+
New builds include the canonical document skill. Add optional app-specific
|
|
23
|
+
instructions only in root `document-guide.md`; the build copies it to the
|
|
24
|
+
skill's single reference after validating its encoding and size.
|
|
25
|
+
|
|
26
|
+
## Capture
|
|
27
|
+
|
|
28
|
+
Call `ready()` after initial durable data and critical assets are usable. Test
|
|
29
|
+
the manifest viewport in live and `data-slop-capture="static"` states. Mark
|
|
30
|
+
editing controls `data-slop-export="hide"`. Keep output in normal document
|
|
31
|
+
flow so full-height PNG/PDF can see it.
|
|
32
|
+
|
|
33
|
+
An optional renderer-only icon target is a square 512px DOM element. Use
|
|
34
|
+
`capture.isRenderer()` so they never mount in the interactive app.
|
|
35
|
+
|
|
36
|
+
## Identity and publish
|
|
37
|
+
|
|
38
|
+
`slop identity show`, `set-name`, `export`, and `import` manage the local
|
|
39
|
+
publisher key. Never place private identity material in a project. Publish signs
|
|
40
|
+
one built/captured artifact; the registry assigns release numbers externally.
|
|
41
|
+
|
|
42
|
+
## Definition of done
|
|
43
|
+
|
|
44
|
+
Validate keyboard/focus/reduced motion, long and empty content, persistence
|
|
45
|
+
reopen, resize or skin hit testing, static/full-height export, icon
|
|
46
|
+
legibility, and the contents of the final runtime package.
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: hitslop-design
|
|
3
|
+
description: Design or refine hitSlop mini apps and documents with a purpose-led tactile language, responsive native-window behavior, transparent or PNG-skinned surfaces, and export-aware capture.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# hitSlop design
|
|
7
|
+
|
|
8
|
+
Read `manifest.json` first and design at its exact initial dimensions. A slop
|
|
9
|
+
is one complete digital object, not a small website.
|
|
10
|
+
|
|
11
|
+
If the project provides `_vibe/`, inspect it for visual direction. Treat those
|
|
12
|
+
images as inspiration only; do not copy them into source or runtime packages.
|
|
13
|
+
|
|
14
|
+
## Decide before styling
|
|
15
|
+
|
|
16
|
+
1. State the single job in one sentence.
|
|
17
|
+
2. Choose a dominant object family: Paper, Instrument, or Skin.
|
|
18
|
+
3. Identify the primary action/readout and persistent state.
|
|
19
|
+
4. Decide whether the window is standard/resizable, transparent, or PNG-skinned.
|
|
20
|
+
5. Define what live editing, static capture, and icon must show.
|
|
21
|
+
|
|
22
|
+
Read [references/object-families.md](references/object-families.md) for visual
|
|
23
|
+
patterns and anti-patterns. Read
|
|
24
|
+
[references/presentation-and-export.md](references/presentation-and-export.md)
|
|
25
|
+
for transparency, resizing, skins, responsive layout, capture, icon, PNG,
|
|
26
|
+
and PDF behavior.
|
|
27
|
+
|
|
28
|
+
## Core design rules
|
|
29
|
+
|
|
30
|
+
- Let the host window be the outer object boundary; do not wrap everything in a
|
|
31
|
+
generic app card or fake desktop window.
|
|
32
|
+
- Use hierarchy, spacing, typography, rules, and tonal contrast before adding
|
|
33
|
+
containers.
|
|
34
|
+
- Use one dominant neutral surface and one purpose-specific accent; reserve
|
|
35
|
+
extra color for meaningful state.
|
|
36
|
+
- Size the initial viewport around realistic default content. Compactness comes
|
|
37
|
+
from rhythm and hierarchy, never tiny type.
|
|
38
|
+
- Preserve critical actions at narrow sizes. Prefer container queries and
|
|
39
|
+
reflow to simply hiding features.
|
|
40
|
+
- Keep focus visible, labels concise, numbers tabular, contrast accessible, and
|
|
41
|
+
motion respectful of `prefers-reduced-motion`.
|
|
42
|
+
- Prefer direct editing and progressive disclosure. When interactive controls are
|
|
43
|
+
needed (dialogs, dropdowns, selects, sliders, segmented tabs, checkboxes, calendars,
|
|
44
|
+
popovers, tooltips), always use Bits UI headless primitives (`bits-ui`). Never home-make
|
|
45
|
+
custom modal overlays, focus traps, or range hacks. Read [references/bits-ui-styling.md](references/bits-ui-styling.md).
|
|
46
|
+
- Style Bits UI primitives via data attributes (`[data-dialog-overlay]`, `[data-select-trigger]`,
|
|
47
|
+
`[data-slider-track]`, `[data-state="checked"]`) and CSS custom properties
|
|
48
|
+
(`--slop-surface`, `--slop-accent`, `--slop-ink`). This ensures each slop retains its
|
|
49
|
+
authentic physical personality (Paper, Instrument, Skin) while remaining effortless
|
|
50
|
+
to restyle or re-theme at runtime.
|
|
51
|
+
- In new Svelte projects, keep structural styles in Vanilla Extract `.css.ts`
|
|
52
|
+
files and bind public `--slop-*` variables with `createGlobalThemeContract`.
|
|
53
|
+
Keep the plain default values in `assets/theme.css`; do not compile the owner-editable
|
|
54
|
+
theme surface into generated class names.
|
|
55
|
+
- Mark editing-only UI with `data-slop-export="hide"`; keep exportable content
|
|
56
|
+
in normal flow.
|
|
57
|
+
- Make each slop purpose-specific. Shared SDK patterns must not make unrelated
|
|
58
|
+
objects look like one reskinned dashboard.
|
|
59
|
+
|
|
60
|
+
Runtime packages remain source-free: never ship authoring skills, editable CSS,
|
|
61
|
+
dependencies, build caches, or seed stores inside a `.slop`. The builder alone
|
|
62
|
+
adds the canonical document skill.
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# Bits UI Styling in hitSlop
|
|
2
|
+
|
|
3
|
+
hitSlop apps use **Bits UI** (`bits-ui`) as the standard primitive layer for interactive controls:
|
|
4
|
+
- Dialogs and Modals (`Dialog.Root`)
|
|
5
|
+
- Dropdowns and Selects (`Select.Root`)
|
|
6
|
+
- Sliders and Faders (`Slider.Root`)
|
|
7
|
+
- Checkboxes (`Checkbox.Root`)
|
|
8
|
+
- Segmented Views and Tabs (`Tabs.Root`)
|
|
9
|
+
- Date Pickers and Calendars (`Calendar.Root` + `Popover.Root`)
|
|
10
|
+
- Progress and Meters (`Progress.Root`)
|
|
11
|
+
- Tooltips (`Tooltip.Root`)
|
|
12
|
+
- Switches and Toggles (`Switch.Root`)
|
|
13
|
+
|
|
14
|
+
## Why Bits UI?
|
|
15
|
+
|
|
16
|
+
1. **Zero Default Styles**: Bits UI is completely headless. Unlike styled UI libraries that force a generic SaaS dashboard appearance, Bits UI lets each slop look like a real physical paper document, brass audio hardware, vintage receipt, or retro console.
|
|
17
|
+
2. **Accessibility Built-In**: Provides focus restoration, keyboard arrow navigation, screen reader ARIA roles, portal mounting, and scroll management out of the box.
|
|
18
|
+
3. **No Homemade Hacks**: Eliminates hand-rolled modal backdrops, broken focus traps, un-styled WebKit `<select>` dropdowns, and range input styling hacks.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Styling Architecture: Data Attributes & CSS Tokens
|
|
23
|
+
|
|
24
|
+
Bits UI components expose semantic HTML attributes (such as `data-state="open|closed"`, `data-orientation="vertical|horizontal"`, `data-disabled`, `data-selected`).
|
|
25
|
+
|
|
26
|
+
### 1. Data-Attribute Selectors
|
|
27
|
+
Always style Bits UI elements using their standard data attributes:
|
|
28
|
+
|
|
29
|
+
```css
|
|
30
|
+
/* Checkbox Root */
|
|
31
|
+
[data-checkbox-root] {
|
|
32
|
+
display: inline-flex;
|
|
33
|
+
align-items: center;
|
|
34
|
+
justify-content: center;
|
|
35
|
+
width: 18px;
|
|
36
|
+
height: 18px;
|
|
37
|
+
border-radius: 4px;
|
|
38
|
+
border: 1.5px solid var(--slop-rule);
|
|
39
|
+
background: var(--slop-surface);
|
|
40
|
+
cursor: pointer;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
[data-checkbox-root][data-state="checked"] {
|
|
44
|
+
background: var(--slop-accent);
|
|
45
|
+
border-color: var(--slop-accent);
|
|
46
|
+
color: #ffffff;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/* Vertical Slider (e.g. Audio Faders) */
|
|
50
|
+
[data-slider-root][data-orientation="vertical"] {
|
|
51
|
+
position: relative;
|
|
52
|
+
display: flex;
|
|
53
|
+
flex-direction: column;
|
|
54
|
+
align-items: center;
|
|
55
|
+
width: 28px;
|
|
56
|
+
height: 100%;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
[data-slider-thumb] {
|
|
60
|
+
cursor: grab;
|
|
61
|
+
outline: none;
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### 2. Runtime Re-Theming with CSS Variables
|
|
66
|
+
Define semantic tokens in `:root` so that updating a theme or switching color schemes (e.g., `html[data-theme="dark"]` or `html[data-theme="sepia"]`) instantly updates all Bits UI components:
|
|
67
|
+
|
|
68
|
+
```css
|
|
69
|
+
:root {
|
|
70
|
+
--slop-surface: #fdfbf7;
|
|
71
|
+
--slop-panel: #f3efe6;
|
|
72
|
+
--slop-ink: #1e293b;
|
|
73
|
+
--slop-muted: #64748b;
|
|
74
|
+
--slop-accent: #2563eb;
|
|
75
|
+
--slop-rule: rgba(0, 0, 0, 0.12);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
[data-theme="dark"] {
|
|
79
|
+
--slop-surface: #0f172a;
|
|
80
|
+
--slop-panel: #1e293b;
|
|
81
|
+
--slop-ink: #f8fafc;
|
|
82
|
+
--slop-muted: #94a3b8;
|
|
83
|
+
--slop-accent: #38bdf8;
|
|
84
|
+
--slop-rule: rgba(255, 255, 255, 0.12);
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## Modals & Portals
|
|
91
|
+
When using `Dialog.Portal`:
|
|
92
|
+
- Mounts outside normal DOM flow into `<body>`.
|
|
93
|
+
- Style the `[data-dialog-overlay]` with `position: fixed; inset: 0; backdrop-filter: blur(...);`.
|
|
94
|
+
- Style `[data-dialog-content]` with `position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%);`.
|
|
95
|
+
- Mark both with `data-slop-export="hide"` to exclude them from print/export capture.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Object families
|
|
2
|
+
|
|
3
|
+
## Paper
|
|
4
|
+
|
|
5
|
+
Use for an artifact people read, edit, share, print, or export: invoice, recipe,
|
|
6
|
+
résumé, planner, notes. Favor editorial hierarchy, direct editing, generous
|
|
7
|
+
reading rhythm, fine rules, and a flat export state. Editing affordances can be
|
|
8
|
+
quiet, but focus and selection must remain obvious.
|
|
9
|
+
|
|
10
|
+
Avoid placing the paper inside another rounded card. Avoid permanent toolbars in
|
|
11
|
+
the export. Do not use fixed-height nested scrollers for printable content.
|
|
12
|
+
|
|
13
|
+
## Instrument
|
|
14
|
+
|
|
15
|
+
Use for a compact control surface: timer, tracker, picker, mixer, calculator.
|
|
16
|
+
Prioritize one live readout or action, group related controls like hardware,
|
|
17
|
+
make modes/state unmistakable, and use restrained depth only to communicate
|
|
18
|
+
operation.
|
|
19
|
+
|
|
20
|
+
Avoid dashboard grids, decorative gauges, excessive status pills, and many
|
|
21
|
+
equal-weight actions.
|
|
22
|
+
|
|
23
|
+
## Skin
|
|
24
|
+
|
|
25
|
+
Use when silhouette is essential to recognition or delight: radio, pocket
|
|
26
|
+
device, novelty utility. The exact-size RGBA PNG is both backing and native
|
|
27
|
+
hit-test mask. Keep controls inside the solid safe area and verify pointer
|
|
28
|
+
behavior around holes and feathered alpha.
|
|
29
|
+
|
|
30
|
+
Avoid a skin when rounded/capsule CSS already communicates the object. A skin is
|
|
31
|
+
fixed-size and trades flexibility for a meaningful silhouette.
|
|
32
|
+
|
|
33
|
+
## Hybrids
|
|
34
|
+
|
|
35
|
+
Choose one dominant family and borrow one supporting behavior. A recipe may be
|
|
36
|
+
Paper with an Instrument timer; an invoice may adopt a receipt Skin. The job,
|
|
37
|
+
not visual novelty, decides.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Presentation and export
|
|
2
|
+
|
|
3
|
+
## Standard and responsive
|
|
4
|
+
|
|
5
|
+
Manifest width/height are the initial viewport. Standard windows may be
|
|
6
|
+
resizable and shaped `rounded`, `ellipse`, or `capsule`. Build the outer
|
|
7
|
+
layout with grid/flex, relative units, min/max constraints, and container
|
|
8
|
+
queries. Test initial, narrower, and content-heavy states.
|
|
9
|
+
|
|
10
|
+
An unskinned slop may request `slop.window.resize({ width, height })`; use the
|
|
11
|
+
returned host-applied size. Never assume the screen can fit the request.
|
|
12
|
+
|
|
13
|
+
## Transparent backgrounds
|
|
14
|
+
|
|
15
|
+
Set `html`, `body`, and the app root transparent, then paint only the visible
|
|
16
|
+
object. Transparency does not make pixels click-through. Keep focus rings and
|
|
17
|
+
content within the painted surface. Use an exact RGBA PNG skin when native
|
|
18
|
+
alpha-shaped hit testing is required.
|
|
19
|
+
|
|
20
|
+
## PNG skins
|
|
21
|
+
|
|
22
|
+
The skin path is under `assets/`; PNG dimensions must exactly equal manifest
|
|
23
|
+
dimensions and include alpha. Pixels below 10% alpha are click-through. Skinned
|
|
24
|
+
windows cannot resize. Avoid critical controls on antialiased/translucent edges.
|
|
25
|
+
|
|
26
|
+
## Static output
|
|
27
|
+
|
|
28
|
+
During static capture the root has `data-slop-capture="static"`. Add
|
|
29
|
+
`data-slop-export="hide"` to editing-only controls and use capture CSS for
|
|
30
|
+
necessary flattening. Do not hide the content those controls manipulate.
|
|
31
|
+
|
|
32
|
+
Preview preserves manifest dimensions. PNG/PDF export uses current width and
|
|
33
|
+
full document height; PNG is deterministic 2× and PDF keeps selectable text/
|
|
34
|
+
vectors. Keep export content in normal flow, not nested scroll panels.
|
|
35
|
+
|
|
36
|
+
## Icon
|
|
37
|
+
|
|
38
|
+
Mount at most one 512×512 `data-slop-render="icon"` target only when
|
|
39
|
+
`capture.isRenderer()` is true. Reveal it only in icon capture mode. The icon
|
|
40
|
+
should communicate the job with a strong silhouette, safe margins, and no
|
|
41
|
+
essential small text. Catalog detail uses the full preview; Finder and compact
|
|
42
|
+
catalog rows use the icon.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# hitSlop authoring
|
|
2
|
+
|
|
3
|
+
Read `manifest.json` first. Use the local `hitslop-authoring` skill for package,
|
|
4
|
+
storage, capture, install, and publish work. Use `hitslop-design` when creating
|
|
5
|
+
or revising the interface. Treat the host window as the outer object boundary
|
|
6
|
+
and size it for realistic default content. Use Bits UI (`bits-ui`) for interactive
|
|
7
|
+
controls (dialogs, selects, sliders, tabs, checkboxes, calendars, tooltips) rather
|
|
8
|
+
than home-making components. Style via data attributes and semantic CSS custom
|
|
9
|
+
properties so your slop retains its bespoke aesthetic and can be re-themed cleanly.
|
|
10
|
+
Use Vanilla Extract for structural `.css.ts` styles and a global theme contract;
|
|
11
|
+
keep the editable token defaults in plain `assets/theme.css`.
|
|
12
|
+
Preview both live and static states, then validate, build, install a writable test
|
|
13
|
+
copy, and publish only when ready. Browser development is a disposable UI
|
|
14
|
+
preview; test persistence in an installed writable copy. Attach the root Zod
|
|
15
|
+
schema to every Svelte JSON store. Source, skills, dependencies, editable
|
|
16
|
+
styles, secrets, and seed data never enter the runtime `.slop`. Build output
|
|
17
|
+
does include the canonical `hitslop-document` skill; optional app-specific
|
|
18
|
+
agent guidance belongs only in root `document-guide.md`.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
--slop-surface: #f7f4eb;
|
|
3
|
+
--slop-panel: #e3e9df;
|
|
4
|
+
--slop-control: #ede9df;
|
|
5
|
+
--slop-ink: #182126;
|
|
6
|
+
--slop-muted: #687276;
|
|
7
|
+
--slop-accent: #db6648;
|
|
8
|
+
--slop-rule: color-mix(in srgb, var(--slop-ink) 15%, transparent);
|
|
9
|
+
--slop-font: "Avenir Next", Avenir, sans-serif;
|
|
10
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { capture } from "@hitslop/runtime";
|
|
3
|
+
import { jsonStore } from "@hitslop/svelte";
|
|
4
|
+
import { Button } from "bits-ui";
|
|
5
|
+
import counterSchema from "../schema";
|
|
6
|
+
import * as styles from "./styles.css.ts";
|
|
7
|
+
|
|
8
|
+
const title = __SLOP_TITLE_LITERAL__;
|
|
9
|
+
const state = jsonStore({ schema: counterSchema, initial: { count: 0 } });
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
<main class={styles.main} data-slop-selection="none">
|
|
13
|
+
<section class={styles.counter} aria-labelledby="counter-title">
|
|
14
|
+
<header>
|
|
15
|
+
<span class={styles.eyebrow}>Quick counter</span>
|
|
16
|
+
<h1 class={styles.heading} id="counter-title">{title}</h1>
|
|
17
|
+
</header>
|
|
18
|
+
<div class={styles.readout}><output class={styles.output} aria-live="polite">{state.current.count}</output><span class={styles.readoutLabel}>things counted</span></div>
|
|
19
|
+
<div class={styles.controls} data-slop-export="hide">
|
|
20
|
+
<Button.Root class={styles.controlButton} onclick={() => state.current.count -= 1} aria-label="Decrease count">−</Button.Root>
|
|
21
|
+
<Button.Root class={`${styles.controlButton} ${styles.primaryButton}`} onclick={() => state.current.count += 1} aria-label="Increase count">+</Button.Root>
|
|
22
|
+
<Button.Root class={`${styles.controlButton} ${styles.resetButton}`} onclick={() => state.current.count = 0}>Reset</Button.Root>
|
|
23
|
+
</div>
|
|
24
|
+
{#if state.error}<p class={styles.error} data-slop-export="hide">Your latest change couldn’t be saved.</p>{/if}
|
|
25
|
+
</section>
|
|
26
|
+
</main>
|
|
27
|
+
|
|
28
|
+
{#if capture.isRenderer()}
|
|
29
|
+
<section class={styles.renderTarget} data-slop-render="icon" aria-hidden="true">
|
|
30
|
+
<span class={styles.iconTile}>#</span>
|
|
31
|
+
</section>
|
|
32
|
+
{/if}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ready } from "@hitslop/runtime";
|
|
2
|
+
import { mount } from "svelte";
|
|
3
|
+
import App from "./App.svelte";
|
|
4
|
+
import "./styles.css.ts";
|
|
5
|
+
|
|
6
|
+
const target = document.getElementById("app");
|
|
7
|
+
if (!target) throw new Error("Missing #app");
|
|
8
|
+
mount(App, { target });
|
|
9
|
+
ready();
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { globalStyle, style } from "@vanilla-extract/css";
|
|
2
|
+
import { theme } from "./theme-contract.css.ts";
|
|
3
|
+
|
|
4
|
+
globalStyle(":root", { colorScheme: "light", fontFamily: theme.font, fontSynthesis: "none" });
|
|
5
|
+
globalStyle("*", { boxSizing: "border-box" });
|
|
6
|
+
globalStyle("html, body, #app", { width: "100%", minHeight: "100%", margin: 0 });
|
|
7
|
+
globalStyle("body", { color: theme.ink, background: theme.surface });
|
|
8
|
+
globalStyle("button", { font: "inherit" });
|
|
9
|
+
globalStyle('html[data-slop-capture="static"] [data-slop-export="hide"]', { display: "none !important" });
|
|
10
|
+
globalStyle('html[data-slop-renderer="true"][data-slop-capture="icon"] main', { display: "none" });
|
|
11
|
+
globalStyle('html[data-slop-renderer="true"][data-slop-capture="icon"] [data-slop-render="icon"]', { display: "grid !important" });
|
|
12
|
+
globalStyle("*, *::before, *::after", {
|
|
13
|
+
"@media": { "(prefers-reduced-motion: reduce)": { scrollBehavior: "auto", transitionDuration: ".01ms" } },
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
export const main = style({ minHeight: "100vh", containerType: "inline-size", background: theme.surface });
|
|
17
|
+
export const counter = style({
|
|
18
|
+
display: "grid",
|
|
19
|
+
alignContent: "center",
|
|
20
|
+
minHeight: "100vh",
|
|
21
|
+
padding: "34px 38px",
|
|
22
|
+
"@container": { "(max-width: 360px)": { padding: "25px 23px" } },
|
|
23
|
+
});
|
|
24
|
+
globalStyle(`${counter} header`, { display: "grid", gap: 4 });
|
|
25
|
+
export const eyebrow = style({ margin: 0, color: theme.accent, fontSize: 10, fontWeight: 760, letterSpacing: ".1em", textTransform: "uppercase" });
|
|
26
|
+
export const heading = style({ margin: 0, fontSize: 25, fontWeight: 670, letterSpacing: "-.035em" });
|
|
27
|
+
export const readout = style({ margin: "25px 0 14px", border: `1px solid ${theme.rule}`, borderRadius: 12, padding: "25px 24px 21px", background: theme.panel });
|
|
28
|
+
export const output = style({ display: "block", fontSize: "clamp(70px, 20cqw, 94px)", fontWeight: 650, lineHeight: ".84", letterSpacing: "-.075em", fontVariantNumeric: "tabular-nums" });
|
|
29
|
+
export const readoutLabel = style({ display: "block", marginTop: 13, color: theme.muted, fontSize: 12 });
|
|
30
|
+
export const controls = style({ display: "grid", gridTemplateColumns: "1fr 1.25fr", gap: 9 });
|
|
31
|
+
export const controlButton = style({
|
|
32
|
+
minHeight: 48,
|
|
33
|
+
border: `1px solid ${theme.rule}`,
|
|
34
|
+
borderRadius: 10,
|
|
35
|
+
color: theme.ink,
|
|
36
|
+
background: theme.control,
|
|
37
|
+
cursor: "pointer",
|
|
38
|
+
fontSize: 23,
|
|
39
|
+
selectors: { "&:focus-visible": { outline: `3px solid color-mix(in srgb, ${theme.accent} 34%, transparent)`, outlineOffset: 2 } },
|
|
40
|
+
});
|
|
41
|
+
export const primaryButton = style({ borderColor: "transparent", color: theme.surface, background: theme.accent });
|
|
42
|
+
export const resetButton = style({ gridColumn: "1 / -1", minHeight: 30, border: 0, color: theme.muted, background: "transparent", fontSize: 10, fontWeight: 750, letterSpacing: ".08em", textTransform: "uppercase" });
|
|
43
|
+
export const error = style({ margin: "10px 0 0", color: "#9a2e27", fontSize: 11 });
|
|
44
|
+
export const renderTarget = style({ display: "none !important", width: 512, height: 512, placeItems: "center", overflow: "hidden", background: "transparent" });
|
|
45
|
+
export const iconTile = style({ display: "grid", placeItems: "center", width: 464, height: 464, border: `18px solid ${theme.ink}`, borderRadius: 72, color: theme.surface, background: theme.accent, fontSize: 250, fontWeight: 700 });
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { createGlobalThemeContract } from "@vanilla-extract/css";
|
|
2
|
+
|
|
3
|
+
export const theme = createGlobalThemeContract({
|
|
4
|
+
surface: "slop-surface",
|
|
5
|
+
panel: "slop-panel",
|
|
6
|
+
control: "slop-control",
|
|
7
|
+
ink: "slop-ink",
|
|
8
|
+
muted: "slop-muted",
|
|
9
|
+
accent: "slop-accent",
|
|
10
|
+
rule: "slop-rule",
|
|
11
|
+
font: "slop-font",
|
|
12
|
+
});
|