@telepath-computer/television 1.3.7 → 1.3.9

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.
@@ -709,7 +709,7 @@ The public icon names are:
709
709
  - `phone`, `web`, `tag`, `bookmark`, `table`, `code`, `group`, `up`, `down`
710
710
  - `money`, `bank`, `card`, `wallet`, `gauge`, `activity`, `trend-up`
711
711
  - `trend-down`, `database`, `server`, `deploy`, `list`, `terminal`, `branch`
712
- - `shield`, `select`
712
+ - `shield`, `select`, `sidebar`
713
713
 
714
714
  Choose a name from this catalog. When the catalog has no suitable glyph, use a
715
715
  text label or an emoji.
@@ -573,7 +573,7 @@ These four tokens are enough to replace the main surfaces and text, but they do
573
573
  --sidebar-background: var(--color-surface-muted);
574
574
  --sidebar-text-color: var(--color-text);
575
575
  --sidebar-shadow: none;
576
- --sidebar-border-color: color-mix(in oklch, var(--color-surface), oklch(from var(--color-surface) 1 0 h) var(--alpha-15));
576
+ --sidebar-border-color: var(--color-border);
577
577
  --sidebar-border: var(--panel-border);
578
578
  --sidebar-heading-text-color: var(--color-text-muted);
579
579
  --sidebar-heading-font-size: var(--text-md);
@@ -618,6 +618,18 @@ These four tokens are enough to replace the main surfaces and text, but they do
618
618
  --wallpaper-overlay-blur: 0px;
619
619
 
620
620
  /* Navbar and empty stage. */
621
+ /* The one height of the app's top bars — the navbar and the sidebar
622
+ titlebar — so controls across the boundary share a band. The value
623
+ centres the macOS traffic lights (15px top inset, 14px buttons) with even
624
+ air above and below the cluster. */
625
+ --app-bar-height: 44px;
626
+ /* Match the top inset of the standard 24px navbar controls. */
627
+ --app-bar-padding-inline: 10px;
628
+ /* The horizontal room an app bar reserves for the traffic-light cluster
629
+ when it holds the window's top-left corner. Only the desktop shell has
630
+ the cluster, and it marks the document root; in a browser the
631
+ reservation is zero. */
632
+ --traffic-light-x-reserve: 0px;
621
633
  --navbar-background: transparent;
622
634
  --navbar-text-color: var(--wallpaper-overlay-text-color);
623
635
  --navbar-border-color: transparent;
@@ -637,7 +649,7 @@ These four tokens are enough to replace the main surfaces and text, but they do
637
649
  a theme's root override wins — a declaration on the component would
638
650
  shadow it. */
639
651
  --page-gap: var(--space-24);
640
- --page-inset: var(--space-12);
652
+ --page-inset: var(--space-8);
641
653
  --page-inactive-blur: 0px;
642
654
  --page-inactive-backdrop-blur: var(--wallpaper-overlay-blur);
643
655
  --page-inactive-opacity: 0.55;
@@ -674,9 +686,8 @@ These four tokens are enough to replace the main surfaces and text, but they do
674
686
  --drag-action-shadow: var(--shadow-sm);
675
687
 
676
688
 
677
- /* The air between the navbar and the stage ([[ui/app/styles.css]] consumes
678
- it for the bar's seat and the stage's top padding). */
679
- --navbar-gap: var(--space-8);
689
+ /* The shared top band supplies equal air above and below the tabs. */
690
+ --navbar-gap: 0px;
680
691
 
681
692
  /* The artifact frame's radius ([[ui/app/artifact-frame/artifact-frame.frame]]);
682
693
  artifact documents also apply it by reference
@@ -687,6 +698,15 @@ These four tokens are enough to replace the main surfaces and text, but they do
687
698
  :where([data-theme="dark"]) {
688
699
  --app-wallpaper: var(--app-wallpaper-image) center / cover no-repeat, var(--neutral-950);
689
700
  }
701
+
702
+ /* The desktop shell marks the document root; the traffic-light reservation
703
+ exists only there. */
704
+ :root[data-platform="electron"] {
705
+ /* The cluster ends at 73px (15px inset, three 14px buttons, two 8px gaps);
706
+ the bar adds its outer padding to this reservation, leaving air
707
+ before its first control. */
708
+ --traffic-light-x-reserve: 72px;
709
+ }
690
710
  ```
691
711
 
692
712
  ## Application structure for this release
@@ -708,7 +728,18 @@ An artifact frame belongs to the application document. The document inside its i
708
728
  <div id="foreground-overlay" inert aria-hidden="true"></div>
709
729
  ```
