@telepath-computer/television 0.1.216 → 1.3.2
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/dist/THIRD-PARTY-NOTICES.txt +336 -0
- package/dist/canonical/v1/frozen.json +6 -0
- package/dist/canonical/v1/styles.css +7 -2
- package/dist/canonical/v2/components.js +1 -0
- package/dist/canonical/v2/fonts/Hind-Variable.933e9900.woff2 +0 -0
- package/dist/canonical/v2/styles.css +1153 -0
- package/dist/cli.cjs +1763 -761
- package/dist/onboarding/README.md +16 -14
- package/dist/onboarding/business-ops/about.html +4 -3
- package/dist/onboarding/business-ops/retention-vs-releases.html +25 -32
- package/dist/onboarding/business-ops/revenue-vs-goals.html +26 -31
- package/dist/onboarding/productivity/about.html +4 -3
- package/dist/onboarding/productivity/company-todos/THIRD-PARTY-NOTICES.txt +29 -0
- package/dist/onboarding/productivity/company-todos/index.html +20 -20
- package/dist/onboarding/productivity/company-todos/task.css +4 -5
- package/dist/onboarding/productivity/company-todos/task.js +1 -1
- package/dist/onboarding/productivity/meeting-prep.html +12 -12
- package/dist/onboarding/productivity/priorities-today.html +10 -10
- package/dist/onboarding/productivity/todays-calendar/calendar.css +1 -1
- package/dist/onboarding/productivity/todays-calendar/index.html +12 -14
- package/dist/onboarding/research/about.html +4 -3
- package/dist/onboarding/research/open-model-research.html +35 -35
- package/dist/onboarding/research/todays-news.html +72 -72
- package/dist/onboarding/tv-guide/welcome.html +178 -0
- package/dist/skills/television/SKILL.md +122 -67
- package/dist/skills/television/theming.md +1087 -0
- package/dist/skills/tv-calendar/SKILL.md +3 -1
- package/dist/skills/tv-calendar/calendar.css +1 -1
- package/dist/skills/tv-table/SKILL.md +5 -2
- package/dist/skills/tv-tasks/SKILL.md +19 -15
- package/dist/skills/tv-tasks/task.css +4 -26
- package/dist/skills/tv-tasks/task.js +1 -1
- package/dist/themes/README.md +5 -0
- package/dist/themes/aquarium/README.md +48 -0
- package/dist/themes/aquarium/iframe-background.js +137 -0
- package/dist/themes/aquarium/manifest.json +7 -0
- package/dist/themes/aquarium/theme.css +46 -0
- package/dist/themes/blueprint/README.md +31 -0
- package/dist/themes/blueprint/manifest.json +6 -0
- package/dist/themes/blueprint/theme.css +115 -0
- package/dist/themes/clouds/README.md +15 -0
- package/dist/themes/clouds/THIRD-PARTY-NOTICES.txt +81 -0
- package/dist/themes/clouds/manifest.json +6 -0
- package/dist/themes/clouds/theme.css +62 -0
- package/dist/themes/clouds/wallpaper-dark.webp +0 -0
- package/dist/themes/clouds/wallpaper.webp +0 -0
- package/dist/themes/crt-phosphor/README.md +31 -0
- package/dist/themes/crt-phosphor/manifest.json +6 -0
- package/dist/themes/crt-phosphor/theme.css +105 -0
- package/dist/themes/nord/README.md +9 -0
- package/dist/themes/nord/THIRD-PARTY-NOTICES.txt +29 -0
- package/dist/themes/nord/manifest.json +5 -0
- package/dist/themes/nord/theme.css +28 -0
- package/dist/themes/swiss/README.md +5 -0
- package/dist/themes/swiss/manifest.json +5 -0
- package/dist/themes/swiss/theme.css +34 -0
- package/dist/themes/tokyo-night/README.md +17 -0
- package/dist/themes/tokyo-night/THIRD-PARTY-NOTICES.txt +235 -0
- package/dist/themes/tokyo-night/manifest.json +5 -0
- package/dist/themes/tokyo-night/theme.css +31 -0
- package/dist/themes/tokyo-night/wallpaper.webp +0 -0
- package/dist/views/artifact-missing/index.html +40 -68
- package/dist/views/markdown/THIRD-PARTY-NOTICES.txt +26 -0
- package/dist/views/markdown/index.html +49 -23
- package/dist/web/THIRD-PARTY-NOTICES.txt +26 -0
- package/dist/web/assets/artifact-bridge-BEUWXKq7.js +1 -0
- package/dist/web/assets/{artifactMissing-BiK_l3Ts.js → artifactMissing-CUs1ZwDx.js} +1 -1
- package/dist/web/assets/main-3UimTRLK.css +1 -0
- package/dist/web/assets/main-CSSzbG7q.js +704 -0
- package/dist/web/assets/{urlUnsupported-RGIrhMDe.js → urlUnsupported-powR79PT.js} +1 -1
- package/dist/web/index.html +40 -5
- package/dist/web/views/artifact-missing/index.html +40 -68
- package/dist/web/views/url-unsupported/index.html +40 -68
- package/package.json +1 -1
- package/dist/onboarding/tv-guide/welcome/assets/television.svg +0 -71
- package/dist/onboarding/tv-guide/welcome/index.html +0 -203
- package/dist/skills/tv-theme/SKILL.md +0 -449
- package/dist/web/assets/artifact-bridge-3Trk-B2L.js +0 -1
- package/dist/web/assets/clouds-CAYIArXj.jpg +0 -0
- package/dist/web/assets/main-Blg8fuaV.css +0 -1
- package/dist/web/assets/main-V14r9Psb.js +0 -596
|
@@ -20,11 +20,13 @@ instead of referencing `/skills/tv-calendar/...` URLs.
|
|
|
20
20
|
For a Television HTML directory artifact, copy both files into the registered directory next to `index.html` and reference them relatively:
|
|
21
21
|
|
|
22
22
|
```html
|
|
23
|
-
<link rel="stylesheet" href="/canonical/
|
|
23
|
+
<link rel="stylesheet" href="/canonical/v2/styles.css?authoredForAppVersion=<version>" />
|
|
24
24
|
<link rel="stylesheet" href="./calendar.css" />
|
|
25
25
|
<script type="module" src="./calendar.js"></script>
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
+
For a running server, read the exact release `version` from `tv status`; when working in a Television checkout, read the exact version from the checkout root `package.json`. Copy that exact release version unchanged into `authoredForAppVersion`. A missing version or the `0.0.0` development sentinel does not identify a release, so omit the metadata.
|
|
29
|
+
|
|
28
30
|
For a standalone single-file HTML artifact, inline the contents of
|
|
29
31
|
`calendar.css` and `calendar.js` directly into `<style>` and
|
|
30
32
|
`<script type="module">` tags so the output stays self-contained.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
calendar-event{display:contents}calendar-event .event-block{pointer-events:none;z-index:2}calendar-event .event-block-inner{background:var(--event-bg-timed, var(--neutral-100));border-left:3px solid var(--event-border-color, var(--neutral-300));border-radius:4px;box-shadow:inset 0 0 0 .5px var(--neutral-200)}calendar-grid calendar-event .event-block{grid-column:calc(var(--day-index) + 1);grid-row:1 / -1;position:relative;z-index:0}calendar-grid calendar-event .event-block-inner{position:absolute;top:calc(var(--y-start) * 100%);height:calc((var(--y-end) - var(--y-start)) * 100%);left:calc(2px + var(--cascade-depth, 0) * 6px);right:2px;padding:var(--space-4) var(--space-8);overflow:hidden;pointer-events:auto;z-index:calc(1 + var(--cascade-depth, 0))}calendar-allday calendar-event .event-block{grid-column:calc(var(--day-start) + 1) / span var(--day-span);grid-row:var(--lane-index);padding:2px}calendar-allday calendar-event .event-block-inner{background:var(--event-bg-all-day, var(--event-bg-timed, var(--neutral-100)));border-left:var( --event-all-day-border, 3px solid var(--event-border-color, var(--neutral-300)) );padding:1px var(--space-8);display:flex;align-items:center;pointer-events:auto}calendar-event[color=red]{--event-bg-timed: var(--red-100);--event-bg-all-day: var(--red-200);--event-border-color: var(--red-500);--event-all-day-border: none}calendar-event[color=orange]{--event-bg-timed: var(--orange-100);--event-bg-all-day: var(--orange-200);--event-border-color: var(--orange-500);--event-all-day-border: none}calendar-event[color=yellow]{--event-bg-timed: var(--yellow-100);--event-bg-all-day: var(--yellow-200);--event-border-color: var(--yellow-500);--event-all-day-border: none}calendar-event[color=green]{--event-bg-timed: var(--green-100);--event-bg-all-day: var(--green-200);--event-border-color: var(--green-500);--event-all-day-border: none}calendar-event[color=blue]{--event-bg-timed: var(--blue-100);--event-bg-all-day: var(--blue-200);--event-border-color: var(--blue-500);--event-all-day-border: none}calendar-event[color=purple]{--event-bg-timed: var(--purple-100);--event-bg-all-day: var(--purple-200);--event-border-color: var(--purple-500);--event-all-day-border: none}calendar-event h3{margin:0;font-size:var(--text-sm);font-weight:600;color:var(--
|
|
1
|
+
calendar-event{display:contents}calendar-event .event-block{pointer-events:none;z-index:2}calendar-event .event-block-inner{background:var(--event-bg-timed, var(--neutral-100));border-left:3px solid var(--event-border-color, var(--neutral-300));border-radius:4px;box-shadow:inset 0 0 0 .5px var(--neutral-200)}calendar-grid calendar-event .event-block{grid-column:calc(var(--day-index) + 1);grid-row:1 / -1;position:relative;z-index:0}calendar-grid calendar-event .event-block-inner{position:absolute;top:calc(var(--y-start) * 100%);height:calc((var(--y-end) - var(--y-start)) * 100%);left:calc(2px + var(--cascade-depth, 0) * 6px);right:2px;padding:var(--space-4) var(--space-8);overflow:hidden;pointer-events:auto;z-index:calc(1 + var(--cascade-depth, 0))}calendar-allday calendar-event .event-block{grid-column:calc(var(--day-start) + 1) / span var(--day-span);grid-row:var(--lane-index);padding:2px}calendar-allday calendar-event .event-block-inner{background:var(--event-bg-all-day, var(--event-bg-timed, var(--neutral-100)));border-left:var( --event-all-day-border, 3px solid var(--event-border-color, var(--neutral-300)) );padding:1px var(--space-8);display:flex;align-items:center;pointer-events:auto}calendar-event[color=red]{--event-bg-timed: var(--red-100);--event-bg-all-day: var(--red-200);--event-border-color: var(--red-500);--event-all-day-border: none}calendar-event[color=orange]{--event-bg-timed: var(--orange-100);--event-bg-all-day: var(--orange-200);--event-border-color: var(--orange-500);--event-all-day-border: none}calendar-event[color=yellow]{--event-bg-timed: var(--yellow-100);--event-bg-all-day: var(--yellow-200);--event-border-color: var(--yellow-500);--event-all-day-border: none}calendar-event[color=green]{--event-bg-timed: var(--green-100);--event-bg-all-day: var(--green-200);--event-border-color: var(--green-500);--event-all-day-border: none}calendar-event[color=blue]{--event-bg-timed: var(--blue-100);--event-bg-all-day: var(--blue-200);--event-border-color: var(--blue-500);--event-all-day-border: none}calendar-event[color=purple]{--event-bg-timed: var(--purple-100);--event-bg-all-day: var(--purple-200);--event-border-color: var(--purple-500);--event-all-day-border: none}calendar-event h3{margin:0;font-size:var(--text-sm);font-weight:600;color:var(--neutral-900);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}calendar-time-axis{display:contents}calendar-time-axis .hour-label{grid-column:time-left / days-start;grid-row-start:calc(var(--hour-index) + 1);align-self:start;padding:var(--space-2) var(--space-8);font-size:round(calc(var(--text-base) * 10 / 14),1px);color:var(--color-text-muted);text-align:right;white-space:nowrap;transform:translateY(-50%)}calendar-time-axis .hour-label[data-hour="0"]{visibility:hidden;width:0;height:0;padding:0;overflow:hidden}calendar-week{display:grid;grid-template-columns:[time-left] auto [days-start] repeat(var(--day-count, 0),minmax(0,1fr)) [days-end];grid-template-rows:[headers] auto [allday] auto [hours-start] repeat(var(--hour-count, 0),minmax(var(--hour-min-height, 4rem),1fr)) [hours-end];width:100%;height:100%;overflow:auto;background:var(--color-surface);font-family:inherit;color:var(--color-text)}calendar-week>calendar-headers,calendar-week>calendar-allday,calendar-week>calendar-grid{display:grid;grid-column:1 / -1;grid-template-columns:subgrid;min-width:0}calendar-week>calendar-headers{grid-row:headers / allday;position:sticky;top:0;z-index:3;background:var(--color-surface);border-top:.5px solid var(--color-border);border-bottom:1px solid var(--color-border)}calendar-headers>calendar-day{grid-column:calc(var(--day-index) + 1);display:flex;align-items:baseline;gap:var(--space-2);padding:var(--space-12);font-size:var(--text-sm);border-left:.5px solid var(--color-border)}calendar-headers>calendar-day:last-of-type{border-right:.5px solid var(--color-border)}calendar-headers>calendar-day .weekday{text-transform:uppercase;letter-spacing:.04em;color:var(--color-text-muted)}calendar-headers>calendar-day .date-num{color:var(--color-text)}calendar-week>calendar-allday{grid-row:allday / hours-start;grid-template-rows:repeat(var(--allday-lane-count, 1),auto);position:sticky;top:var(--headers-height, 0px);z-index:2;border-bottom:1px solid var(--color-border);background:var(--color-surface)}calendar-allday>calendar-cell{grid-column:calc(var(--day-index) + 1);grid-row:1;border-left:.5px solid var(--color-border)}calendar-allday>calendar-cell:last-of-type{border-right:.5px solid var(--color-border)}calendar-week>calendar-grid{grid-row:hours-start / hours-end;grid-template-rows:subgrid;position:relative}calendar-grid>calendar-column{grid-column:calc(var(--day-index) + 1);grid-row:1 / -1;border-left:.5px solid var(--color-border);background-image:repeating-linear-gradient(to bottom,transparent 0,transparent calc(100% / var(--hour-count) - .5px),var(--color-border) calc(100% / var(--hour-count) - .5px),var(--color-border) calc(100% / var(--hour-count)))}calendar-grid>calendar-column:last-of-type{border-right:.5px solid var(--color-border)}calendar-grid>.start-hour-marker{grid-column:2;grid-row-start:calc(var(--start-hour) + 1);width:0;height:0}calendar-grid>.now-line-faint,calendar-grid>.now-line{grid-row:1 / -1;position:relative;pointer-events:none;z-index:1}calendar-grid>.now-line-faint{grid-column:2 / -1}calendar-grid>.now-line{grid-column:calc(var(--day-index) + 1)}calendar-grid>.now-line-faint:before,calendar-grid>.now-line:before{content:"";position:absolute;left:0;right:0;top:calc(var(--y-fraction) * 100%);height:1px;transform:translateY(-.5px)}calendar-grid>.now-line-faint:before{background:#e7202240}calendar-grid>.now-line:before{background:#e72022}calendar-grid>.now-line:after{content:"";position:absolute;left:0;top:calc(var(--y-fraction) * 100%);width:.5rem;height:.5rem;border-radius:50%;background:#e72022;transform:translate(-50%,-50%)}
|
|
@@ -5,7 +5,9 @@ description: Author dense Airtable-style HTML record tables using the shared Tel
|
|
|
5
5
|
|
|
6
6
|
# Authoring a record table
|
|
7
7
|
|
|
8
|
-
Know the main `television` skill first. Re-read it only if it is not already in your context or you know it changed. It defines the general Television workflow and the canonical HTML house style. This skill only adds table-specific guidance.
|
|
8
|
+
Know the main `television` skill first. Re-read it only if it is not already in your context or you know it changed. It defines the general Television workflow and the canonical HTML house style (`<link rel="stylesheet" href="/canonical/v2/styles.css?authoredForAppVersion=<version>" />`). This skill only adds table-specific guidance.
|
|
9
|
+
|
|
10
|
+
For a running server, read the exact release `version` from `tv status`; when working in a Television checkout, read the exact version from the checkout root `package.json`. Copy that exact release version unchanged into `authoredForAppVersion`. A missing version or the `0.0.0` development sentinel does not identify a release, so omit the metadata.
|
|
9
11
|
|
|
10
12
|
A dense, Airtable-style table of records. Lots of cells, mixed cell types,
|
|
11
13
|
structured rows. It should look crisp at a glance and read like real software,
|
|
@@ -48,7 +50,8 @@ Use this page structure:
|
|
|
48
50
|
</body>
|
|
49
51
|
```
|
|
50
52
|
|
|
51
|
-
|
|
53
|
+
Use `--color-surface` for the page background and `--color-text` for its text.
|
|
54
|
+
Hairline borders using `--color-border` around the thead and between rows do the visual work.
|
|
52
55
|
|
|
53
56
|
## Styling guidance
|
|
54
57
|
|
|
@@ -22,12 +22,14 @@ For a directory artifact, copy both next to `index.html` and load them with the
|
|
|
22
22
|
canonical boilerplate:
|
|
23
23
|
|
|
24
24
|
```html
|
|
25
|
-
<link rel="stylesheet" href="/canonical/
|
|
26
|
-
<script type="module" src="/canonical/
|
|
25
|
+
<link rel="stylesheet" href="/canonical/v2/styles.css?authoredForAppVersion=<version>" />
|
|
26
|
+
<script type="module" src="/canonical/v2/components.js"></script>
|
|
27
27
|
<link rel="stylesheet" href="./task.css" />
|
|
28
28
|
<script type="module" src="./task.js"></script>
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
+
For a running server, read the exact release `version` from `tv status`; when working in a Television checkout, read the exact version from the checkout root `package.json`. Copy that exact release version unchanged into `authoredForAppVersion`. A missing version or the `0.0.0` development sentinel does not identify a release, so omit the metadata.
|
|
32
|
+
|
|
31
33
|
For a single-file artifact, inline `task.css`/`task.js` into `<style>` and
|
|
32
34
|
`<script type="module">` tags instead. `components.js` gives you `<tv-icon>`;
|
|
33
35
|
`task.js` registers `<tv-task-checkbox>` and `<tv-task-meta-due>`.
|
|
@@ -36,6 +38,13 @@ A task list is not a prose document: don't add `body` padding (the house
|
|
|
36
38
|
style doc suggests it for prose) — `tv-task-list` and the page header supply
|
|
37
39
|
their own gutters. Leave the body at zero.
|
|
38
40
|
|
|
41
|
+
Television manages light and dark appearance. Live canonical v2 supplies the
|
|
42
|
+
selected theme and shared defaults; the task kit inherits its surface, text,
|
|
43
|
+
muted-text, and border roles. Author no `data-theme` marker or separate dark
|
|
44
|
+
palette. Task colors use existing semantics: primary for checked checkboxes,
|
|
45
|
+
`--color-primary-text` for their marks, `--color-alert` for due today and the
|
|
46
|
+
default highlight, and `--color-danger` for overdue.
|
|
47
|
+
|
|
39
48
|
## Markup
|
|
40
49
|
|
|
41
50
|
Author this shape — a page header, then a list of sections and rows:
|
|
@@ -103,8 +112,8 @@ actually in the section, and skip it when it adds nothing.
|
|
|
103
112
|
One task row — a `<tv-task-checkbox>` followed by a `<tv-task-body>`. Give it a
|
|
104
113
|
plain `id` if you'll wire interactivity. One per task.
|
|
105
114
|
|
|
106
|
-
Add `highlighted` to emphasize a row: a soft wash
|
|
107
|
-
is one knob — `--task-highlight` — settable inline for a single row (`<tv-task highlighted style="--task-highlight:
|
|
115
|
+
Add `highlighted` to emphasize a row: a soft wash of the shared alert color by default. The hue
|
|
116
|
+
is one knob — `--task-highlight` — settable inline for a single row (`<tv-task highlighted style="--task-highlight: var(--color-primary)">`)
|
|
108
117
|
or on an ancestor to retint every highlight in the list. Highlight only with
|
|
109
118
|
a good reason: the user asked for it, or the emphasis clearly benefits them
|
|
110
119
|
(the one row they should see first). At more than one or two per list the
|
|
@@ -137,7 +146,7 @@ it.
|
|
|
137
146
|
|
|
138
147
|
### `<tv-task-meta-due>`
|
|
139
148
|
A due date: `<tv-task-meta-due date="YYYY-MM-DD">`. Give it only the machine
|
|
140
|
-
date — it computes urgency (today →
|
|
149
|
+
date — it computes urgency (today → alert, overdue → danger, upcoming → muted),
|
|
141
150
|
writes the human label ("Today", "Tomorrow", "Jun 20"), and draws the calendar
|
|
142
151
|
glyph. Never hand-format the date or set a color. At most one per task.
|
|
143
152
|
|
|
@@ -212,21 +221,16 @@ An overdue date and multiple tags — same markup, the component colors it:
|
|
|
212
221
|
A highlighted row, retinted to match the artifact's accent:
|
|
213
222
|
|
|
214
223
|
```html
|
|
215
|
-
<tv-task highlighted style="--task-highlight:
|
|
224
|
+
<tv-task highlighted style="--task-highlight: var(--color-primary)">
|
|
216
225
|
<tv-task-checkbox></tv-task-checkbox>
|
|
217
226
|
<tv-task-body><tv-task-title>Sign the lease by Friday</tv-task-title></tv-task-body>
|
|
218
227
|
</tv-task>
|
|
219
228
|
```
|
|
220
229
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
```html
|
|
226
|
-
<html data-theme="dark">
|
|
227
|
-
…
|
|
228
|
-
</html>
|
|
229
|
-
```
|
|
230
|
+
The kit retains `--task-due-today`, `--task-highlight`, and `--task-meta-text`
|
|
231
|
+
for deliberate artifact customization. Prefer shared semantic tokens for their
|
|
232
|
+
values. Their defaults follow alert, the due-today color, and shared muted text
|
|
233
|
+
at 85% opacity, respectively; the kit does not redefine canonical color tokens.
|
|
230
234
|
|
|
231
235
|
## Guidelines
|
|
232
236
|
|
|
@@ -9,18 +9,11 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
:root {
|
|
12
|
-
/*
|
|
13
|
-
|
|
14
|
-
--task-due-today: #EA880B;
|
|
12
|
+
/* Task accents follow the active canonical/theme semantic roles. */
|
|
13
|
+
--task-due-today: var(--color-alert);
|
|
15
14
|
/* Highlight hue — the highlighted row's wash derives from this one knob.
|
|
16
15
|
Set it on any ancestor (or the row itself) to retint. */
|
|
17
16
|
--task-highlight: var(--task-due-today);
|
|
18
|
-
/* Skill-local muted: a shade darker than the canonical --color-text-muted
|
|
19
|
-
(neutral-500, L 0.553) so notes hold their own against the darker meta
|
|
20
|
-
treatment below. Declared at :root, so it recolors ALL muted text on the
|
|
21
|
-
page (canonical components included) — deliberate for a single-skill
|
|
22
|
-
artifact; revisit if skills ever share a page. */
|
|
23
|
-
--color-text-muted: oklch(0.52 0 0);
|
|
24
17
|
/* The meta line recedes a step behind the note: 85% of muted. */
|
|
25
18
|
--task-meta-text: color-mix(in srgb, var(--color-text-muted) 85%, transparent);
|
|
26
19
|
}
|
|
@@ -40,7 +33,7 @@ tv-task-body {
|
|
|
40
33
|
|
|
41
34
|
tv-task-title {
|
|
42
35
|
display: block;
|
|
43
|
-
font-weight: var(--font-weight-
|
|
36
|
+
font-weight: var(--font-weight-normal);
|
|
44
37
|
line-height: 1.35;
|
|
45
38
|
}
|
|
46
39
|
/* Completed — tracks the checkbox's reflected host attribute live, not a
|
|
@@ -168,7 +161,7 @@ tv-task-section > header h2 {
|
|
|
168
161
|
tv-task-count {
|
|
169
162
|
color: var(--task-meta-text);
|
|
170
163
|
font-size: var(--text-sm);
|
|
171
|
-
font-weight: var(--font-weight-
|
|
164
|
+
font-weight: var(--font-weight-normal);
|
|
172
165
|
}
|
|
173
166
|
|
|
174
167
|
/* Empty state — shown when a section or list has no tasks. */
|
|
@@ -186,18 +179,3 @@ tv-task-placeholder {
|
|
|
186
179
|
tv-task[highlighted] {
|
|
187
180
|
background: color-mix(in srgb, var(--task-highlight) 10%, transparent);
|
|
188
181
|
}
|
|
189
|
-
|
|
190
|
-
/* ---- Dark mode ----
|
|
191
|
-
The canonical theme is light-only, so the skill carries its own dark token
|
|
192
|
-
set, applied when the document has data-theme="dark". */
|
|
193
|
-
[data-theme="dark"] {
|
|
194
|
-
--color-surface: var(--neutral-800);
|
|
195
|
-
--color-surface-muted: var(--neutral-900);
|
|
196
|
-
--color-text: var(--neutral-50);
|
|
197
|
-
--color-text-muted: var(--neutral-400);
|
|
198
|
-
/* Re-derive: custom properties resolve where declared, so without this the
|
|
199
|
-
meta tone would keep the light-mode muted it was mixed from. */
|
|
200
|
-
--task-meta-text: color-mix(in srgb, var(--color-text-muted) 85%, transparent);
|
|
201
|
-
--color-border: rgba(255, 255, 255, 0.14);
|
|
202
|
-
color-scheme: dark;
|
|
203
|
-
}
|
|
@@ -157,7 +157,7 @@ const CHECKBOX_STYLES = `
|
|
|
157
157
|
content: "";
|
|
158
158
|
width: 4px;
|
|
159
159
|
height: 7px;
|
|
160
|
-
border: solid
|
|
160
|
+
border: solid var(--color-primary-text);
|
|
161
161
|
border-width: 0 1.5px 1.5px 0;
|
|
162
162
|
opacity: 0;
|
|
163
163
|
transform: translateY(-0.5px) rotate(45deg) scale(0.75);
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
# Bundled themes
|
|
2
|
+
|
|
3
|
+
Each immediate child directory is an installed-theme package with `manifest.json`, `theme.css`, and any relative assets it needs. The server build validates this tree and copies it into the server and CLI distributions.
|
|
4
|
+
|
|
5
|
+
A serving boot copies each package into the user themes directory once. The installed folder then belongs to the user. The null theme is labelled `None` and has no package in this tree. Clouds is the default theme selected on a new installation.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Aquarium
|
|
2
|
+
|
|
3
|
+
Authored by an agent (Claude) on 2026-09-17 as part of a batch of "fun themes" testing the theme system. Covers "background iframe" and "light and dark modes".
|
|
4
|
+
|
|
5
|
+
## Intent
|
|
6
|
+
|
|
7
|
+
A fish tank behind the interface. Light appearance: sunlit lagoon, colourful fish, sun shafts. Dark appearance: deep sea, bioluminescent fish outlines, drifting jellyfish and plankton. Interface controls are frosted glass so the tank shows through without hurting legibility.
|
|
8
|
+
|
|
9
|
+
## Why a script
|
|
10
|
+
|
|
11
|
+
Fish that flee the pointer and bubbles that burst where the user presses cannot be done in CSS. No application DOM access is needed, so the effect lives in the sandboxed background frame (`iframe-background.js`), not `main.js`.
|
|
12
|
+
|
|
13
|
+
## How it works
|
|
14
|
+
|
|
15
|
+
- `manifest.json` declares `enableIframeBackgroundJS: true`.
|
|
16
|
+
- The wallpaper paints on `.app-main`, which is in front of the background frame. `theme.css` therefore sets `--app-wallpaper: transparent` — but only under `:root:has(#theme-iframe-background)`. Without the frame (script gate closed, loading) the CSS water gradient is the fallback.
|
|
17
|
+
- The script draws the water itself on a full-viewport canvas.
|
|
18
|
+
- Appearance: read once from the frame root's `data-theme` (see "Frame appearance and transparency" below).
|
|
19
|
+
- Pointer: accepts only `event.source === parent` and the five documented `television-theme-pointer-*` types. Move → fish within 150px steer away. Down → bubble burst + nearby fish startle. Cancel → stop fleeing.
|
|
20
|
+
|
|
21
|
+
## Resource cost
|
|
22
|
+
|
|
23
|
+
One 2D canvas, capped at 30fps and devicePixelRatio 1.5; 22 fish, ≤90 bubbles, 5 jellyfish, 70 plankton points. Skips frames while `document.hidden`. Under `prefers-reduced-motion: reduce` it draws a single still frame and redraws only on resize. `shadowBlur` (dark mode glow) is the most expensive part; reduce fish count first if it ever matters.
|
|
24
|
+
|
|
25
|
+
## Selector rules
|
|
26
|
+
|
|
27
|
+
- `:root:has(#theme-iframe-background)` — wallpaper fallback switch described above.
|
|
28
|
+
- `.sidebar { backdrop-filter }` with an 82%-opaque `--sidebar-background` lets water colour tint the sidebar.
|
|
29
|
+
|
|
30
|
+
## Files
|
|
31
|
+
|
|
32
|
+
`theme.css`, `manifest.json`, `iframe-background.js`. No assets.
|
|
33
|
+
|
|
34
|
+
## Maintenance
|
|
35
|
+
|
|
36
|
+
Every save in this folder destroys and recreates the frame, so fish positions reset — expected. Manifest changes need a registry refresh (`tv set-theme aquarium` does one).
|
|
37
|
+
|
|
38
|
+
## Frame appearance and transparency
|
|
39
|
+
|
|
40
|
+
Television generates the frame document. It stamps `data-theme="light"` or `"dark"` on the document's root, supplies full size, zero margin, hidden overflow and a transparent ground, and keeps the document's `color-scheme` matched to the frame element. That match is what keeps the frame transparent. Television recreates the frame and reruns the script when appearance changes.
|
|
41
|
+
|
|
42
|
+
So the scripts here read `data-theme` from the root once at startup when they need it, install no appearance listener, and do not style the root element at all. Never set `color-scheme` on the frame document's root: a mismatch with the frame element makes the frame opaque, and an opaque overlay frame hides the whole application.
|
|
43
|
+
|
|
44
|
+
History: 1.0.0 predated this contract and, in dark appearance, rendered the frame as a solid white sheet. 1.0.1 worked around it from inside the script. 1.0.2 removes the workaround and follows the contract above.
|
|
45
|
+
|
|
46
|
+
## Manifest colour scheme
|
|
47
|
+
|
|
48
|
+
`"colorScheme": "light dark"`: the theme adapts and follows the Light / Dark / System preference through its `[data-theme]` blocks. The theme sets no CSS `color-scheme`; the manifest is the only place the scheme is stated.
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/* Aquarium background. Runs in Television's sandboxed background frame: no app DOM access, input arrives only as pointer messages. */
|
|
2
|
+
(() => {
|
|
3
|
+
"use strict";
|
|
4
|
+
|
|
5
|
+
const POINTER = {
|
|
6
|
+
move: "television-theme-pointer-move",
|
|
7
|
+
down: "television-theme-pointer-down",
|
|
8
|
+
up: "television-theme-pointer-up",
|
|
9
|
+
cancel: "television-theme-pointer-cancel",
|
|
10
|
+
click: "television-theme-pointer-click",
|
|
11
|
+
};
|
|
12
|
+
const FRAME_MS = 1000 / 30; // 30fps is plenty for drifting fish
|
|
13
|
+
const rand = (a, b) => a + Math.random() * (b - a);
|
|
14
|
+
|
|
15
|
+
function start() {
|
|
16
|
+
// Television's frame document already supplies full size, zero margin, hidden overflow, a transparent ground and the
|
|
17
|
+
// matching color-scheme. Leave the root element's styles alone: changing color-scheme there turns the frame opaque.
|
|
18
|
+
const canvas = document.createElement("canvas");
|
|
19
|
+
canvas.style.cssText = "display:block;width:100vw;height:100vh";
|
|
20
|
+
document.body.appendChild(canvas);
|
|
21
|
+
const ctx = canvas.getContext("2d");
|
|
22
|
+
|
|
23
|
+
// Television stamps the effective appearance on this document's root and recreates the frame when it changes,
|
|
24
|
+
// so one read at startup is enough and no listener is needed.
|
|
25
|
+
const dark = document.documentElement.getAttribute("data-theme") === "dark";
|
|
26
|
+
const still = matchMedia("(prefers-reduced-motion: reduce)");
|
|
27
|
+
let W = 0, H = 0, dpr = 1;
|
|
28
|
+
const pointer = { x: -9999, y: -9999, active: false };
|
|
29
|
+
|
|
30
|
+
function resize() {
|
|
31
|
+
dpr = Math.min(devicePixelRatio || 1, 1.5);
|
|
32
|
+
W = innerWidth; H = innerHeight;
|
|
33
|
+
canvas.width = Math.max(1, Math.round(W * dpr));
|
|
34
|
+
canvas.height = Math.max(1, Math.round(H * dpr));
|
|
35
|
+
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
36
|
+
}
|
|
37
|
+
addEventListener("resize", resize);
|
|
38
|
+
resize();
|
|
39
|
+
|
|
40
|
+
const DAY_FISH = ["#ff7a59", "#ffc145", "#ff5d8f", "#f4f1de", "#7bdff2", "#b388eb"];
|
|
41
|
+
const fish = Array.from({ length: 22 }, (_, i) => ({
|
|
42
|
+
x: rand(0, 1600), y: rand(60, 800), vx: rand(-1, 1) || 0.5, vy: 0,
|
|
43
|
+
size: rand(9, 22), hue: i % DAY_FISH.length, phase: rand(0, 6.28), speed: rand(0.5, 1.3), depth: rand(0.55, 1),
|
|
44
|
+
}));
|
|
45
|
+
const bubbles = Array.from({ length: 34 }, () => newBubble(true));
|
|
46
|
+
const jellies = Array.from({ length: 5 }, () => ({ x: rand(0, 1600), y: rand(0, 900), r: rand(16, 34), phase: rand(0, 6.28), hue: rand(170, 320) }));
|
|
47
|
+
const plankton = Array.from({ length: 70 }, () => ({ x: rand(0, 1600), y: rand(0, 900), p: rand(0, 6.28) }));
|
|
48
|
+
|
|
49
|
+
function newBubble(anywhere, x, y) {
|
|
50
|
+
return { x: x ?? rand(0, Math.max(W, 800)), y: y ?? (anywhere ? rand(0, Math.max(H, 600)) : Math.max(H, 600) + 10), r: rand(1.5, 5.5), vy: rand(0.4, 1.4), wob: rand(0, 6.28) };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
addEventListener("message", (event) => {
|
|
54
|
+
if (event.source !== parent) return;
|
|
55
|
+
const d = event.data;
|
|
56
|
+
if (!d || typeof d.type !== "string") return;
|
|
57
|
+
if (d.type === POINTER.move) { pointer.x = d.clientX; pointer.y = d.clientY; pointer.active = true; }
|
|
58
|
+
else if (d.type === POINTER.down) {
|
|
59
|
+
pointer.x = d.clientX; pointer.y = d.clientY;
|
|
60
|
+
for (let i = 0; i < 9 && bubbles.length < 90; i++) bubbles.push(newBubble(false, d.clientX + rand(-10, 10), d.clientY + rand(-6, 6)));
|
|
61
|
+
for (const f of fish) { // a tap on the glass startles nearby fish
|
|
62
|
+
const dx = f.x - d.clientX, dy = f.y - d.clientY, dist = Math.hypot(dx, dy) || 1;
|
|
63
|
+
if (dist < 260) { f.vx += (dx / dist) * 5; f.vy += (dy / dist) * 3; }
|
|
64
|
+
}
|
|
65
|
+
} else if (d.type === POINTER.cancel) pointer.active = false;
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
function drawWater(dark, t) {
|
|
69
|
+
const g = ctx.createLinearGradient(0, 0, 0, H);
|
|
70
|
+
if (dark) { g.addColorStop(0, "#0b2545"); g.addColorStop(0.6, "#06142e"); g.addColorStop(1, "#02060f"); }
|
|
71
|
+
else { g.addColorStop(0, "#9be7f0"); g.addColorStop(0.55, "#3aa7d6"); g.addColorStop(1, "#1b5fa8"); }
|
|
72
|
+
ctx.fillStyle = g; ctx.fillRect(0, 0, W, H);
|
|
73
|
+
if (!dark) { // sun shafts
|
|
74
|
+
ctx.save(); ctx.globalCompositeOperation = "lighter";
|
|
75
|
+
for (let i = 0; i < 5; i++) {
|
|
76
|
+
const x = (W * (i + 0.5)) / 5 + Math.sin(t / 4000 + i) * 60, w = 50 + 25 * Math.sin(t / 3000 + i * 2);
|
|
77
|
+
const rg = ctx.createLinearGradient(0, 0, 0, H * 0.85);
|
|
78
|
+
rg.addColorStop(0, "rgba(255,255,230,0.16)"); rg.addColorStop(1, "rgba(255,255,230,0)");
|
|
79
|
+
ctx.fillStyle = rg; ctx.beginPath(); ctx.moveTo(x - w / 2, 0); ctx.lineTo(x + w / 2, 0); ctx.lineTo(x + w * 1.6 - 120, H * 0.85); ctx.lineTo(x - w * 1.6 - 120, H * 0.85); ctx.closePath(); ctx.fill();
|
|
80
|
+
}
|
|
81
|
+
ctx.restore();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function drawFish(f, dark, t) {
|
|
86
|
+
const dir = f.vx >= 0 ? 1 : -1, s = f.size * f.depth, wag = Math.sin(t / 110 * f.speed + f.phase) * 0.45;
|
|
87
|
+
ctx.save(); ctx.translate(f.x, f.y); ctx.scale(dir, 1); ctx.rotate(Math.max(-0.4, Math.min(0.4, f.vy * 0.25)));
|
|
88
|
+
ctx.globalAlpha = 0.45 + 0.55 * f.depth;
|
|
89
|
+
if (dark) { ctx.fillStyle = "rgba(10,25,50,0.9)"; ctx.strokeStyle = `hsla(${170 + f.hue * 25},90%,65%,0.85)`; ctx.lineWidth = 1.2; ctx.shadowColor = ctx.strokeStyle; ctx.shadowBlur = 8; }
|
|
90
|
+
else { ctx.fillStyle = DAY_FISH[f.hue]; }
|
|
91
|
+
ctx.beginPath(); ctx.ellipse(0, 0, s, s * 0.48, 0, 0, 6.2832); ctx.fill(); if (dark) ctx.stroke();
|
|
92
|
+
ctx.beginPath(); ctx.moveTo(-s * 0.85, 0); ctx.lineTo(-s * 1.7, -s * 0.55 + wag * s); ctx.lineTo(-s * 1.7, s * 0.55 + wag * s); ctx.closePath(); ctx.fill(); if (dark) ctx.stroke();
|
|
93
|
+
ctx.shadowBlur = 0; ctx.fillStyle = dark ? "rgba(200,255,250,0.9)" : "rgba(20,30,50,0.85)";
|
|
94
|
+
ctx.beginPath(); ctx.arc(s * 0.55, -s * 0.1, Math.max(1, s * 0.09), 0, 6.2832); ctx.fill();
|
|
95
|
+
ctx.restore();
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function drawJelly(j, t) {
|
|
99
|
+
const pulse = 1 + 0.12 * Math.sin(t / 700 + j.phase);
|
|
100
|
+
ctx.save(); ctx.translate(j.x, j.y); ctx.globalAlpha = 0.55;
|
|
101
|
+
ctx.shadowColor = `hsla(${j.hue},90%,70%,0.9)`; ctx.shadowBlur = 22; ctx.fillStyle = `hsla(${j.hue},85%,72%,0.5)`;
|
|
102
|
+
ctx.beginPath(); ctx.ellipse(0, 0, j.r * pulse, j.r * 0.75 / pulse, 0, Math.PI, 0); ctx.closePath(); ctx.fill();
|
|
103
|
+
ctx.shadowBlur = 0; ctx.strokeStyle = `hsla(${j.hue},85%,78%,0.45)`; ctx.lineWidth = 1.2;
|
|
104
|
+
for (let k = -2; k <= 2; k++) { ctx.beginPath(); ctx.moveTo(k * j.r * 0.35, 0); for (let y = 4; y < j.r * 2.2; y += 4) ctx.lineTo(k * j.r * 0.35 + Math.sin(t / 400 + y / 9 + k + j.phase) * 4, y); ctx.stroke(); }
|
|
105
|
+
ctx.restore();
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function step(t) {
|
|
109
|
+
for (const f of fish) {
|
|
110
|
+
f.vx += Math.cos(t / 2300 * f.speed + f.phase) * 0.01; f.vy += Math.sin(t / 1700 + f.phase) * 0.012;
|
|
111
|
+
if (pointer.active) { const dx = f.x - pointer.x, dy = f.y - pointer.y, dist = Math.hypot(dx, dy) || 1; if (dist < 150) { const k = (150 - dist) / 150; f.vx += (dx / dist) * k * 0.9; f.vy += (dy / dist) * k * 0.6; } }
|
|
112
|
+
const cruise = f.speed * (f.vx >= 0 ? 1 : -1); f.vx += (cruise - f.vx) * 0.02; f.vy *= 0.95;
|
|
113
|
+
f.x += f.vx * f.depth; f.y += f.vy * f.depth;
|
|
114
|
+
if (f.x < -50) f.x = W + 40; else if (f.x > W + 50) f.x = -40;
|
|
115
|
+
if (f.y < 40) f.vy += 0.05; else if (f.y > H - 40) f.vy -= 0.05;
|
|
116
|
+
}
|
|
117
|
+
for (let i = bubbles.length - 1; i >= 0; i--) { const b = bubbles[i]; b.y -= b.vy; b.wob += 0.04; b.x += Math.sin(b.wob) * 0.35; if (b.y < -10) { if (bubbles.length > 34) bubbles.splice(i, 1); else bubbles[i] = newBubble(false); } }
|
|
118
|
+
for (const j of jellies) { j.y -= 0.18 + 0.12 * Math.sin(t / 700 + j.phase); j.x += Math.sin(t / 2600 + j.phase) * 0.2; if (j.y < -80) { j.y = H + 80; j.x = rand(0, W); } }
|
|
119
|
+
|
|
120
|
+
drawWater(dark, t);
|
|
121
|
+
if (dark) { ctx.fillStyle = "rgba(160,255,240,0.55)"; for (const p of plankton) { const a = 0.5 + 0.5 * Math.sin(t / 900 + p.p); ctx.globalAlpha = a * 0.6; ctx.fillRect(p.x % Math.max(W, 1), (p.y + t / 140) % Math.max(H, 1), 1.5, 1.5); } ctx.globalAlpha = 1; for (const j of jellies) drawJelly(j, t); }
|
|
122
|
+
fish.sort((a, b) => a.depth - b.depth); for (const f of fish) drawFish(f, dark, t);
|
|
123
|
+
ctx.lineWidth = 1; for (const b of bubbles) { ctx.strokeStyle = dark ? "rgba(170,230,255,0.35)" : "rgba(255,255,255,0.7)"; ctx.fillStyle = dark ? "rgba(170,230,255,0.06)" : "rgba(255,255,255,0.18)"; ctx.beginPath(); ctx.arc(b.x, b.y, b.r, 0, 6.2832); ctx.fill(); ctx.stroke(); }
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
let last = 0;
|
|
127
|
+
function loop(t) {
|
|
128
|
+
requestAnimationFrame(loop);
|
|
129
|
+
if (document.hidden || t - last < FRAME_MS) return;
|
|
130
|
+
last = t; step(t);
|
|
131
|
+
}
|
|
132
|
+
if (still.matches) { step(0); addEventListener("resize", () => step(0)); }
|
|
133
|
+
else requestAnimationFrame(loop);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (document.body) start(); else addEventListener("DOMContentLoaded", start, { once: true });
|
|
137
|
+
})();
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/* Aquarium — a living tank behind the interface (iframe-background.js). Lagoon by day, deep sea by night. */
|
|
2
|
+
:root {
|
|
3
|
+
--accent: oklch(0.72 0.15 200);
|
|
4
|
+
--neutral: oklch(0.556 0.03 225);
|
|
5
|
+
--control-radius: 10px;
|
|
6
|
+
--panel-radius: 14px;
|
|
7
|
+
--color-link: var(--accent-600);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
[data-theme="dark"] {
|
|
11
|
+
--accent: oklch(0.8 0.14 190);
|
|
12
|
+
--color-surface: oklch(0.22 0.04 235);
|
|
13
|
+
--color-surface-muted: oklch(0.18 0.04 238);
|
|
14
|
+
--color-link: var(--accent-300);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
:root[data-television-document="app"] {
|
|
18
|
+
--frame-radius: 16px;
|
|
19
|
+
--tab-radius: var(--radius-pill);
|
|
20
|
+
--channel-radius: 10px;
|
|
21
|
+
/* Tank glass: frosted controls so the fish stay visible but labels stay readable. */
|
|
22
|
+
--wallpaper-overlay-background: var(--tint-surface);
|
|
23
|
+
--wallpaper-overlay-blur: 12px;
|
|
24
|
+
--wallpaper-overlay-border: 1px solid oklch(1 0 0 / 25%);
|
|
25
|
+
--page-inactive-opacity: 0.7;
|
|
26
|
+
/* The wallpaper is a water gradient that also serves when the script is off or still loading… */
|
|
27
|
+
--app-wallpaper: linear-gradient(to bottom, oklch(0.86 0.09 200), oklch(0.62 0.13 225) 60%, oklch(0.45 0.12 245));
|
|
28
|
+
--wallpaper-overlay-text-color: oklch(0.25 0.06 240);
|
|
29
|
+
--sidebar-background: oklch(from var(--color-surface-muted) l c h / 82%);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
:root[data-television-document="app"][data-theme="dark"] {
|
|
33
|
+
--app-wallpaper: linear-gradient(to bottom, oklch(0.26 0.08 245), oklch(0.15 0.06 255) 60%, oklch(0.08 0.03 260));
|
|
34
|
+
--wallpaper-overlay-text-color: oklch(0.93 0.03 200);
|
|
35
|
+
--wallpaper-overlay-border: 1px solid oklch(1 0 0 / 12%);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/* …and becomes transparent once the background frame exists, because the wallpaper paints on .app-main in front of the frame and would hide it. */
|
|
39
|
+
:root[data-television-document="app"]:has(#theme-iframe-background) {
|
|
40
|
+
--app-wallpaper: transparent;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/* Let the water show faintly through the sidebar. */
|
|
44
|
+
:root[data-television-document="app"] .sidebar {
|
|
45
|
+
backdrop-filter: blur(14px);
|
|
46
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Blueprint
|
|
2
|
+
|
|
3
|
+
Authored by an agent (Claude) on 2026-09-17 as part of a batch of "fun themes" testing the theme system. Covers the brief's "single-mode theme" and "creative CSS" cases.
|
|
4
|
+
|
|
5
|
+
## Intent
|
|
6
|
+
|
|
7
|
+
A cyanotype engineering drawing. One fixed look with no `data-theme` blocks. The manifest declares `"colorScheme": "dark"`, so Television applies the dark appearance wherever this theme is active, whatever the appearance preference says. The theme sets no CSS `color-scheme` anywhere; Television supplies it. White-line edges, square corners, monospace lettering, a drafting grid.
|
|
8
|
+
|
|
9
|
+
## Choices
|
|
10
|
+
|
|
11
|
+
- Role tokens (`--color-surface`, `--color-text`, …) are stated at `:root` so they beat both foundation mode tables.
|
|
12
|
+
- `--alpha-active: 12%` is restated in `theme.css`. It dates from when a fixed look could run under light appearance; with `colorScheme: "dark"` the foundation's dark table always applies, so the restatement is now redundant but harmless. Hue slots (`--red`, …) are *not* restated: in light appearance they keep the slightly more saturated light-table values. Acceptable.
|
|
13
|
+
- Shadows are replaced: a blueprint has no depth. Panels get a 4px surface-coloured halo (`--shadow-md`) so popovers separate from the grid.
|
|
14
|
+
- `--font-sans` is monospace and unprefixed, so live-canonical artifacts are lettered the same way. Intentional cross-document reach.
|
|
15
|
+
|
|
16
|
+
## Selector rules (all app-scoped)
|
|
17
|
+
|
|
18
|
+
- **Grid wallpaper** — four `linear-gradient` layers in `--app-wallpaper`; no image asset.
|
|
19
|
+
- **Part numbers** — `counter-reset` on `.sidebar-body`, `counter-increment` on `.channel-row`, shown in `.channel::before`. Numbering runs continuously through Pinned and Recent. Pure decoration; the DOM is untouched.
|
|
20
|
+
- **Selection callout** — selected channel is a dashed cyan outline with cyan text (`--channel-background-selected: transparent`), not a fill. Outline is on `.channel`, separate from the keyboard focus ring.
|
|
21
|
+
- **Uppercase lettering** — `.tab-label`, `.channel-group-label`, `.artifact-title`.
|
|
22
|
+
- **Dimension line** — `.page[selected]::before`, positioned in the gap above the frame. Relies on `.page` being a positioned, unclipped box; if a release changes that, the mark simply disappears or misplaces — delete the rule.
|
|
23
|
+
- **Title block** — `#foreground-overlay::after`, bottom-left, width derived from `--sidebar-width`. It sits above the interface without taking input. With a very long channel list it overlaps the last rows (85% opaque); accepted.
|
|
24
|
+
|
|
25
|
+
## Files
|
|
26
|
+
|
|
27
|
+
`theme.css`, `manifest.json`. No assets, no JavaScript.
|
|
28
|
+
|
|
29
|
+
## Maintenance
|
|
30
|
+
|
|
31
|
+
`.channel::before`, `.page[selected]::before` and `#foreground-overlay::after` were confirmed unused by Television 1.3.1 (computed `content: none`) before being claimed. Re-check on upgrade.
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/* Blueprint — a fixed cyanotype look: one appearance, stated at :root. */
|
|
2
|
+
:root {
|
|
3
|
+
--neutral: oklch(0.556 0.09 255);
|
|
4
|
+
--accent: oklch(0.86 0.12 205); /* pale cyan pencil */
|
|
5
|
+
--color-surface: oklch(0.3 0.11 258);
|
|
6
|
+
--color-surface-muted: oklch(0.26 0.1 258);
|
|
7
|
+
--color-text: oklch(0.95 0.025 225);
|
|
8
|
+
--color-text-muted: oklch(0.78 0.06 235);
|
|
9
|
+
--color-text-reversed: oklch(0.22 0.09 258);
|
|
10
|
+
--color-border: oklch(from var(--color-text) l c h / 35%);
|
|
11
|
+
--color-link: var(--accent);
|
|
12
|
+
/* The foundation's dark table only applies in dark appearance; restate its press strength for the fixed look. */
|
|
13
|
+
--alpha-active: 12%;
|
|
14
|
+
|
|
15
|
+
/* Technical lettering everywhere, artifacts included. */
|
|
16
|
+
--font-sans: ui-monospace, "SF Mono", SFMono-Regular, Menlo, "DejaVu Sans Mono", monospace;
|
|
17
|
+
|
|
18
|
+
/* Drafted with a ruler: no curves, white-line edges. */
|
|
19
|
+
--control-radius: 0px;
|
|
20
|
+
--panel-radius: 0px;
|
|
21
|
+
--surface-border-color: oklch(from var(--color-text) l c h / 55%);
|
|
22
|
+
--surface-outline-color: transparent;
|
|
23
|
+
--panel-border: 1px dashed var(--panel-border-color);
|
|
24
|
+
--shadow-sm: none;
|
|
25
|
+
--shadow-md: 0 0 0 4px oklch(from var(--color-surface) l c h / 60%);
|
|
26
|
+
--shadow-lg: var(--shadow-md);
|
|
27
|
+
--shadow-xl: none;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
:root[data-television-document="app"] {
|
|
31
|
+
--frame-radius: 0px;
|
|
32
|
+
--tab-radius: 0px;
|
|
33
|
+
--channel-radius: 0px;
|
|
34
|
+
--page-snap-preview-radius: 0px;
|
|
35
|
+
--frame-border: 1px solid var(--frame-border-color);
|
|
36
|
+
--frame-titlebar-border-color: var(--frame-border-color);
|
|
37
|
+
--sidebar-background: oklch(0.27 0.105 258);
|
|
38
|
+
--sidebar-heading-font-size: var(--text-sm);
|
|
39
|
+
--channel-background-selected: transparent;
|
|
40
|
+
--channel-text-color-selected: var(--accent);
|
|
41
|
+
--tab-background-selected: var(--color-surface);
|
|
42
|
+
--tab-border-selected: 1px dashed var(--surface-border-color);
|
|
43
|
+
--wallpaper-overlay-text-color: var(--color-text);
|
|
44
|
+
--page-inactive-opacity: 0.4;
|
|
45
|
+
|
|
46
|
+
/* Drafting grid: 100px majors, 20px minors, drawn entirely with gradients. */
|
|
47
|
+
--app-wallpaper:
|
|
48
|
+
linear-gradient(oklch(1 0 0 / 16%) 1px, transparent 1px) 0 0 / 100px 100px,
|
|
49
|
+
linear-gradient(90deg, oklch(1 0 0 / 16%) 1px, transparent 1px) 0 0 / 100px 100px,
|
|
50
|
+
linear-gradient(oklch(1 0 0 / 6%) 1px, transparent 1px) 0 0 / 20px 20px,
|
|
51
|
+
linear-gradient(90deg, oklch(1 0 0 / 6%) 1px, transparent 1px) 0 0 / 20px 20px,
|
|
52
|
+
oklch(0.34 0.125 258);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/* Part numbers: a CSS counter labels every channel like a bill of materials. */
|
|
56
|
+
:root[data-television-document="app"] .sidebar-body {
|
|
57
|
+
counter-reset: part;
|
|
58
|
+
}
|
|
59
|
+
:root[data-television-document="app"] .channel-row {
|
|
60
|
+
counter-increment: part;
|
|
61
|
+
}
|
|
62
|
+
:root[data-television-document="app"] .channel::before {
|
|
63
|
+
content: counter(part, decimal-leading-zero) " ";
|
|
64
|
+
color: var(--color-text-muted);
|
|
65
|
+
margin-right: var(--space-6);
|
|
66
|
+
}
|
|
67
|
+
/* Selection is a drafted callout — dashed box and cyan ink — instead of a fill. */
|
|
68
|
+
:root[data-television-document="app"] .channel[aria-selected="true"] {
|
|
69
|
+
outline: 1px dashed var(--accent);
|
|
70
|
+
outline-offset: -1px;
|
|
71
|
+
}
|
|
72
|
+
:root[data-television-document="app"] .channel[aria-selected="true"]::before {
|
|
73
|
+
color: var(--accent);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/* Drawing-label lettering for tabs and group headings. */
|
|
77
|
+
:root[data-television-document="app"] :is(.tab-label, .channel-group-label, .artifact-title) {
|
|
78
|
+
text-transform: uppercase;
|
|
79
|
+
letter-spacing: 0.08em;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/* Dimension line: a measured arrow across the top edge of the selected page. The page is not clipped, so the mark sits in the gap above the frame. */
|
|
83
|
+
:root[data-television-document="app"] .page[selected]::before {
|
|
84
|
+
content: "◂──────── SELECTED ────────▸";
|
|
85
|
+
position: absolute;
|
|
86
|
+
top: -18px;
|
|
87
|
+
left: 0;
|
|
88
|
+
right: 0;
|
|
89
|
+
text-align: center;
|
|
90
|
+
font: 10px/1 var(--font-mono);
|
|
91
|
+
letter-spacing: 0.1em;
|
|
92
|
+
white-space: nowrap;
|
|
93
|
+
overflow: hidden;
|
|
94
|
+
color: oklch(from var(--color-text) l c h / 70%);
|
|
95
|
+
pointer-events: none;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/* Title block, as on every engineering drawing, in the usually empty bottom of the sidebar. Lives on the input-free foreground overlay. */
|
|
99
|
+
:root[data-television-document="app"] #foreground-overlay::after {
|
|
100
|
+
content: "TELEVISION / CHANNEL LAYOUT\A DWG NO. TV-001 SHEET 1 OF 1\A SCALE 1:1 DO NOT SCALE DRAWING";
|
|
101
|
+
white-space: pre;
|
|
102
|
+
position: absolute;
|
|
103
|
+
left: 12px;
|
|
104
|
+
bottom: 12px;
|
|
105
|
+
width: calc(var(--sidebar-width) - 24px);
|
|
106
|
+
box-sizing: border-box;
|
|
107
|
+
padding: 8px 10px;
|
|
108
|
+
font: 9px/1.7 var(--font-mono);
|
|
109
|
+
letter-spacing: 0.06em;
|
|
110
|
+
color: oklch(from var(--color-text) l c h / 75%);
|
|
111
|
+
border: 1px solid oklch(from var(--color-text) l c h / 50%);
|
|
112
|
+
box-shadow: inset 0 0 0 3px oklch(0.27 0.105 258), inset 0 0 0 4px oklch(from var(--color-text) l c h / 30%);
|
|
113
|
+
background: oklch(0.27 0.105 258 / 85%);
|
|
114
|
+
overflow: hidden;
|
|
115
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Clouds
|
|
2
|
+
|
|
3
|
+
Clouds gives Television a cool, airy appearance with sky photographs and restrained interface colors. Light appearance pairs white clouds and blue sky with pale gray surfaces; dark appearance uses deep blue clouds and stars. Translucent controls and soft shadows keep the interface readable while leaving the sky visible around the content.
|
|
4
|
+
|
|
5
|
+
The blue accent echoes the daylight photograph. Interface colors adapt the Gray, Neutral and Slate palettes from [Tailwind CSS](https://github.com/tailwindlabs/tailwindcss/blob/41d9cae8e53378d16087fcf359eb785c2fd42ce4/packages/tailwindcss/theme.css), under the [MIT License](https://github.com/tailwindlabs/tailwindcss/blob/41d9cae8e53378d16087fcf359eb785c2fd42ce4/LICENSE). Copyright (c) Tailwind Labs, Inc. Complete terms are included in `THIRD-PARTY-NOTICES.txt`.
|
|
6
|
+
|
|
7
|
+
## Wallpapers and credits
|
|
8
|
+
|
|
9
|
+
The day photograph is `wallpaper.webp`; the night photograph is `wallpaper-dark.webp`.
|
|
10
|
+
|
|
11
|
+
Day: Photo by <a href="https://unsplash.com/@ljubazarkovic?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Ljubomir Žarković</a> on <a href="https://unsplash.com/photos/white-clouds-and-blue-sky-during-daytime-bt_ZtkCxLs4?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>.
|
|
12
|
+
|
|
13
|
+
Night: Photo by <a href="https://unsplash.com/@shotbycerqueira?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Shot by Cerqueira</a> on <a href="https://unsplash.com/photos/blue-and-white-starry-night-sky-NpF9JLGYfeQ?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>.
|
|
14
|
+
|
|
15
|
+
Both photographs are used under the [Unsplash License](https://unsplash.com/license), retrieved 2026-09-17. Complete license terms and photo attributions are included in `THIRD-PARTY-NOTICES.txt`.
|