@telepath-computer/television 0.1.74 → 0.1.76
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/canonical/v1/styles.css +165 -29
- package/dist/cli.cjs +153 -42
- package/dist/skills/television/SKILL.md +15 -583
- package/dist/skills/television/artifact-workflow.md +13 -0
- package/dist/skills/television/what-to-read.md +26 -0
- package/dist/views/markdown/index.html +1 -1
- package/dist/web/assets/index-BUCSGUvF.css +1 -0
- package/dist/web/assets/index-DIZAkzX9.js +577 -0
- package/dist/web/index.html +2 -2
- package/package.json +4 -2
- package/dist/web/assets/index-C4qF3VNe.css +0 -1
- package/dist/web/assets/index-CYC4bj_I.js +0 -527
- /package/dist/skills/{artifact-table/SKILL.md → television/artifact-types/table.md} +0 -0
- /package/dist/skills/{artifacts/SKILL.md → television/html-house-style.md} +0 -0
|
@@ -74,6 +74,15 @@ select {
|
|
|
74
74
|
--text-xl: round(calc(var(--text-lg) * var(--text-scale)), 1px);
|
|
75
75
|
--leading-base: 1.5;
|
|
76
76
|
|
|
77
|
+
/* Control height — single source of truth for buttons, segmented-controls,
|
|
78
|
+
inputs and any future control whose shape includes "horizontal pill".
|
|
79
|
+
Suffix names a size (md = default); add `--control-height-sm`,
|
|
80
|
+
`--control-height-lg` if/when those sizes appear. Setting the height
|
|
81
|
+
directly (vs. computing from padding + line-height) is what lets a
|
|
82
|
+
mixed row of text + icon-only chrome line up exactly. */
|
|
83
|
+
--control-height-sm: 24px;
|
|
84
|
+
--control-height-md: 28px;
|
|
85
|
+
|
|
77
86
|
/* Spacing */
|
|
78
87
|
--space-2: 2px;
|
|
79
88
|
--space-4: 4px;
|
|
@@ -379,19 +388,30 @@ th {
|
|
|
379
388
|
either light or dark surfaces. */
|
|
380
389
|
input,
|
|
381
390
|
textarea {
|
|
382
|
-
padding: 6px 10px;
|
|
383
391
|
border: 0.5px solid var(--tint-300);
|
|
384
392
|
border-radius: var(--radius-12);
|
|
385
393
|
background: var(--tint-100);
|
|
386
394
|
color: inherit;
|
|
387
395
|
font: inherit;
|
|
396
|
+
box-sizing: border-box;
|
|
388
397
|
transition:
|
|
389
398
|
background 120ms ease,
|
|
390
399
|
border-color 120ms ease,
|
|
391
400
|
box-shadow 120ms ease;
|
|
392
401
|
}
|
|
393
402
|
|
|
403
|
+
/* Single-line input snaps to the shared control height so it lines up
|
|
404
|
+
with buttons in a chrome row. Padding goes horizontal-only since the
|
|
405
|
+
explicit height handles vertical centering via the line-box. */
|
|
406
|
+
input {
|
|
407
|
+
height: var(--control-height-md);
|
|
408
|
+
padding: 0 10px;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/* Multi-line textarea stays intrinsic — height grows with content (or
|
|
412
|
+
max-height on a host) rather than locking to a control unit. */
|
|
394
413
|
textarea {
|
|
414
|
+
padding: 6px 10px;
|
|
395
415
|
resize: none;
|
|
396
416
|
}
|
|
397
417
|
|
|
@@ -434,26 +454,53 @@ label.field > span {
|
|
|
434
454
|
}
|
|
435
455
|
}
|
|
436
456
|
|
|
457
|
+
/* Button-only layout — placement of icon + label inside a single
|
|
458
|
+
button. Segmented-button doesn't share these (it's a flex row of
|
|
459
|
+
children, not a center-aligned label-and-icon stack).
|
|
460
|
+
`height: var(--control-height-md)` is explicit so a row of mixed
|
|
461
|
+
chrome (text button + icon-only button + segmented-control) lines up
|
|
462
|
+
without each variant having to tune its intrinsic content + padding
|
|
463
|
+
to match. Padding is purely horizontal — vertical centering is handled
|
|
464
|
+
by `align-items: center`. */
|
|
437
465
|
button {
|
|
438
466
|
display: inline-flex;
|
|
439
467
|
align-items: center;
|
|
440
468
|
justify-content: center;
|
|
441
469
|
gap: var(--space-6);
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
470
|
+
box-sizing: border-box;
|
|
471
|
+
height: var(--control-height-md);
|
|
472
|
+
/* 2px extra bottom padding compensates for the visual offset from
|
|
473
|
+
font ascender/descender asymmetry — `align-items: center` centers
|
|
474
|
+
the line-box but the glyph mass sits below center because the
|
|
475
|
+
ascender claims more vertical space than the descender. The
|
|
476
|
+
icon-only override resets padding to 0 since icons are already
|
|
477
|
+
symmetric. */
|
|
478
|
+
padding: 0 14px 2px;
|
|
447
479
|
font: inherit;
|
|
448
480
|
cursor: default;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/* Default chrome — shared between <button> and <segmented-control> so
|
|
484
|
+
the two stay in visual lockstep. Variant overrides (ghost, primary,
|
|
485
|
+
danger) and tone-dark / tone-light / disabled cascades live below.
|
|
486
|
+
The shared-with-segmented bits are intentional: a segmented control
|
|
487
|
+
is a chrome surface that looks like a single button containing two
|
|
488
|
+
click targets, so its host adopts the same fill, hairline, radius,
|
|
489
|
+
and shadow. */
|
|
490
|
+
button,
|
|
491
|
+
segmented-control {
|
|
492
|
+
background: var(--color-surface);
|
|
493
|
+
border: 0.5px solid var(--tint-200);
|
|
494
|
+
border-radius: var(--radius-12);
|
|
449
495
|
box-shadow: var(--shadow-control);
|
|
496
|
+
color: inherit;
|
|
450
497
|
}
|
|
451
498
|
|
|
452
|
-
button:hover:not(:disabled) {
|
|
499
|
+
button:not([material]):hover:not(:disabled) {
|
|
453
500
|
background: color-mix(in srgb, var(--color-surface) 96%, black);
|
|
454
501
|
}
|
|
455
502
|
|
|
456
|
-
button:active:not(:disabled) {
|
|
503
|
+
button:not([material]):active:not(:disabled) {
|
|
457
504
|
background: color-mix(in srgb, var(--color-surface) 88%, black);
|
|
458
505
|
box-shadow: inset 0 1px 2px rgb(0 0 0 / 0.08);
|
|
459
506
|
}
|
|
@@ -503,7 +550,7 @@ button[variant="ghost"] {
|
|
|
503
550
|
/* Ghost is a chromeless overlay — at rest it's invisible and relies on
|
|
504
551
|
the surface behind it. The overlay tokens cascade through `tone="dark"`
|
|
505
552
|
(set on the button itself by the auto-tone watcher in
|
|
506
|
-
`
|
|
553
|
+
`utils/button-tone.ts`, or inherited from a tone-sampled ancestor), so
|
|
507
554
|
one rule covers light + dark backdrops. */
|
|
508
555
|
button[variant="ghost"]:hover:not(:disabled) {
|
|
509
556
|
background: var(--color-overlay-hover);
|
|
@@ -527,6 +574,81 @@ button[variant="ghost"][aria-expanded="true"]:hover:not(:disabled) {
|
|
|
527
574
|
box-shadow: none;
|
|
528
575
|
}
|
|
529
576
|
|
|
577
|
+
/* Glass variant — translucent surface with backdrop blur. Shared between
|
|
578
|
+
<button variant="glass"> and <segmented-control variant="glass"> so a
|
|
579
|
+
chrome row of buttons + segmented controls reads as one surface
|
|
580
|
+
family. Self-contained per-variant chrome (bg + border + shadow + blur
|
|
581
|
+
+ hover/active overlays) means no fighting with default-button rules
|
|
582
|
+
or tone-driven default fills.
|
|
583
|
+
|
|
584
|
+
No explicit `color` here — auto-tone (utils/button-tone.ts) writes
|
|
585
|
+
`tone="light"` or `tone="dark"` on the element after sampling its
|
|
586
|
+
actual backdrop. The materials.css `[tone="…"]` color rules then set
|
|
587
|
+
text color on the element directly (specificity 0,1,0), beating the
|
|
588
|
+
shared `button { color: inherit }` (0,0,1). An explicit `color:
|
|
589
|
+
inherit` here would be 0,1,1 and would defeat the tone-aware color. */
|
|
590
|
+
button[variant="glass"],
|
|
591
|
+
segmented-control[variant="glass"] {
|
|
592
|
+
background: transparent;
|
|
593
|
+
border: 0.5px solid color-mix(in srgb, var(--black) var(--alpha-100), transparent);
|
|
594
|
+
backdrop-filter: blur(20px) brightness(0.95) saturate(1.5);
|
|
595
|
+
-webkit-backdrop-filter: blur(20px) brightness(0.95) saturate(1.5);
|
|
596
|
+
box-shadow: none;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
/* `background:` shorthand (not `background-image`) resets background-color
|
|
600
|
+
back to transparent — necessary because the default-button hover rule
|
|
601
|
+
`button:not([material]):hover` fires on glass too (glass has no
|
|
602
|
+
material) at equal specificity and would otherwise paint a 96% white
|
|
603
|
+
surface underneath this overlay. */
|
|
604
|
+
button[variant="glass"]:hover:not(:disabled) {
|
|
605
|
+
background: linear-gradient(var(--color-overlay-hover), var(--color-overlay-hover));
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
button[variant="glass"]:active:not(:disabled) {
|
|
609
|
+
background: linear-gradient(var(--color-overlay-active), var(--color-overlay-active));
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
[tone="dark"] button[variant="glass"],
|
|
613
|
+
[tone="dark"] segmented-control[variant="glass"] {
|
|
614
|
+
border-color: var(--tint-400);
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
/* Material composition with controls (option B in the design notes —
|
|
618
|
+
contained override rather than refactoring materials.css globally).
|
|
619
|
+
Material is a passive-surface primitive: it locks color, draws a
|
|
620
|
+
fixed-alpha hairline, and a default-variant `:hover` would replace
|
|
621
|
+
its background entirely. These overrides let `<button material="...">`
|
|
622
|
+
and `<segmented-control material="...">` compose:
|
|
623
|
+
|
|
624
|
+
- `color: inherit` so the tone-aware text cascade still applies
|
|
625
|
+
(material sets a fixed `--color-text` which doesn't flip on dark).
|
|
626
|
+
- `box-shadow: none` because both materials want a flat surface; the
|
|
627
|
+
default control shadow fights glass especially.
|
|
628
|
+
- hover/active paint via `background-image` (a flat linear-gradient)
|
|
629
|
+
so the material's `background-color` is preserved underneath.
|
|
630
|
+
- dark-tone bumps the hairline to `--tint-400` so the rim reads on
|
|
631
|
+
dark backdrops (material's locked black-alpha border vanishes
|
|
632
|
+
against tone-dark patches). */
|
|
633
|
+
button[material],
|
|
634
|
+
segmented-control[material] {
|
|
635
|
+
color: inherit;
|
|
636
|
+
box-shadow: none;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
button[material]:hover:not(:disabled) {
|
|
640
|
+
background-image: linear-gradient(var(--color-overlay-hover), var(--color-overlay-hover));
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
button[material]:active:not(:disabled) {
|
|
644
|
+
background-image: linear-gradient(var(--color-overlay-active), var(--color-overlay-active));
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
[tone="dark"] button[material],
|
|
648
|
+
[tone="dark"] segmented-control[material] {
|
|
649
|
+
border-color: var(--tint-400);
|
|
650
|
+
}
|
|
651
|
+
|
|
530
652
|
button:disabled {
|
|
531
653
|
background: var(--tint-300);
|
|
532
654
|
border-color: transparent;
|
|
@@ -537,18 +659,26 @@ button:disabled {
|
|
|
537
659
|
/* Default-variant button on a dark surface — drop the chromed light fill,
|
|
538
660
|
pick up a brighter white-tint than inputs (15% vs 8%) to read as raised
|
|
539
661
|
rather than recessed, and replace the ineffective black drop shadow
|
|
540
|
-
with a top-edge inset highlight (macOS NSButton-on-dark convention).
|
|
541
|
-
|
|
662
|
+
with a top-edge inset highlight (macOS NSButton-on-dark convention).
|
|
663
|
+
`:not([material])` opts the tone-driven default fill OUT when the
|
|
664
|
+
element has explicit `material` — material owns the surface in that
|
|
665
|
+
case, regardless of tone. */
|
|
666
|
+
:where([tone="dark"]) button:not([variant]):not([material]):not(:disabled),
|
|
667
|
+
:where([tone="dark"]) button[variant="default"]:not([material]):not(:disabled),
|
|
668
|
+
:where([tone="dark"]) segmented-control:not([variant]):not([material]),
|
|
669
|
+
:where([tone="dark"]) segmented-control[variant="default"]:not([material]) {
|
|
542
670
|
background: var(--tint-200);
|
|
543
671
|
border-color: var(--tint-300);
|
|
544
672
|
box-shadow: inset 0 0.5px 0 var(--tint-300);
|
|
545
673
|
}
|
|
546
674
|
|
|
547
|
-
[tone="dark"] button:not([variant]):hover:not(:disabled)
|
|
675
|
+
:where([tone="dark"]) button:not([variant]):hover:not(:disabled),
|
|
676
|
+
:where([tone="dark"]) button[variant="default"]:hover:not(:disabled) {
|
|
548
677
|
background: var(--tint-300);
|
|
549
678
|
}
|
|
550
679
|
|
|
551
|
-
[tone="dark"] button:not([variant]):active:not(:disabled)
|
|
680
|
+
:where([tone="dark"]) button:not([variant]):active:not(:disabled),
|
|
681
|
+
:where([tone="dark"]) button[variant="default"]:active:not(:disabled) {
|
|
552
682
|
background: var(--tint-400);
|
|
553
683
|
box-shadow: inset 0 1px 2px rgb(0 0 0 / 0.20);
|
|
554
684
|
}
|
|
@@ -558,39 +688,45 @@ button:disabled {
|
|
|
558
688
|
one (e.g. a paper popover anchored inside dark chrome). Same
|
|
559
689
|
specificity as the dark-tone rules above; source order makes the
|
|
560
690
|
closer light-tone scope win for slotted descendants. */
|
|
561
|
-
[tone="light"] button:not([variant]):not(:disabled)
|
|
691
|
+
:where([tone="light"]) button:not([variant]):not([material]):not(:disabled),
|
|
692
|
+
:where([tone="light"]) button[variant="default"]:not([material]):not(:disabled),
|
|
693
|
+
:where([tone="light"]) segmented-control:not([variant]):not([material]),
|
|
694
|
+
:where([tone="light"]) segmented-control[variant="default"]:not([material]) {
|
|
562
695
|
background: var(--color-surface);
|
|
563
696
|
border-color: var(--tint-200);
|
|
564
697
|
box-shadow: var(--shadow-control);
|
|
565
698
|
}
|
|
566
699
|
|
|
567
|
-
[tone="light"] button:not([variant]):hover:not(:disabled) {
|
|
700
|
+
:where([tone="light"]) button:not([variant]):hover:not(:disabled) {
|
|
568
701
|
background: color-mix(in srgb, var(--color-surface) 96%, black);
|
|
569
702
|
}
|
|
570
703
|
|
|
571
|
-
[tone="light"] button:not([variant]):active:not(:disabled) {
|
|
704
|
+
:where([tone="light"]) button:not([variant]):active:not(:disabled) {
|
|
572
705
|
background: color-mix(in srgb, var(--color-surface) 88%, black);
|
|
573
706
|
box-shadow: inset 0 1px 2px rgb(0 0 0 / 0.08);
|
|
574
707
|
}
|
|
575
708
|
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
709
|
+
/* Size opt-in: `size="sm"` swaps to the smaller control-height token.
|
|
710
|
+
Same applies to `<segmented-control size="sm">` so the host stretches
|
|
711
|
+
children correctly, and to `<button size="sm" icon-only>` so the
|
|
712
|
+
square footprint stays at the smaller control unit. The default
|
|
713
|
+
(md, 28px) is set on the base `<button>` rule above; only sm needs
|
|
714
|
+
an explicit override. */
|
|
715
|
+
button[size="sm"],
|
|
716
|
+
segmented-control[size="sm"] {
|
|
717
|
+
height: var(--control-height-sm);
|
|
579
718
|
}
|
|
580
719
|
|
|
581
|
-
button[size="
|
|
582
|
-
|
|
583
|
-
padding: var(--space-6) var(--space-12);
|
|
720
|
+
button[size="sm"][icon-only] {
|
|
721
|
+
width: var(--control-height-sm);
|
|
584
722
|
}
|
|
585
723
|
|
|
586
|
-
/* Square icon-only buttons.
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
724
|
+
/* Square icon-only buttons. Width = control height so the footprint is
|
|
725
|
+
square at the same vertical size as a text-button sibling. Chrome is
|
|
726
|
+
determined by the variant (default = chromed pill, ghost = chromeless
|
|
727
|
+
overlay). */
|
|
590
728
|
button[icon-only] {
|
|
591
|
-
width:
|
|
592
|
-
height: 24px;
|
|
593
|
-
min-height: 0;
|
|
729
|
+
width: var(--control-height-md);
|
|
594
730
|
padding: 0;
|
|
595
731
|
}
|
|
596
732
|
|