710
730
 
711
- `#app` owns the application layout and suppresses ordinary text selection in its contents; readable regions can explicitly restore selection. `.app-sidebar` holds the channel list. The sidebar defaults to 260 pixels wide, resizes between 160 and 350 pixels, and remembers the chosen width per browser. `.app-main` holds the wallpaper, navbar and stage. `.app-main > .top-bar` supplies the inset shared with the stage. `#app:focus` suppresses a ring on the container itself. The wallpaper region establishes a backdrop boundary so overlay blur samples the wallpaper rather than adjacent content, while fixed popovers retain viewport positioning.
731
+ `#app` owns the application layout and suppresses ordinary text selection in its contents; readable regions can explicitly restore selection. `.app-sidebar` holds the channel list. The sidebar defaults to 260 pixels wide, resizes between 160 and 350 pixels, and remembers the chosen width per browser; it can also collapse entirely, the navbar then opening with an expand control and — while a channel is open — a channel-switcher popover, with the collapsed state remembered per browser. `.app-main` holds the wallpaper, navbar and stage. `.app-main > .top-bar` supplies the horizontal control inset; stage padding is separate. Preserve the shell width constraints so overflowing tabs and pages scroll inside their regions without widening the document. `#app:focus` suppresses a ring on the container itself. The wallpaper region establishes a backdrop boundary so overlay blur samples the wallpaper rather than adjacent content, while fixed popovers retain viewport positioning.
732
+
733
+ During collapse or expansion, `#app[data-sidebar-motion]` contains both resting layouts and one travelling control:
734
+
735
+ ```html
736
+ <button class="sidebar-motion-toggle" variant="ghost" icon aria-label="Collapse sidebar">
737
+ <span class="expanded-paint" aria-hidden="true"><tv-icon name="sidebar" size="sm"></tv-icon></span>
738
+ <span class="collapsed-paint" aria-hidden="true"><tv-icon name="sidebar" size="sm"></tv-icon></span>
739
+ </button>
740
+ ```
741
+
742
+ The button is a direct child of `#app`, alongside `.app-sidebar` and `.app-main`. Its two spans carry the sidebar and navbar treatments, clipped at the moving boundary. The resting collapse and expand controls are hidden during motion. Theme rules that change those controls should give the corresponding spans the same treatment. Preserve the application-controlled position, clipping and opacity; shell transitions and animations are suppressed during this motion so they cannot trail the prescribed pose. Artifact documents remain independent.
712
743
 
