simdeck 0.1.0 → 0.1.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.
@@ -1 +0,0 @@
1
- :root{color-scheme:light dark;--bg: #1e1e1e;--surface: #252526;--surface-hover: #2d2d2e;--border: #3c3c3c;--border-subtle: #333333;--text: #cccccc;--text-secondary: #888888;--text-muted: #666666;--accent: #0078d4;--accent-text: #ffffff;--success: #4ec9b0;--success-bg: rgba(78, 201, 176, .12);--error: #f14c4c;--error-bg: rgba(241, 76, 76, .1);--canvas-bg: #1a1a1a;--canvas-dot: rgba(255, 255, 255, .04);--bezel: #111111;--bezel-highlight: rgba(255, 255, 255, .06);--screen-bg: #000000}@media(prefers-color-scheme:light){:root{--bg: #f3f3f3;--surface: #ffffff;--surface-hover: #e8e8e8;--border: #d4d4d4;--border-subtle: #e0e0e0;--text: #1e1e1e;--text-secondary: #656565;--text-muted: #999999;--canvas-bg: #e0e0e0;--canvas-dot: rgba(0, 0, 0, .05);--bezel: #1a1a1a;--bezel-highlight: rgba(255, 255, 255, .08);--error: #cd3131;--error-bg: rgba(205, 49, 49, .08);--success: #16825d;--success-bg: rgba(22, 130, 93, .08)}}*,*:before,*:after{box-sizing:border-box;margin:0}body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,system-ui,sans-serif;font-size:13px;background:var(--bg);color:var(--text);overflow:hidden}button,input{font:inherit}button{cursor:pointer}.app{display:grid;grid-template-rows:auto 1fr;height:100vh;height:100dvh;overflow:hidden}.toolbar{display:flex;align-items:center;justify-content:space-between;gap:8px;height:40px;padding:0 8px;background:var(--surface);border-bottom:1px solid var(--border);-webkit-user-select:none;user-select:none;flex-shrink:0}.toolbar-left,.toolbar-right{display:flex;align-items:center;gap:6px;min-width:0}.toolbar-left{flex:1;min-width:0}.toolbar-sim-info{display:flex;align-items:center;gap:8px;min-width:0;overflow:hidden}.toolbar-sim-copy{display:flex;flex-direction:column;gap:1px;min-width:0}.toolbar-sim-title-row{display:flex;align-items:center;gap:6px;min-width:0}.toolbar-sim-name{font-weight:600;font-size:13px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.toolbar-sim-detail{color:var(--text-secondary);font-size:12px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.toolbar-status-dot{width:8px;height:8px}.toolbar-actions{display:flex;align-items:center;gap:2px;min-width:0}.main{display:flex;overflow:hidden;min-height:0;min-width:0;width:100%;height:100%}.viewport-zoom-dock{position:absolute;right:16px;bottom:16px;z-index:8}@media(max-width:600px){.app{grid-template-rows:auto minmax(0,1fr)}.toolbar{height:auto;min-height:40px;flex-wrap:wrap;align-items:stretch;gap:6px;padding:6px}.toolbar-left,.toolbar-right{width:100%}.toolbar-left{display:grid;grid-template-columns:auto auto minmax(0,1fr)}.toolbar-right{overflow-x:auto;padding-bottom:2px;scrollbar-width:none}.toolbar-right::-webkit-scrollbar{display:none}.toolbar-actions{width:max-content;min-width:100%}.main{flex-direction:column}.debug-grid{grid-template-columns:1fr}.debug-row-wide{grid-column:auto}.viewport-zoom-dock{right:12px;bottom:12px;max-width:calc(100vw - 24px)}.menu-popover{width:min(260px,calc(100vw - 16px))}.hierarchy-panel{width:100%;min-width:0;max-width:none;max-height:min(42dvh,360px);border-right:0;border-bottom:1px solid var(--border)}.toolbar-sim-detail{display:none}}.tbtn{display:inline-flex;align-items:center;justify-content:center;gap:4px;height:26px;min-width:26px;padding:0 8px;border:none;border-radius:5px;background:transparent;color:var(--text);font-size:12px;white-space:nowrap;transition:background .1s}.tbtn:hover,.tbtn.active{background:var(--surface-hover)}.tbtn:disabled{cursor:default;opacity:.45}.tbtn:disabled:hover{background:transparent}.tbtn.accent{background:var(--accent);color:var(--accent-text);font-weight:600}.tbtn.accent:hover{filter:brightness(1.15)}.icon-btn{padding:0}.state-dot{width:6px;height:6px;border-radius:50%;background:var(--text-muted);flex-shrink:0}.state-dot.booted{background:var(--success)}.zoom-controls{display:flex;align-items:center;gap:2px}.zoom-controls-floating{gap:4px;flex-wrap:wrap;justify-content:flex-end;padding:6px;border:1px solid var(--border);border-radius:10px;background:color-mix(in srgb,var(--surface) 94%,transparent);box-shadow:0 12px 24px #0003;-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px)}.zoom-label{font-size:11px;font-variant-numeric:tabular-nums;color:var(--text-secondary);min-width:36px;text-align:center;-webkit-user-select:none;user-select:none}.debug-panel{padding:10px 12px 12px;border:1px solid var(--border);border-radius:8px;background:color-mix(in srgb,var(--surface) 96%,transparent);box-shadow:0 12px 32px #00000047;-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px)}.debug-panel-popover{position:absolute;top:calc(100% + 8px);right:0;width:min(360px,calc(100vw - 16px));z-index:30}.debug-panel-inline{width:100%;box-shadow:none;-webkit-backdrop-filter:none;backdrop-filter:none}.debug-panel-header{margin-bottom:10px;color:var(--text-secondary);font-size:11px;font-weight:700;letter-spacing:.04em;text-transform:uppercase}.debug-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px 12px}.debug-row{min-width:0}.debug-row-wide{grid-column:1 / -1}.debug-label{margin-bottom:2px;color:var(--text-muted);font-size:10px;font-weight:600;letter-spacing:.04em;text-transform:uppercase}.debug-value{color:var(--text);font-size:12px;font-variant-numeric:tabular-nums}.debug-value-wrap{line-height:1.4;white-space:normal;word-break:break-word}.menu-wrap{position:relative}.menu-popover{position:absolute;top:calc(100% + 6px);left:0;width:260px;max-height:min(72vh,560px);display:flex;flex-direction:column;background:var(--surface);border:1px solid var(--border);border-radius:8px;box-shadow:0 12px 32px #00000047;overflow:hidden;z-index:20}.sidebar-search{width:100%;padding:8px 12px;border:none;border-bottom:1px solid var(--border-subtle);background:transparent;color:var(--text);outline:none;font-size:12px;flex-shrink:0}.sidebar-search:focus{background:var(--surface-hover)}.sidebar-search::placeholder{color:var(--text-muted)}.sim-list{flex:1;overflow-y:auto;padding:4px;scrollbar-width:thin;scrollbar-color:var(--border) transparent}.sim-list::-webkit-scrollbar{width:6px}.sim-list::-webkit-scrollbar-track{background:transparent}.sim-list::-webkit-scrollbar-thumb{background:var(--border);border-radius:3px}.menu-divider{height:1px;background:var(--border-subtle)}.menu-actions{display:flex;flex-direction:column;padding:4px}.menu-debug-panel{padding:6px 4px 4px}.menu-action{width:100%;padding:8px 10px;border:none;border-radius:6px;background:transparent;color:var(--text);text-align:left;font-size:12px;transition:background .1s}.menu-action:hover{background:var(--surface-hover)}.sim-item{display:flex;flex-direction:column;gap:2px;width:100%;padding:7px 10px;border:none;border-radius:6px;background:transparent;color:inherit;text-align:left;transition:background .1s}.sim-item:hover{background:var(--surface-hover)}.sim-item.selected{background:var(--accent);color:#fff}.sim-item.selected .sim-item-meta{color:#ffffffb3}.sim-item.selected .state-dot{background:#ffffff80}.sim-item.selected .state-dot.booted{background:#80f0c8}.sim-item-name{font-size:12px;font-weight:600;line-height:1.3}.sim-item-meta{display:flex;align-items:center;gap:6px;font-size:11px;color:var(--text-secondary);line-height:1.3}.list-empty{padding:20px 12px;text-align:center;color:var(--text-muted);font-size:12px}.hierarchy-panel{position:relative;flex:0 0 auto;min-width:240px;max-width:55vw;display:flex;flex-direction:column;border-right:1px solid var(--border);background:var(--surface);overflow:hidden}.hierarchy-tools{display:flex;align-items:center;gap:4px;min-height:34px;padding:4px 6px;border-bottom:1px solid var(--border-subtle)}.hierarchy-tools .tbtn.active{color:var(--accent-text);background:var(--accent)}.hierarchy-header{display:flex;align-items:center;justify-content:space-between;gap:12px;min-height:52px;padding:9px 10px 9px 12px;border-bottom:1px solid var(--border-subtle)}.hierarchy-title{color:var(--text);font-size:12px;font-weight:700;letter-spacing:.04em;text-transform:uppercase}.hierarchy-subtitle{max-width:190px;overflow:hidden;color:var(--text-secondary);font-size:11px;text-overflow:ellipsis;white-space:nowrap}.hierarchy-tree{flex:1;overflow:auto;padding:4px;scrollbar-width:thin;scrollbar-color:var(--border) transparent}.hierarchy-source{display:flex;align-items:center;gap:6px;min-height:26px;padding:3px 6px 6px;color:var(--text-muted);font-size:10px}.hierarchy-source-switcher{display:inline-flex;align-items:center;gap:4px;flex:0 0 auto}.hierarchy-source-pill{display:inline-flex;align-items:center;max-width:150px;overflow:hidden;padding:2px 6px;border:1px solid var(--border-subtle);border-radius:999px;background:color-mix(in srgb,var(--surface-hover) 70%,transparent);color:var(--text-secondary);font-weight:700;letter-spacing:.03em;line-height:1.2;text-overflow:ellipsis;text-transform:uppercase;white-space:nowrap}.hierarchy-source-pill:not(:disabled):hover{background:var(--surface-hover);color:var(--text)}.hierarchy-source-pill.active{border-color:color-mix(in srgb,var(--accent) 58%,var(--border));background:color-mix(in srgb,var(--accent) 18%,transparent);color:var(--text)}.hierarchy-source-pill.source-in-app-inspector{border-color:color-mix(in srgb,var(--success) 50%,var(--border));background:var(--success-bg);color:color-mix(in srgb,var(--success) 82%,var(--text))}.hierarchy-source-pill.source-nativescript{border-color:color-mix(in srgb,#65d2ff 55%,var(--border));background:color-mix(in srgb,#65d2ff 16%,transparent);color:color-mix(in srgb,#65d2ff 82%,var(--text))}.hierarchy-source-pill.source-react-native{border-color:color-mix(in srgb,#61dafb 50%,var(--border));background:color-mix(in srgb,#61dafb 14%,transparent);color:color-mix(in srgb,#61dafb 78%,var(--text))}.hierarchy-source-pill.source-swiftui{border-color:color-mix(in srgb,#ff6b9d 50%,var(--border));background:color-mix(in srgb,#ff6b9d 14%,transparent);color:color-mix(in srgb,#ff6b9d 82%,var(--text))}.hierarchy-source-pill.source-native-ax{border-color:color-mix(in srgb,#d7ba7d 55%,var(--border));background:color-mix(in srgb,#d7ba7d 13%,transparent);color:color-mix(in srgb,#d7ba7d 82%,var(--text))}.hierarchy-source-pill.active{box-shadow:inset 0 0 0 1px currentColor}.hierarchy-source-note{min-width:0;overflow:hidden;color:var(--text-muted);text-overflow:ellipsis;white-space:nowrap}.hierarchy-node{display:flex;align-items:center;gap:2px;width:max-content;min-width:100%;min-height:26px;padding-block:1px;padding-right:8px;border-radius:5px;color:var(--text)}.hierarchy-node:hover{background:var(--surface-hover)}.hierarchy-node.selected{background:var(--accent);color:var(--accent-text)}.hierarchy-disclosure{width:16px;height:22px;flex:0 0 auto;padding:0;border:none;background:transparent;color:var(--text-secondary);font-size:11px;line-height:1}.hierarchy-disclosure.empty{visibility:hidden}.hierarchy-node-main{display:flex;align-items:center;gap:7px;flex:0 0 auto;width:max-content;height:24px;padding:0;border:none;background:transparent;color:inherit;text-align:left}.hierarchy-node-kind{color:var(--success);font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-size:11px;white-space:nowrap}.hierarchy-node-chain{display:inline-flex;align-items:center;justify-content:center;min-width:24px;height:16px;padding:0 5px;border:1px solid var(--border-subtle);border-radius:999px;color:var(--text-muted);font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-size:10px;line-height:1;white-space:nowrap}.hierarchy-node-chain-path{max-width:260px;overflow:hidden;color:var(--text-muted);font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-size:10px;text-overflow:ellipsis;white-space:nowrap}.hierarchy-node.selected .hierarchy-node-kind{color:color-mix(in srgb,var(--accent-text) 82%,var(--success))}.hierarchy-node-text{color:inherit;font-size:12px;white-space:nowrap}.hierarchy-node-source{color:var(--text-muted);font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-size:10px;white-space:nowrap}.hierarchy-node.selected .hierarchy-node-source{color:color-mix(in srgb,var(--accent-text) 72%,transparent)}.hierarchy-empty{padding:18px 10px;color:var(--text-muted);font-size:12px;line-height:1.4;text-align:center}.hierarchy-empty.error{color:var(--error)}.hierarchy-details-wrap{position:relative;flex:0 0 auto;min-height:132px;max-height:60%;overflow:hidden;border-top:1px solid var(--border-subtle);background:color-mix(in srgb,var(--surface) 94%,var(--bg))}.hierarchy-details{height:100%;overflow:auto;padding:12px}.hierarchy-details-title{margin-bottom:8px;color:var(--text-secondary);font-size:11px;font-weight:700;letter-spacing:.04em;text-transform:uppercase}.hierarchy-detail-row{display:grid;grid-template-columns:88px minmax(0,1fr);gap:8px;padding:3px 0;font-size:11px;line-height:1.35}.hierarchy-detail-label{color:var(--text-muted)}.hierarchy-detail-value{overflow-wrap:anywhere;color:var(--text)}.uikit-script{margin-top:14px;padding-top:12px;border-top:1px solid var(--border-subtle)}.uikit-script-header{display:flex;align-items:flex-start;justify-content:space-between;gap:10px;margin-bottom:8px}.uikit-script-title{color:var(--text-secondary);font-size:11px;font-weight:700;letter-spacing:.04em;text-transform:uppercase}.uikit-script-target,.uikit-script-result{color:var(--text-muted);font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-size:10px;line-height:1.35;overflow-wrap:anywhere}.uikit-script-run{padding:6px 8px;border:1px solid var(--border-subtle);border-radius:6px;background:color-mix(in srgb,var(--surface) 88%,var(--bg));color:var(--text);cursor:pointer;font-size:11px;font-weight:650}.uikit-script-run:hover:not(:disabled){border-color:var(--accent)}.uikit-script-run:disabled{cursor:default;opacity:.55}.uikit-script-form{display:grid;gap:6px}.uikit-script-input{width:100%;min-height:58px;resize:vertical;padding:7px 8px;border:1px solid var(--border-subtle);border-radius:7px;outline:none;background:var(--bg);color:var(--text);font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-size:11px}.uikit-script-input:focus{border-color:var(--accent)}.uikit-script-result{margin-top:7px}.uikit-script-error{margin-top:7px;color:var(--error);font-size:11px;line-height:1.35}.console-panel{flex:1;overflow:auto;overflow-anchor:none;padding:6px 0;background:color-mix(in srgb,var(--surface) 92%,var(--bg));color:var(--text);font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-size:11px;line-height:1.35;scrollbar-width:thin;scrollbar-color:var(--border) transparent}.console-filterbar{flex:0 0 auto;display:flex;align-items:center;justify-content:space-between;gap:5px;overflow:visible;padding:6px 8px;border-bottom:1px solid var(--border-subtle);background:color-mix(in srgb,var(--surface) 96%,var(--bg))}.console-text-filterbar{flex:0 0 auto;padding:6px 8px;border-bottom:1px solid var(--border-subtle);background:color-mix(in srgb,var(--surface) 94%,var(--bg))}.console-text-filter{width:100%;height:26px;padding:0 8px;border:1px solid var(--border-subtle);border-radius:6px;outline:none;background:var(--bg);color:var(--text);font-size:12px}.console-text-filter:focus{border-color:color-mix(in srgb,var(--accent) 60%,var(--border))}.console-text-filter::placeholder{color:var(--text-muted)}.console-filter-group{display:flex;align-items:center;gap:4px;flex-wrap:wrap}.console-filter-label{margin-right:2px;color:var(--text-muted);font-size:10px;font-weight:700;letter-spacing:.04em;text-transform:uppercase}.console-filter-chip{max-width:120px;overflow:hidden;padding:3px 7px;border:1px solid var(--border-subtle);border-radius:999px;background:transparent;color:var(--text-secondary);font-size:10px;line-height:1.2;text-overflow:ellipsis;white-space:nowrap}.console-filter-chip.active{border-color:color-mix(in srgb,var(--accent) 55%,var(--border));background:color-mix(in srgb,var(--accent) 18%,transparent);color:var(--text)}.console-filter-chip.level-error.active{border-color:color-mix(in srgb,var(--error) 70%,var(--border));background:color-mix(in srgb,var(--error) 20%,transparent);color:color-mix(in srgb,var(--error) 78%,var(--text))}.console-filter-chip.level-default.active{border-color:color-mix(in srgb,var(--text-secondary) 70%,var(--border));background:color-mix(in srgb,var(--text-secondary) 18%,transparent)}.console-filter-chip.level-info.active{border-color:color-mix(in srgb,var(--success) 70%,var(--border));background:var(--success-bg);color:color-mix(in srgb,var(--success) 78%,var(--text))}.console-filter-chip.level-debug.active{border-color:color-mix(in srgb,#d7ba7d 70%,var(--border));background:color-mix(in srgb,#d7ba7d 18%,transparent);color:color-mix(in srgb,#d7ba7d 78%,var(--text))}.console-service-menu-wrap{position:relative;flex:0 0 auto}.console-service-menu{position:absolute;top:calc(100% + 6px);right:0;z-index:32;width:min(280px,70vw);max-height:min(360px,58vh);overflow:auto;padding:5px;border:1px solid var(--border);border-radius:8px;background:var(--surface);box-shadow:0 12px 32px #00000047;scrollbar-width:thin;scrollbar-color:var(--border) transparent}.console-service-empty{padding:12px;color:var(--text-muted);font-size:11px;text-align:center}.console-service-option{display:flex;align-items:center;gap:8px;width:100%;min-height:28px;padding:5px 7px;border:none;border-radius:5px;background:transparent;color:var(--text);font-size:11px;text-align:left}.console-service-option:hover:not(:disabled){background:var(--surface-hover)}.console-service-option:disabled{cursor:default}.console-service-check{width:12px;height:12px;flex:0 0 auto;border:1px solid var(--border);border-radius:3px}.console-service-check.checked{border-color:var(--accent);background:var(--accent);box-shadow:inset 0 0 0 2px var(--surface)}.console-service-name{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.console-service-pin{color:var(--success);font-size:10px;text-transform:uppercase}.console-row{display:block;padding:3px 10px;border-bottom:1px solid color-mix(in srgb,var(--border-subtle) 55%,transparent)}.console-process{display:inline;margin-right:7px;color:var(--console-service-color);font-weight:700}.console-message{display:inline;overflow-wrap:anywhere;color:var(--text)}.hierarchy-resize-x{position:absolute;top:0;right:-3px;bottom:0;z-index:4;width:6px;cursor:col-resize}.hierarchy-resize-x:hover{background:color-mix(in srgb,var(--accent) 35%,transparent)}.hierarchy-resize-y{position:absolute;top:-3px;right:0;left:0;z-index:4;height:6px;cursor:row-resize}.hierarchy-resize-y:hover{background:color-mix(in srgb,var(--accent) 35%,transparent)}.is-resizing{cursor:grabbing;-webkit-user-select:none;user-select:none}.canvas{position:relative;overflow:hidden;display:flex;flex:1;align-items:center;justify-content:center;-webkit-user-select:none;user-select:none;min-height:0;min-width:0;width:100%;height:100%;overscroll-behavior:none;background-color:var(--canvas-bg);background-image:radial-gradient(var(--canvas-dot) 1px,transparent 1px);background-size:16px 16px}.canvas.pan-enabled{cursor:grab}.canvas.panning{cursor:grabbing}.device-anchor{transform-origin:center center;will-change:transform}.device-anchor-loading{opacity:0;pointer-events:none}.device-frame{position:relative}.device-presentation{position:relative;transform-origin:top left;will-change:transform}.device-anchor.animated{transition:transform .18s ease}.device-bezel{position:relative;background:var(--bezel);border-radius:48px;padding:14px;box-shadow:inset 0 0 0 1px var(--bezel-highlight)}.device-shell{position:relative}.device-chrome{position:absolute;inset:0;width:100%;height:100%;object-fit:fill;pointer-events:none;-webkit-user-drag:none;z-index:2}.pan-enabled .device-bezel,.pan-enabled .device-shell{cursor:grab}.panning .device-bezel,.panning .device-shell{cursor:grabbing}.device-screen{width:320px;background:var(--screen-bg);border-radius:36px;overflow:hidden;cursor:crosshair;touch-action:none;position:relative;z-index:1}.device-screen.chrome-screen{position:absolute;width:auto;border-radius:0}.stream-canvas{position:absolute;inset:0;display:block;width:100%;height:100%;background:#000;pointer-events:none}.accessibility-picker-layer{position:absolute;inset:0;z-index:3;cursor:crosshair;touch-action:none}.accessibility-overlay{position:absolute;inset:0;z-index:2;pointer-events:none}.touch-interaction-overlay{position:absolute;inset:0;z-index:4;pointer-events:none}.touch-indicator{position:absolute;width:30px;height:30px;margin:-15px 0 0 -15px;border:2px solid rgba(245,245,245,.92);border-radius:999px;background:#ffffff2e;box-shadow:0 0 0 1px #00000070,0 0 14px #ffffff42;transform:translateZ(0) scale(.86);opacity:.9;transition:opacity .18s ease,transform .18s ease}.touch-indicator-began,.touch-indicator-moved{transform:translateZ(0) scale(1)}.touch-indicator-ended,.touch-indicator-cancelled{opacity:0;transform:translateZ(0) scale(1.55)}.accessibility-rect{position:absolute;min-width:8px;min-height:8px;border:1px solid var(--success);background:color-mix(in srgb,var(--success) 16%,transparent);box-shadow:0 0 0 1px #00000047}.accessibility-rect.hovered{border-style:dashed;background:color-mix(in srgb,var(--accent) 12%,transparent)}.accessibility-rect.selected{border-color:var(--accent);background:color-mix(in srgb,var(--accent) 18%,transparent)}.accessibility-rect span{position:absolute;left:-1px;bottom:calc(100% + 3px);max-width:180px;overflow:hidden;padding:2px 5px;border-radius:4px;background:var(--accent);color:var(--accent-text);font-size:10px;line-height:1.2;text-overflow:ellipsis;white-space:nowrap}.screen-overlay{position:absolute;inset:0;display:grid;place-items:center;color:#555;font-size:13px;padding:24px;text-align:center}.canvas-empty{color:var(--text-muted);font-size:13px;text-align:center}.canvas-loading{position:absolute;inset:0;z-index:12;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:10px;color:var(--text-secondary);font-size:13px;text-align:center;pointer-events:none}.canvas-loading p{margin:0}.loading-spinner{width:28px;height:28px;border:2px solid color-mix(in srgb,var(--text-muted) 25%,transparent);border-top-color:var(--accent);border-radius:50%;animation:spin .78s linear infinite}.debug-overlay{position:absolute;top:12px;right:12px;z-index:14;width:min(360px,calc(100% - 24px));pointer-events:auto}.debug-overlay .debug-panel-inline{box-shadow:0 12px 32px #00000047;-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px)}@media(max-width:600px){.viewport-zoom-dock{right:8px;bottom:8px;left:auto;width:max-content;max-width:calc(100vw - 16px)}.zoom-controls-floating{width:max-content;max-width:100%;justify-content:flex-end;overflow-x:auto;padding:5px;border-radius:8px}.zoom-controls-floating .tbtn{flex:0 0 auto}.debug-overlay{top:8px;right:8px;left:8px;width:auto;max-height:calc(100% - 72px);overflow:auto}.device-bezel{border-radius:34px;padding:10px}.device-screen{width:min(320px,calc(100vw - 44px))}.accessibility-rect span{max-width:120px}}@keyframes spin{to{transform:rotate(360deg)}}.error-msg{color:var(--error);font-size:11px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;flex-shrink:0;max-width:220px}.muted{color:var(--text-muted)}
@@ -1,22 +0,0 @@
1
- (function(){"use strict";function Q(e,r){if(!e.length)return r;const t=new Uint8Array(e.length+r.length);return t.set(e),t.set(r,e.length),t}function C(e,r){return(e[r]<<24|e[r+1]<<16|e[r+2]<<8|e[r+3])>>>0}function q(e,r){const t=new DataView(e.buffer,e.byteOffset,e.byteLength);return Number(t.getBigUint64(r,!1))}function me(e){const r=atob(e),t=new Uint8Array(r.length);for(let n=0;n<r.length;n+=1)t[n]=r.charCodeAt(n);return t}function Ee(e){const r=e.trim();if(r.length%2!==0)throw new Error("Invalid hex string.");const t=new Uint8Array(r.length/2);for(let n=0;n<r.length;n+=2)t[n/2]=Number.parseInt(r.slice(n,n+2),16);return t}function j(e){if(e)return typeof e=="string"?me(e):e}function Te(e){return e?typeof e=="string"?e:Array.from(e).join(","):""}function ge(e){const r=[];let t=0;for(;e.length-t>=36;){const n=t,o=e[n];if(o!==1)throw new Error(`Unsupported binary video packet version ${o}.`);const d=e[n+1]??0,l=q(e,n+4),u=q(e,n+12),R=C(e,n+20),c=C(e,n+24),i=C(e,n+28),h=C(e,n+32),T=36+i+h;if(e.length-n<T)break;t+=36;const U=(d&2)!==0&&i>0?e.subarray(t,t+i):void 0;t+=i;const X=e.subarray(t,t+h);t+=h,r.push({metadata:{description:U,frameSequence:l,height:c,isKeyFrame:(d&1)!==0,timestampUs:u,width:R},payload:X})}return{packets:r,remainder:e.subarray(t)}}function J(){return{averageRenderMs:0,codec:"",decodeQueueSize:0,decodedFrames:0,droppedFrames:0,frameSequence:0,height:0,latestFrameGapMs:0,latestRenderMs:0,maxRenderMs:0,receivedPackets:0,reconnects:0,renderedFrames:0,waitingForKeyFrame:!1,width:0}}const we=`#version 300 es
2
- layout(location = 0) in vec2 a_position;
3
- layout(location = 1) in vec2 a_texCoord;
4
-
5
- out vec2 v_texCoord;
6
-
7
- void main() {
8
- v_texCoord = a_texCoord;
9
- gl_Position = vec4(a_position, 0.0, 1.0);
10
- }
11
- `,ye=`#version 300 es
12
- precision mediump float;
13
-
14
- in vec2 v_texCoord;
15
- uniform sampler2D u_texture;
16
-
17
- out vec4 outColor;
18
-
19
- void main() {
20
- outColor = texture(u_texture, v_texCoord);
21
- }
22
- `;function Z(e,r,t){const n=e.createShader(r);if(!n)throw new Error("Unable to allocate a WebGL shader.");if(e.shaderSource(n,t),e.compileShader(n),e.getShaderParameter(n,e.COMPILE_STATUS))return n;const o=e.getShaderInfoLog(n)??"Unknown shader compile failure.";throw e.deleteShader(n),new Error(o)}function Re(e){const r=Z(e,e.VERTEX_SHADER,we),t=Z(e,e.FRAGMENT_SHADER,ye),n=e.createProgram();if(!n)throw e.deleteShader(r),e.deleteShader(t),new Error("Unable to allocate a WebGL program.");if(e.attachShader(n,r),e.attachShader(n,t),e.linkProgram(n),e.deleteShader(r),e.deleteShader(t),e.getProgramParameter(n,e.LINK_STATUS))return n;const o=e.getProgramInfoLog(n)??"Unknown program link failure.";throw e.deleteProgram(n),new Error(o)}function Ae(e){return e.getContext("webgl2",{alpha:!1,antialias:!1,depth:!1,desynchronized:!0,powerPreference:"high-performance",premultipliedAlpha:!1,preserveDrawingBuffer:!1,stencil:!1})}class _e{canvas;gl;program;texture;vertexArray;textureHeight=0;textureWidth=0;constructor(r){this.canvas=r;const t=Ae(r);if(!t)throw new Error("This browser does not support WebGL2.");this.gl=t,this.program=Re(t);const n=t.createVertexArray();if(!n)throw new Error("Unable to allocate a WebGL vertex array.");this.vertexArray=n;const o=t.createBuffer();if(!o)throw new Error("Unable to allocate a WebGL vertex buffer.");const d=t.createTexture();if(!d)throw new Error("Unable to allocate a WebGL texture.");this.texture=d,t.bindVertexArray(n),t.bindBuffer(t.ARRAY_BUFFER,o),t.bufferData(t.ARRAY_BUFFER,new Float32Array([-1,-1,0,1,1,-1,1,1,-1,1,0,0,-1,1,0,0,1,-1,1,1,1,1,1,0]),t.STATIC_DRAW),t.enableVertexAttribArray(0),t.vertexAttribPointer(0,2,t.FLOAT,!1,16,0),t.enableVertexAttribArray(1),t.vertexAttribPointer(1,2,t.FLOAT,!1,16,8),t.useProgram(this.program);const l=t.getUniformLocation(this.program,"u_texture");l&&t.uniform1i(l,0),t.activeTexture(t.TEXTURE0),t.bindTexture(t.TEXTURE_2D,this.texture),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MIN_FILTER,t.LINEAR),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MAG_FILTER,t.LINEAR),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_WRAP_S,t.CLAMP_TO_EDGE),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_WRAP_T,t.CLAMP_TO_EDGE),t.pixelStorei(t.UNPACK_ALIGNMENT,1),t.pixelStorei(t.UNPACK_FLIP_Y_WEBGL,0),t.disable(t.BLEND),t.disable(t.DITHER),t.disable(t.DEPTH_TEST),t.disable(t.STENCIL_TEST),t.clearColor(0,0,0,1),this.syncViewport(Math.max(1,r.width),Math.max(1,r.height))}clear(){this.gl.clear(this.gl.COLOR_BUFFER_BIT)}drawFrame(r){this.syncViewport(r.displayWidth,r.displayHeight),this.gl.activeTexture(this.gl.TEXTURE0),this.gl.bindTexture(this.gl.TEXTURE_2D,this.texture),this.uploadFrame(r),this.gl.useProgram(this.program),this.gl.bindVertexArray(this.vertexArray),this.gl.drawArrays(this.gl.TRIANGLES,0,6)}uploadFrame(r){if(this.textureWidth!==r.displayWidth||this.textureHeight!==r.displayHeight){this.textureWidth=r.displayWidth,this.textureHeight=r.displayHeight,this.gl.texImage2D(this.gl.TEXTURE_2D,0,this.gl.RGBA,this.gl.RGBA,this.gl.UNSIGNED_BYTE,r);return}this.gl.texSubImage2D(this.gl.TEXTURE_2D,0,0,0,this.gl.RGBA,this.gl.UNSIGNED_BYTE,r)}syncViewport(r,t){const n=Math.max(1,Math.round(r)),o=Math.max(1,Math.round(t));this.canvas.width!==n&&(this.canvas.width=n),this.canvas.height!==o&&(this.canvas.height=o),this.gl.viewport(0,0,n,o)}}const g=self,ee=120,be=1e3,ve=8,Se=200,Ue=g.crypto?.randomUUID?.()??`worker-${Math.random().toString(36).slice(2)}`;let N=null,A=null,f=null,F=null,w=null,s=0,m=null,D="",b=!1,x=0,$="",H="",B=0,L=0,v=0,V=0,k=0,G=0,te="idle",O=0,a=J(),E=0,S=0,I=!1;function P(e){return e instanceof Error?e.name&&e.name!=="Error"?`${e.name}: ${e.message}`:e.message:String(e)}function Fe(e){return e.includes("WebTransport")||e.includes("Mach port invalid")||e.includes("device disconnected")||e.includes("opening handshake failed")||e.includes("stream closed")||e.includes("session closed")||e.includes("closed before sending")}function xe(e){return e.startsWith("hvc1.")||e.startsWith("hev1.")}function Ce(e){return e.startsWith("hvc1.")?`hev1.${e.slice(5)}`:e.startsWith("hev1.")?`hvc1.${e.slice(5)}`:null}function De(e){const r=[{config:e,label:`${e.codec} prefer-hardware`}],t=Ce(e.codec);return t&&r.push({config:{...e,codec:t},label:`${t} prefer-hardware`}),r.push({config:{codedHeight:e.codedHeight,codedWidth:e.codedWidth,codec:e.codec,description:e.description,optimizeForLatency:e.optimizeForLatency},label:`${e.codec} default-acceleration`}),t&&r.push({config:{codedHeight:e.codedHeight,codedWidth:e.codedWidth,codec:t,description:e.description,optimizeForLatency:e.optimizeForLatency},label:`${t} default-acceleration`}),r}function K(e){g.postMessage(e)}function p(e){te=e.state;const r=`${e.state}|${e.detail??""}|${e.error??""}`;r!==$&&($=r,K({type:"status",status:e}))}function Le(){return new URL("/api/client-stream-stats",g.location.href).toString()}function re(){L=0,v=0,V=0,k=0,G=0,O=0,I=!1}function _(){a.decodeQueueSize=f?.decodeQueueSize??0,a.waitingForKeyFrame=b}function ne(e=!1){const r=m;if(!r||I)return;const t=performance.now();if(!e&&v&&t-v<be)return;const n=v?t-v:0,o=a.receivedPackets-G,d=a.decodedFrames-V,l=a.droppedFrames-k,u=n>0?1e3/n:0;v=t,G=a.receivedPackets,V=a.decodedFrames,k=a.droppedFrames,_(),I=!0,fetch(Le(),{body:JSON.stringify({...a,clientId:Ue,connectionId:s,decodedFps:d*u,droppedFps:l*u,kind:"worker",packetFps:o*u,status:te,timestampMs:Date.now(),udid:r.udid,url:g.location.href,userAgent:g.navigator.userAgent}),cache:"no-store",headers:{"content-type":"application/json"},method:"POST"}).catch(()=>{}).finally(()=>{I=!1})}function oe(){E=0,x=performance.now(),_(),K({type:"stats",stats:{...a}}),ne()}function ae(){S&&(clearTimeout(S),S=0)}function y(e=!1){const r=performance.now();if(e||x===0||r-x>=ee){E&&(clearTimeout(E),E=0),oe();return}if(E)return;const t=Math.max(0,ee-(r-x));E=setTimeout(()=>{oe()},t)}function ie(e,r){const t=`${e}x${r}`;t!==H&&(H=t,K({type:"video-config",size:{width:e,height:r}}))}function se(){!N||A||(A=new _e(N))}function M(){A&&A.clear()}function W(){if(f)try{f.close()}catch{}f=null,D="",b=!1}function ce(){if(w){try{w.close({closeCode:0,reason:"disconnect"})}catch{}w=null}}function de(){$="",H="",x=0,B=0,ae(),E&&(clearTimeout(E),E=0)}function Ie(){ne(!0),s+=1,F?.abort(),F=null,m=null,ce(),W(),de(),re(),M(),p({state:"idle"})}function Y(e,r=s){const t=m;!t||S||r!==s||(p({detail:e,state:"connecting"}),S=setTimeout(()=>{S=0,!(!t||r!==s||t!==m)&&ue(t,!0)},150))}async function Pe(e,r){if(r!==s)return!1;if(typeof VideoDecoder!="function")return p({error:"This browser does not support WebCodecs.",state:"error"}),!1;const t=e.codec??a.codec,n=e.description,o=`${t}:${Te(n)}:${e.width}x${e.height}`;if(f&&D===o||f&&!n&&D.startsWith(`${t}:`)&&a.width===e.width&&a.height===e.height)return!0;if(!t||!n)return!1;W();const d=r;f=new VideoDecoder({output(c){if(d!==s){c.close();return}try{se()}catch(i){c.close(),p({error:i instanceof Error?i.message:"Unable to initialize the GPU renderer.",state:"error"});return}if(!A){c.close();return}try{const i=performance.now();A.drawFrame(c);const h=performance.now(),T=h-i;a.renderedFrames+=1,O+=T,a.latestRenderMs=T,a.maxRenderMs=Math.max(a.maxRenderMs,T),a.averageRenderMs=O/a.renderedFrames,L>0&&(a.latestFrameGapMs=h-L),L=h}catch(i){c.close(),p({error:i instanceof Error?i.message:"Unable to render the decoded frame.",state:"error"});return}c.close(),a.decodedFrames+=1,_(),y(),p({state:"streaming"})},error(c){d===s&&(p({error:c.message,state:"error"}),Y("Reconnecting live stream…",d))}});const l={codedHeight:e.height,codedWidth:e.width,codec:t,description:j(n),hardwareAcceleration:"prefer-hardware",optimizeForLatency:!0};let u=null;const R=[];for(const c of De(l))try{const i=await VideoDecoder.isConfigSupported(c.config);if(R.push(`${c.label}: ${i.supported?"yes":"no"}`),i.supported){u=i.config??c.config;break}}catch(i){R.push(`${c.label}: ${P(i)}`)}if(r!==s||!f)return!1;if(!u){const c=j(n),i=xe(t)?" HEVC decode is disabled or unavailable in this Chrome profile; check Chrome hardware acceleration and chrome://gpu video decode status.":"";return p({error:`Unsupported decoder configuration for ${t} at ${e.width}x${e.height} (${c?.byteLength??0} config bytes). Tried ${R.join("; ")}.${i}`,state:"error"}),W(),!1}return f.configure(u),D=o,a.codec=u.codec,!0}async function Me(e,r){if(r!==s)return;if(a.receivedPackets+=1,a.frameSequence=e.metadata.frameSequence,a.width=e.metadata.width,a.height=e.metadata.height,ie(e.metadata.width,e.metadata.height),e.metadata.isKeyFrame)b=!1;else if(b){a.droppedFrames+=1,_(),y();return}if(!await Pe(e.metadata,r)||!f){y();return}if(r===s){if(f.decodeQueueSize>ve&&!e.metadata.isKeyFrame){a.droppedFrames+=1,b=!0,_(),y(),m&&le(m.udid);return}try{f.decode(new EncodedVideoChunk({data:e.payload,timestamp:Number(e.metadata.timestampUs??0),type:e.metadata.isKeyFrame?"key":"delta"}))}catch{if(r!==s)return;a.droppedFrames+=1,b=!0,_(),y(),m&&le(m.udid);return}_(),y()}}function We(){return new URL("/api/health",g.location.href).toString()}function Ne(e){return new URL(`/api/simulators/${encodeURIComponent(e)}/refresh`,g.location.href).toString()}function $e(e,r){return e.replace("{udid}",encodeURIComponent(r))}async function He(e,r,t){if(!r||t||e!==s)return t;const n=await Promise.race([r.closed.then(()=>"WebTransport session closed.").catch(o=>P(o)),new Promise(o=>{setTimeout(()=>o(""),75)})]);return e===s?n:t}async function Be(e){const r=await fetch(We(),{cache:"no-store",signal:e});if(!r.ok)throw new Error(`Health check failed with status ${r.status}`);return await r.json()}async function Ve(e){const r=e.getReader();let t=new Uint8Array(0);for(;;){const{done:n,value:o}=await r.read();if(n)return new Uint8Array(t);o?.length&&(t=Q(t,o))}}async function ke(e){const r=await Ve(e);return JSON.parse(new TextDecoder().decode(r))}async function le(e){const r=performance.now();if(!(r-B<Se)){B=r;try{await fetch(Ne(e),{cache:"no-store",method:"POST"})}catch{}}}async function ue(e,r=!1){s+=1;const t=s;let n="";m=e,F?.abort(),F=new AbortController,ae(),ce(),W(),de(),M(),a=J(),re(),r&&(a.reconnects+=1),y(!0),p({detail:"Opening live stream…",state:"connecting"});try{if(typeof WebTransport!="function")throw new Error("This browser does not support WebTransport.");const o=await Be(F.signal),d=o.webTransport?.urlTemplate,l=o.webTransport?.certificateHash?.algorithm,u=o.webTransport?.certificateHash?.value,R=o.webTransport?.packetVersion;if(!d||!l||!u||R==null)throw new Error("Server did not provide WebTransport connection details.");if(l!=="sha-256")throw new Error(`Unsupported WebTransport certificate hash algorithm ${l}.`);w=new WebTransport($e(d,e.udid),{congestionControl:"low-latency",serverCertificateHashes:[{algorithm:l,value:new Uint8Array(Ee(u))}]}),w.closed.then(()=>{t===s&&(n||="WebTransport session closed.")}).catch(z=>{t===s&&(n=P(z))}),await w.ready;const c=w.incomingUnidirectionalStreams.getReader(),i=await c.read();if(i.done||!i.value)throw new Error("WebTransport closed before sending control stream.");const h=await ke(i.value);if(h.version!==R)throw new Error(`Unsupported WebTransport packet version ${h.version}.`);if(h.packet_format!=="binary-video-v1")throw new Error(`Unsupported WebTransport packet format ${h.packet_format}.`);a.codec=h.codec??"",ie(h.width,h.height),y(!0);const T=await c.read();if(T.done||!T.value)throw new Error("WebTransport closed before sending video stream.");let U=new Uint8Array(0);const X=T.value.getReader();for(;t===s;){const{done:z,value:he}=await X.read();if(z)break;if(!he?.length)continue;U=Q(U,he);const fe=ge(U);U=fe.remainder;for(const pe of fe.packets){if(t!==s)return;pe.metadata.codec??=h.codec,await Me(pe,t)}}t===s&&Y("Live stream ended. Reconnecting…",t)}catch(o){if(o.name==="AbortError")return;const d=P(o),l=await He(t,w,n),u=l&&l!==d?`${d} (${l})`:d;t===s&&(Fe(u)?p({detail:"Reconnecting live stream…",state:"connecting"}):p({error:u,state:"error"}),Y("Reconnecting live stream…",t))}}g.onmessage=e=>{const r=e.data;switch(r.type){case"attach-canvas":N=r.canvas,A=null;try{se(),M()}catch(t){p({error:t instanceof Error?t.message:"Unable to initialize the GPU renderer.",state:"error"})}break;case"connect":ue(r.target);break;case"disconnect":Ie();break;case"clear":M();break}}})();