713
744
  For the optional iframe surfaces and protected overlay stacking, see [Theme effects and scripts](#theme-effects-and-scripts).
714
745
 
@@ -718,32 +749,50 @@ For the optional iframe surfaces and protected overlay stacking, see [Theme effe
718
749
  <nav class="sidebar">
719
750
  <header class="sidebar-titlebar">
720
751
  <button class="channel-create" variant="ghost" icon aria-label="New channel" title="New channel">…</button>
752
+ <span class="toolbar-separator" role="separator" aria-orientation="vertical"></span>
753
+ <button class="sidebar-collapse" variant="ghost" icon aria-label="Collapse sidebar" title="Collapse sidebar">…</button>
721
754
  </header>
722
- <div class="sidebar-body" role="listbox" aria-label="Channels">
723
- <div class="channel-group" role="group" aria-labelledby="channel-group-pinned">
724
- <div class="channel-group-label" id="channel-group-pinned">Pinned</div>
725
- <div class="channel-row">
726
- <div class="channel" role="option" aria-selected="true" tabindex="-1">…</div>
727
- <button class="channel-menu-trigger" icon variant="ghost" size="sm" tabindex="-1" aria-hidden="true">…</button>
755
+ <div class="sidebar-body">
756
+ <div class="channel-list" role="listbox" aria-label="Channels">
757
+ <div class="channel-group" role="group" aria-labelledby="channel-group-pinned">
758
+ <div class="channel-group-label" id="channel-group-pinned">Pinned</div>
759
+ <div class="channel-row">
760
+ <div class="channel" role="option" aria-selected="true" tabindex="-1">…</div>
761
+ <button class="channel-menu-trigger" icon variant="ghost" size="sm" tabindex="-1" aria-haspopup="menu">…</button>
762
+ </div>
728
763
  </div>
764
+ <!-- The Recent group uses channel-group-unpinned. -->
729
765
  </div>
730
- <!-- The Recent group uses channel-group-unpinned. -->
731
766
  </div>
732
767
  </nav>
733
768
  ```
734
769
 
735
- `.sidebar::after` paints the light inner seam over the sidebar background, alongside its optional soft shadow. `.app-main::before` paints the adjoining fractional dark seam over the wallpaper. Both are pointer-transparent. The sidebar accepts an independent native border; the main region reserves the exterior seam track. `.sidebar-titlebar` reserves the window controls and drag area. `.sidebar-body` scrolls both groups together; `.sidebar:has(.sidebar-body[continues-start]) .sidebar-titlebar` draws the top boundary while content extends above the viewport. `.channel-group + .channel-group` separates the groups, and `.channel-group-label` styles their labels. `.channel-create` sits at the right of the titlebar and is excluded from window dragging.
770
+ `.sidebar::after` paints the light inner seam over the sidebar background, alongside its optional soft shadow. `.app-main::before` paints the adjoining fractional dark seam over the wallpaper. Both are pointer-transparent. The sidebar accepts an independent native border; the main region reserves the exterior seam track. `.sidebar-titlebar` reserves the window controls and drag area; the reservation applies in the desktop shell, which marks the document root `data-platform="electron"`. `.sidebar-body` scrolls both groups together; `.sidebar:has(.sidebar-body[continues-start]) .sidebar-titlebar` draws the top boundary while content extends above the viewport. `.channel-list` holds the groups and their spacing, composed here and inside the collapsed navbar's switcher popover. `.channel-group + .channel-group` separates the groups, and `.channel-group-label` styles their labels. `.channel-create` and `.sidebar-collapse` sit at the right of the titlebar, split by `.toolbar-separator`, and are excluded from window dragging.
736
771
 
737
772
  The channel name and its menu trigger are siblings within the row. `.channel` owns the name box; `.channel[aria-selected="true"]` paints the selection. The selected row changes `.channel-menu-trigger` text color through `.channel-row:has(> .channel[aria-selected="true"])`. Shared foundation tints follow that color for hover, press and expanded states. A hover or an expanded trigger preserves the unselected row tint through `:where(.channel-row:hover, .channel-row:has(.channel-menu-trigger[aria-expanded="true"])) .channel:not([aria-selected="true"])`. Trigger visibility follows row hover, `:focus-visible`, selected state, and expanded state. Use the expanded trigger state when styling an open menu subject. Panels remain in their authored DOM position when opened.
738
773
 
739
774
  Renaming replaces the name and menu trigger with `input.channel-rename` and `button.channel-rename-commit`. The field uses shared native-input styling; these selectors retain its placement and the commit treatment in the same row seat. `.channel-row:has(.channel-rename)` keeps its focus ring visible. A dragged row carries `.channel-row.dragged`; an unpinning row also carries `.unpinning` and contains `.channel-drag-action` with a `tv-icon`. `.channel-row.dragged .channel` removes the spare trigger inset. `.channel-drag-action tv-icon` sizes the action glyph; `.channel-placeholder` marks the drop slot.
740
775
 
741
- Row menus use `tv-menu`, `tv-menu-item`, `hr`, and `tv-menu-item[intent="danger"]`. These shared elements supply menu styling and item states. Deletion uses the shared dialog outline below.
776
+ Channel context menus use `tv-menu`, `tv-menu-item`, `hr`, and `tv-menu-item[intent="danger"]`. An open menu is a sibling of the channel name and trigger inside `.channel-row`; its trigger carries `aria-expanded="true"`. The same composition appears in the sidebar and the channel popover. These shared elements supply menu styling and item states. Deletion uses the shared dialog outline below.
742
777
 
743
778
  ### Navbar and tabs
744
779
 
745
780
  ```html
746
781
  <header class="top-bar">
782
+ <!-- The lead group renders while the channel sidebar is collapsed. -->
783
+ <div class="top-bar-lead">
784
+ <button class="sidebar-expand" variant="ghost" icon aria-label="Show sidebar" title="Show sidebar">…</button>
785
+ <button class="channel-switcher" variant="ghost" target aria-haspopup="listbox"><span class="channel-switcher-name">…</span>…</button>
786
+ <!-- The open channel popover stays inside the lead group. -->
787
+ <tv-popover open class="channel-switcher-pop">
788
+ <div class="channel-switcher-pop-body">
789
+ <div class="channel-list" role="listbox" aria-label="Channels">…</div>
790
+ </div>
791
+ <footer class="channel-switcher-pop-footer">
792
+ <button class="channel-create" variant="ghost" icon aria-label="New channel">…</button>
793
+ </footer>
794
+ </tv-popover>
795
+ </div>
747
796
  <div class="tab-strip" role="tablist">
748
797
  <div class="tab" role="tab" aria-selected="true" tabindex="0">
749
798
  <span class="tab-label">…</span>
@@ -753,11 +802,17 @@ Row menus use `tv-menu`, `tv-menu-item`, `hr`, and `tv-menu-item[intent="danger"
753
802
  </header>
754
803
  ```
755
804
 
756
- `.top-bar` centers `.top-bar > .tab-strip` while tabs fit and keeps `.top-bar-controls` at the trailing edge. With `.tab-strip[data-overflow]`, the strip extends to the left wallpaper boundary and takes the space up to the fixed controls. Both child bands exclude native window dragging; empty bar ground remains available for it.
805
+ `.top-bar` centers `.top-bar > .tab-strip` while tabs fit and keeps `.top-bar-controls` at the trailing edge. With `.tab-strip[data-overflow]`, the strip scrolls within the remaining space. The layout retains an 8-pixel gap before the strip and 48 pixels of draggable ground before the trailing controls; tabs and their edge fades stay clear of both control groups. Both child bands exclude native window dragging; empty bar ground remains available for it. The navbar and sidebar titlebar share the application bar height, with their controls vertically centered.
806
+
807
+ `.top-bar-lead` appears only while the channel sidebar is collapsed: `.sidebar-expand` reopens it, and — only while a channel is open — `.channel-switcher` (with `.channel-switcher-name` truncating the label) opens `.channel-switcher-pop`, a `tv-popover` composing the channel list over a pinned `.channel-switcher-pop-footer` create bar, its scrolling body `.channel-switcher-pop-body`. While the lead group is present the leading flank refuses to shrink below it, so a long name pushes the strip off-centre rather than clipping.
808
+
809
+ The popover retains channel row spacing, type and shape, but assigns its colors locally from ordinary panel and option roles. Sidebar-specific color overrides therefore do not set its colors. `.channel-switcher-pop .channel[aria-selected="true"]` keeps primary selection colors. Unselected rows use a neutral highlight for hover, keyboard focus or an open context menu; `.channel-switcher-pop .channel:focus-visible` has no outline. While keyboard focus is visible, a stationary pointer does not highlight a second row. Preserve these distinctions when styling the popover.
810
+
811
+ All `.top-bar tv-popover` panels have a 600-pixel maximum height, reduced further when the viewport requires it. The channel popover keeps its create footer fixed while `.channel-switcher-pop-body` scrolls. The shared dragged-row, unpin action, placeholder and rename selectors above also apply here. A channel context menu is a child panel of the channel popover; opening it keeps the parent visible, and closing the parent closes its children.
757
812
 
758
- `.top-bar-controls > button:not([intent])` follows the navbar text token. Ordinary ghost buttons share the wallpaper-overlay background, border and blur; hover and pressed/expanded states replace the complete background. Buttons with an intent retain their semantic colours.
813
+ Ordinary controls in both groups — `.top-bar :is(.top-bar-controls, .top-bar-lead) > button:not([intent])` — follow the navbar text token. Ordinary ghost buttons share the wallpaper-overlay background, border and blur; hover and pressed/expanded states replace the complete background. Buttons with an intent retain their semantic colours.
759
814
 
760
- `.tab-strip` scrolls horizontally. Only the right edge fades while more tabs remain to the right. `.tab[data-item-edge-fade]` carries a mask on the individual tab, preserving its backdrop blur; the strip has no mask. `.tab` owns the pill; `.tab-label` truncates its text. `.tab[data-compression="hugging"]` retains intrinsic width, while `.tab[data-compression="capped"]` permits compression to the floor. `.tab:hover:not([aria-selected="true"])` and `.tab:active:not([aria-selected="true"])` style unselected interaction; `.tab[aria-selected="true"]` paints the current page cue. `.tab.dragged` raises the carried tab, and `span.tab-placeholder[aria-hidden="true"]` occupies its drop slot. Nonselected tabs carry `aria-selected="false"` and `tabindex="-1"`.
815
+ `.tab-strip` scrolls horizontally. An edge fades while more tabs remain past it. `.tab[data-item-edge-fade]` carries a mask on the individual tab, preserving its backdrop blur; the strip has no mask. `.tab` owns the pill; `.tab-label` truncates its text. `.tab[data-compression="hugging"]` retains intrinsic width, while `.tab[data-compression="capped"]` permits compression to the floor. `.tab:hover:not([aria-selected="true"])` and `.tab:active:not([aria-selected="true"])` style unselected interaction; `.tab[aria-selected="true"]` paints the current page cue. `.tab.dragged` raises the carried tab, and `span.tab-placeholder[aria-hidden="true"]` occupies its drop slot. Nonselected tabs carry `aria-selected="false"` and `tabindex="-1"`.
761
816
 
762
817
  ### Stage and pages
763
818
 
@@ -798,7 +853,7 @@ An empty channel adds `.stage-empty`, containing an artifact `tv-icon` and a par
798
853
  </div>
799
854
  ```
800
855
 
801
- `.artifact-frame` owns the border, curve, surface and broad shadow. Its `::before` paints the sharp rim and its `::after` paints a pointer-transparent masked inner highlight over document and titlebar. Background pages suppress the broad shadow while retaining the exterior rim. `.artifact-frame-clip > :is(iframe, webview)` fills the document area through the one-pixel padding track. `.artifact-frame-clip` clips the document and titlebar while the outer frame lets both shadows extend outward. `.artifact-title-bar` paints the lower band, `.artifact-title-bar .artifact-title` takes the remaining width and truncates, and `.artifact-bar-divider` separates navigation from the menu trigger. The two direction buttons use `disabled` to show unavailable history. The document repeats the top clipping radius where required for composited iframe/webview rendering.
856
+ `.artifact-frame` owns the border, curve, surface and broad shadow. Its `::before` paints the sharp rim and its `::after` paints a pointer-transparent masked inner highlight over document and titlebar. Background pages suppress the broad shadow while retaining the exterior rim. `.artifact-frame-clip > :is(iframe, webview)` fills the document area through the one-pixel padding track. `.artifact-frame-clip` clips the document and titlebar while the outer frame lets both shadows extend outward. `.artifact-title-bar` paints the lower band, `.artifact-title-bar .artifact-title` takes the remaining width and truncates, and `.artifact-bar-divider` separates navigation from the menu trigger. The two direction buttons use `disabled` to show unavailable history. In browser demo mode, the frame of an external web page wraps its artifact `tv-icon` in `button.artifact-return` (`icon`, ghost, `sm`) and shows no direction controls. The document repeats the top clipping radius where required for composited iframe/webview rendering.
802
857
 
803
858
  The artifact menu uses the same shared menu elements and placement as channel menus. Deletion uses the shared dialog outline below.
804
859