domma-cms 0.107.0 → 0.108.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/admin/dist/domma/domma-tools.css +3 -3
- package/admin/dist/domma/domma-tools.min.js +3 -3
- package/admin/js/lib/effect-defs.js +1 -1
- package/admin/js/lib/effects-builder.js +2 -2
- package/admin/js/lib/shortcode-context-menu.js +2 -2
- package/admin/js/templates/docs/usage-tools.html +7 -0
- package/package.json +2 -2
- package/plugins/_lib/mail/render.js +14 -1
- package/plugins/_lib/public/call/blur-clock.js +7 -0
- package/plugins/_lib/public/call/blur.js +225 -0
- package/plugins/_lib/public/call/call.css +1 -1
- package/plugins/_lib/public/call/call.js +103 -9
- package/plugins/_lib/public/call/vendor/mediapipe/selfie_segmenter_landscape.tflite +0 -0
- package/plugins/_lib/public/call/vendor/mediapipe/vision_bundle.mjs +2 -0
- package/plugins/_lib/public/call/vendor/mediapipe/vision_wasm_internal.js +8841 -0
- package/plugins/_lib/public/call/vendor/mediapipe/vision_wasm_internal.wasm +0 -0
- package/public/js/celebrations/index.js +1 -1
- package/public/js/celebrations/themes/butterfly-drawing.js +1 -0
- package/public/js/celebrations/themes/halloween-drawing.js +1 -0
- package/public/js/celebrations/themes/halloween.js +1 -1
- package/public/js/celebrations/themes/valentines.js +1 -1
- package/public/js/celebrations/trait-data.mjs +1 -0
- package/public/js/effects.js +1 -1
- package/scripts/build.js +6 -0
- package/server/server.js +3 -1
- package/server/templates/page.html +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
.dmc{--dmc-accent: #4f7cff;--dmc-accent-solid: color-mix(in srgb, var(--dmc-accent) 84%, #000);--dmc-accent-soft: color-mix(in srgb, var(--dmc-accent) 18%, transparent);--dmc-accent-text: color-mix(in srgb, var(--dmc-accent) 55%, #fff);--dmc-gap: 8px;--dmc-base: #1b1d21;--dmc-panel: #25282d;--dmc-raised: #2f3338;--dmc-line: #363a40;--dmc-on-dark: #e8eaed;--dmc-muted-dark: #9aa0a8;--dmc-red: #d9453f;--dmc-red-hover: #c23a35;--dmc-page: #f3f4f6;--dmc-card: #fff;--dmc-ink: #1f2328;--dmc-ink-muted: #5b6470;--dmc-border: #dde0e4;--dmc-offset: 0px;--dmc-left: 0px;--dmc-right: 0px;display:flex;flex-direction:column;gap:1rem;color:var(--dmc-ink)}.dmc [hidden]{display:none!important}html.dmc-page,html.dmc-page body{overflow:hidden!important}.dmc.dmc--app{position:fixed;z-index:40;top:var(--dmc-offset);left:var(--dmc-left);right:var(--dmc-right);bottom:0;margin:0;background:var(--dmc-page);overflow-y:auto;overscroll-behavior:contain;padding:2.5rem 1rem;align-items:center;justify-content:safe center;gap:1rem}.dmc.dmc--app[data-stage=in]{padding:0;background:var(--dmc-base);overflow:hidden}.dmc.dmc--app>*{width:min(60rem,100%);flex:none}.dmc.dmc--app>.dmc-call{width:100%;flex:1 1 auto}.dmc-head{display:flex;align-items:center;gap:.75rem;flex-wrap:wrap}.dmc.dmc--app:not([data-stage=check])>.dmc-head{width:min(28rem,100%);justify-content:center;text-align:center}.dmc-title{font-size:1.35rem;font-weight:600;margin:0;flex:1 1 auto;letter-spacing:-.01em;color:var(--dmc-ink)}.dmc-btn{font:inherit;font-size:.9rem;cursor:pointer;padding:.5rem .95rem;min-height:2.5rem;border-radius:8px;border:1px solid var(--dmc-border);background:var(--dmc-card);color:inherit;display:inline-flex;align-items:center;justify-content:center;gap:.45rem;position:relative;font-weight:500;transition:background-color .15s,border-color .15s,color .15s}.dmc-btn:hover{border-color:#b9bec5;background:#f7f8f9}.dmc-btn:disabled{opacity:.55;cursor:default}.dmc-btn:focus-visible,.dmc-compose:focus-visible,.dmc-input:focus-visible{outline:2px solid var(--dmc-accent);outline-offset:2px}.dmc-i{display:inline-flex}.dmc-btn svg,.dmc-count svg,.dmc-note svg,.dmc-chip svg{width:18px;height:18px;flex:none}.dmc-btn.is-off{background:#fdecec;border-color:#f3c4c2;color:#b3261e}.dmc-btn--go{background:var(--dmc-accent-solid);border-color:transparent;color:#fff;font-weight:600}.dmc-btn--go:hover{background:color-mix(in srgb,var(--dmc-accent) 74%,#000);border-color:transparent}.dmc-btn--small{padding:.3rem .65rem;min-height:2rem;font-size:.82rem}.dmc-panel{display:grid;grid-template-columns:minmax(0,3fr) minmax(0,2fr);gap:1.75rem;align-items:center;background:var(--dmc-card);border:1px solid var(--dmc-border);border-radius:12px;padding:1.5rem;box-shadow:0 1px 2px #1018280d,0 8px 24px #1018280f}.dmc-panel--msg{display:flex;flex-direction:column;align-items:center;text-align:center;padding:2.5rem 2rem;gap:.25rem;width:min(28rem,100%)!important;align-self:center}.dmc-panel--msg h2{margin:.75rem 0 .25rem;font-size:1.2rem;font-weight:600;color:var(--dmc-ink)}.dmc-panel--msg p{color:var(--dmc-ink-muted);margin:0 0 1.25rem;font-size:.92rem;line-height:1.5}.dmc-pulse{width:64px;height:64px;border-radius:50%;display:grid;place-items:center;color:var(--dmc-accent-solid);background:var(--dmc-accent-soft);box-shadow:0 0 color-mix(in srgb,var(--dmc-accent) 35%,transparent);animation:dmc-pulse 2.2s infinite}.dmc-pulse svg{width:26px;height:26px}@keyframes dmc-pulse{70%{box-shadow:0 0 0 16px transparent}to{box-shadow:0 0 0 0 transparent}}.dmc-preview{position:relative;aspect-ratio:16 / 9;background:var(--dmc-base);border-radius:10px;overflow:hidden}.dmc-preview video,.dmc-tile video{width:100%;height:100%;object-fit:cover;display:block}.dmc-self,.dmc-self-preview{transform:scaleX(-1)}.dmc-nocam{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.6rem;color:var(--dmc-muted-dark);margin:0;font-size:.9rem}.dmc-avatar-dot{width:76px;height:76px;border-radius:50%;display:grid;place-items:center;background:var(--dmc-raised);color:var(--dmc-on-dark);font-size:1.8rem;font-weight:600}.dmc-preview-tools{position:absolute;left:0;right:0;bottom:.75rem;display:flex;justify-content:center;gap:.5rem}.dmc-round{width:44px;height:44px;min-height:0;padding:0;border-radius:10px;border:1px solid rgba(255,255,255,.14);background:#1b1d21b8;color:#fff;backdrop-filter:blur(6px)}.dmc-round:hover{background:#2f3338e6;border-color:#ffffff4d}.dmc-round.is-off{background:var(--dmc-red);border-color:transparent;color:#fff}.dmc-check h2{margin:0 0 .35rem;font-size:1.25rem;font-weight:600;color:var(--dmc-ink)}.dmc-label{display:grid;gap:.35rem;margin:1rem 0 .25rem;font-weight:500;font-size:.88rem;color:var(--dmc-ink)}.dmc-input{font:inherit;font-size:.95rem;padding:.55rem .75rem;min-height:2.5rem;border:1px solid var(--dmc-border);border-radius:8px;background:#fff;color:var(--dmc-ink);min-width:0}.dmc-label .dmc-input:focus{outline:0;border-color:var(--dmc-accent);box-shadow:0 0 0 3px var(--dmc-accent-soft)}.dmc-row{display:flex;gap:.5rem;flex-wrap:wrap;margin:.9rem 0}.dmc-chipbtn{font-size:.86rem}.dmc-note{color:var(--dmc-ink-muted);font-size:.9rem;line-height:1.45;margin:0}.dmc-note--rec{display:flex;gap:.5rem;align-items:flex-start;font-size:.85rem;margin:.25rem 0 0}.dmc-note--rec svg{color:var(--dmc-red);fill:currentColor;width:10px;height:10px;margin-top:.35rem}.dmc-error{color:#b3261e;font-weight:500;font-size:.9rem;margin:.5rem 0 0}.dmc-check .dmc-btn--go{width:100%;min-height:2.75rem;margin-top:1rem;font-size:.95rem}.dmc-call{display:flex;color:var(--dmc-on-dark);background:var(--dmc-base);overflow:hidden;color-scheme:dark;min-height:0;font-size:14px}.dmc-stage{position:relative;flex:1 1 auto;min-width:0;display:flex;flex-direction:column}.dmc-top{order:-1;flex:none;display:flex;align-items:center;justify-content:space-between;gap:1rem;height:44px;padding:0 1rem;background:var(--dmc-panel);border-bottom:1px solid var(--dmc-line)}.dmc-top-l{display:flex;align-items:center;gap:.75rem;min-width:0}.dmc-top .dmc-top-title{margin:0;font-size:.92rem;line-height:1.3;font-weight:600;letter-spacing:0;color:var(--dmc-on-dark);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.dmc-timer{flex:none;font-size:.82rem;color:var(--dmc-muted-dark);font-variant-numeric:tabular-nums;padding-left:.75rem;border-left:1px solid var(--dmc-line)}.dmc-top-r{display:flex;align-items:center;gap:.6rem;flex:none}.dmc-rec{display:inline-flex;align-items:center;gap:.4rem;color:#ff8a85;background:#d9453f29;border:1px solid rgba(217,69,63,.45);border-radius:6px;padding:.12rem .5rem;font-size:.74rem;font-weight:600;letter-spacing:.02em}.dmc-rec-dot{width:7px;height:7px;border-radius:50%;background:#ff5a52;animation:dmc-blink 1.4s infinite}@keyframes dmc-blink{50%{opacity:.3}}.dmc-count{display:inline-flex;align-items:center;gap:.35rem;font-size:.82rem;color:var(--dmc-muted-dark);font-variant-numeric:tabular-nums}.dmc-count svg{width:16px;height:16px}.dmc-main{position:relative;flex:1 1 auto;min-height:0}.dmc-remote{position:absolute;inset:var(--dmc-gap);display:flex;flex-wrap:wrap;align-content:center;justify-content:center;gap:var(--dmc-gap);--cols: 1;--rows: 1}.dmc-cell{min-width:0;min-height:0;display:flex;width:var(--tile-w, calc((100% - (var(--cols) - 1) * var(--dmc-gap)) / var(--cols)));height:var(--tile-h, calc((100% - (var(--rows) - 1) * var(--dmc-gap)) / var(--rows)))}.dmc-main[data-tiles="2"] .dmc-remote{--cols: 2}.dmc-main[data-tiles="3"] .dmc-remote,.dmc-main[data-tiles="4"] .dmc-remote{--cols: 2;--rows: 2}.dmc-main[data-tiles="5"] .dmc-remote,.dmc-main[data-tiles="6"] .dmc-remote{--cols: 3;--rows: 2}.dmc-tile{position:relative;margin:0;background:var(--dmc-panel);border-radius:8px;overflow:hidden}.dmc-cell .dmc-tile{flex:1 1 auto;width:100%;height:100%}.dmc-tile video{position:relative;z-index:1}.dmc-avatar{position:absolute;inset:0;display:grid;place-items:center;z-index:0}.dmc-avatar span{width:clamp(3rem,18%,6rem);aspect-ratio:1;border-radius:50%;display:grid;place-items:center;background:var(--dmc-raised);color:var(--dmc-on-dark);font-size:clamp(1.1rem,2.4vw,2.2rem);font-weight:600}.dmc-tile--me .dmc-avatar{z-index:2;background:var(--dmc-panel)}.dmc-chip{position:absolute;left:.5rem;bottom:.5rem;z-index:3;display:inline-flex;align-items:center;gap:.3rem;max-width:calc(100% - 1rem);padding:.18rem .5rem;border-radius:6px;background:#1b1d21c7;color:var(--dmc-on-dark);font-size:.78rem;font-weight:500;line-height:1.35}.dmc-chip>span:last-child{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.dmc-chip-mute{display:inline-flex;color:#ff8a85}.dmc-chip-mute svg{width:13px;height:13px}.dmc-tile--me{position:absolute;z-index:2;right:calc(var(--dmc-gap) * 2);bottom:calc(var(--dmc-gap) * 2);width:clamp(10rem,17%,15rem);aspect-ratio:16 / 9;box-shadow:0 6px 20px #00000073;outline:1px solid rgba(255,255,255,.14)}.dmc-tile--me .dmc-chip{left:.35rem;bottom:.35rem;font-size:.7rem;padding:.1rem .4rem}.dmc-main[data-tiles="0"] .dmc-tile--me{inset:0;width:auto;aspect-ratio:auto;border-radius:0;outline:0;box-shadow:none}.dmc-main[data-tiles="0"] .dmc-tile--me .dmc-chip{left:.75rem;bottom:.75rem;font-size:.78rem;padding:.18rem .5rem}.dmc-main[data-tiles="0"] .dmc-tile--me:after{content:"";position:absolute;inset:0;z-index:2;background:#1b1d218c;pointer-events:none}.dmc-alone{position:absolute;inset:0;z-index:2;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;padding:1rem;pointer-events:none}.dmc-alone-title{margin:0;font-size:1.1rem;font-weight:600;color:#fff}.dmc-alone-sub{margin:.35rem 0 0;color:#c9cdd2;font-size:.88rem}.dmc-waiting{position:absolute;z-index:4;top:calc(44px + .75rem);right:.75rem;width:min(20rem,calc(100% - 1.5rem));padding:.85rem;border-radius:10px;background:var(--dmc-raised);box-shadow:0 12px 32px #00000073;border:1px solid #40454c;animation:dmc-drop .2s ease}@keyframes dmc-drop{0%{opacity:0;transform:translateY(-4px)}}.dmc-waiting h3{display:flex;align-items:center;gap:.5rem;margin:0 0 .7rem;font-size:.85rem;font-weight:600;color:var(--dmc-on-dark)}.dmc-waiting-dot{width:8px;height:8px;border-radius:50%;background:var(--dmc-accent);box-shadow:0 0 0 3px var(--dmc-accent-soft)}.dmc-waiting ul{list-style:none;margin:0;padding:0;display:grid;gap:.7rem;max-height:14rem;overflow-y:auto}.dmc-waiting li{display:grid;grid-template-columns:auto 1fr;align-items:center;gap:.4rem .6rem}.dmc-winitial{width:2rem;height:2rem;border-radius:50%;display:grid;place-items:center;background:#454a52;color:#fff;font-weight:600;font-size:.85rem}.dmc-wname{font-weight:500;color:var(--dmc-on-dark);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.dmc-wbtns{grid-column:2;display:flex;gap:.4rem}.dmc-waiting .dmc-btn svg{width:15px;height:15px}.dmc-waiting .dmc-btn--go{background:var(--dmc-accent-solid);color:#fff}.dmc-btn--ghost{background:transparent;color:var(--dmc-on-dark);border-color:#4a4f57}.dmc-btn--ghost:hover{background:#ffffff0f;border-color:#626872}.dmc-bar{flex:none;display:grid;grid-template-columns:1fr auto 1fr;align-items:center;gap:.5rem;padding:.5rem 1rem;background:var(--dmc-panel);border-top:1px solid var(--dmc-line)}.dmc-bar-mid{display:flex;align-items:center;gap:.35rem}.dmc-bar-end{display:flex;justify-content:flex-end}.dmc-tool{flex-direction:column;gap:3px;min-width:4.25rem;height:3.5rem;min-height:0;padding:.3rem .6rem;border-radius:10px;border:1px solid transparent;background:transparent;color:var(--dmc-on-dark);flex:none;font-weight:400}.dmc-tool:hover{background:var(--dmc-raised);border-color:transparent}.dmc-tool svg{width:20px;height:20px}.dmc-lbl{font-size:.72rem;line-height:1;color:var(--dmc-muted-dark);white-space:nowrap}.dmc-tool:hover .dmc-lbl{color:var(--dmc-on-dark)}.dmc-tool.is-off{background:transparent;color:#ff8a85}.dmc-tool.is-off:hover{background:#d9453f24}.dmc-tool.is-off .dmc-lbl{color:#ff8a85}.dmc-tool.is-on{background:var(--dmc-accent-soft);color:var(--dmc-accent-text)}.dmc-tool.is-on .dmc-lbl{color:var(--dmc-accent-text)}.dmc-btn--rec svg{color:var(--dmc-red);fill:currentColor;width:16px;height:16px;margin:2px 0}.dmc-btn--rec.is-on{background:#d9453f2e;color:#ff8a85}.dmc-btn--rec.is-on .dmc-lbl{color:#ff8a85}.dmc-btn--rec.is-on svg{color:#ff5a52;animation:dmc-blink 1.4s infinite}.dmc-btn--leave{background:var(--dmc-red);color:#fff;flex-direction:row;gap:.45rem;height:2.75rem;min-width:0;padding:0 1.1rem;border-radius:10px;font-weight:600}.dmc-btn--leave:hover{background:var(--dmc-red-hover)}.dmc-btn--leave .dmc-lbl,.dmc-btn--leave:hover .dmc-lbl{color:#fff;font-size:.85rem}.dmc-btn--leave svg{width:18px;height:18px}.dmc-badge{position:absolute;top:2px;right:10px;min-width:1.1rem;height:1.1rem;padding:0 .28rem;border-radius:999px;background:var(--dmc-accent-solid);color:#fff;font-size:.66rem;font-weight:600;line-height:1.1rem;text-align:center;box-shadow:0 0 0 2px var(--dmc-panel)}.dmc-chat{flex:none;width:0;display:flex;flex-direction:column;background:var(--dmc-panel);color:var(--dmc-on-dark);overflow:hidden;transition:width .2s ease;border-left:1px solid var(--dmc-line);font-size:14px}.dmc-call:not(.is-chat) .dmc-chat{border-left-width:0}.dmc-call.is-chat .dmc-chat{width:20rem}.dmc-chat>*{min-width:20rem}.dmc-chat-head{flex:none;display:flex;align-items:center;gap:.5rem;height:44px;padding:0 .5rem 0 1rem;border-bottom:1px solid var(--dmc-line)}.dmc-chat-titles{flex:1;min-width:0;display:flex;align-items:baseline;gap:.6rem}.dmc-chat-titles h3{margin:0;font-size:.92rem;font-weight:600;color:var(--dmc-on-dark)}.dmc-chat-titles span{font-size:.75rem;color:var(--dmc-muted-dark);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.dmc-chat-back,.dmc-chat-close{width:2rem;height:2rem;border:0;border-radius:8px;background:transparent;color:var(--dmc-muted-dark);cursor:pointer;display:grid;place-items:center}.dmc-chat-back:hover,.dmc-chat-close:hover{background:var(--dmc-raised);color:var(--dmc-on-dark)}.dmc-chat-back:focus-visible,.dmc-chat-close:focus-visible{outline:2px solid var(--dmc-accent);outline-offset:1px}.dmc-chat-back svg,.dmc-chat-close svg{width:18px;height:18px}.dmc-chat-back{display:none}.dmc-chat-body{position:relative;flex:1;min-height:0;display:flex}.dmc-messages{list-style:none;margin:0;padding:.5rem 1rem 1rem;flex:1;overflow-y:auto;overscroll-behavior:contain;scrollbar-width:thin;scrollbar-color:#43484f transparent}.dmc-chat-empty{position:absolute;inset:auto 1.5rem 45%;margin:0;text-align:center;color:var(--dmc-muted-dark);font-size:.85rem;line-height:1.5}.dmc-msg{display:flex;flex-direction:column;margin-top:3px}.dmc-msg.is-first{margin-top:.85rem}.dmc-msg.is-mine{align-items:flex-end}.dmc-msg.is-other{align-items:flex-start}.dmc-msg.is-sys{align-items:center;margin-top:.6rem}.dmc-msg.is-sys+.dmc-msg.is-sys:not(.has-day){margin-top:.2rem}.dmc-msg.has-day.is-first{margin-top:.35rem}.dmc-day{align-self:stretch;display:flex;align-items:center;gap:.75rem;margin:.5rem 0;color:var(--dmc-muted-dark);font-size:.7rem;font-weight:600;text-transform:uppercase;letter-spacing:.06em}.dmc-day:before,.dmc-day:after{content:"";flex:1;height:1px;background:var(--dmc-line)}.dmc-sys{color:#80868e;font-size:.76rem;line-height:1.4;text-align:center}.dmc-meta{display:flex;align-items:baseline;gap:.5rem;margin:0 .15rem .2rem;max-width:88%}.dmc-from{font-size:.8rem;font-weight:500;color:#8fb0ff;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.dmc-time{font-size:.7rem;color:#80868e;font-variant-numeric:tabular-nums;flex:none}.dmc-bubble{max-width:88%;padding:.45rem .7rem;border-radius:8px;background:var(--dmc-raised);color:var(--dmc-on-dark);line-height:1.42;overflow-wrap:anywhere}.is-mine .dmc-bubble{background:color-mix(in srgb,var(--dmc-accent) 20%,var(--dmc-panel));color:#f1f4ff}.dmc-text{white-space:pre-wrap}.dmc-msg[data-hue="1"] .dmc-from{color:#6fc2ad}.dmc-msg[data-hue="2"] .dmc-from{color:#d6ad72}.dmc-msg[data-hue="3"] .dmc-from{color:#bf9fdc}.dmc-msg[data-hue="4"] .dmc-from{color:#e0948e}.dmc-msg[data-hue="5"] .dmc-from{color:#9fcb8d}.dmc-msg[data-hue="6"] .dmc-from{color:#7fbad6}.dmc-msg[data-hue="7"] .dmc-from{color:#d49fba}.dmc-newchip{position:absolute;left:50%;bottom:.75rem;transform:translate(-50%);display:inline-flex;align-items:center;gap:.35rem;cursor:pointer;border:1px solid #40454c;border-radius:8px;padding:.35rem .7rem;background:var(--dmc-raised);color:var(--dmc-on-dark);font:inherit;font-size:.78rem;box-shadow:0 4px 14px #0006}.dmc-newchip svg{width:14px;height:14px;color:var(--dmc-accent-text)}.dmc-send{flex:none;display:flex;align-items:flex-end;gap:.5rem;padding:.75rem 1rem;border-top:1px solid var(--dmc-line)}.dmc-compose{flex:1;resize:none;border:1px solid #40454c;border-radius:8px;padding:.5rem .7rem;min-height:0;background:var(--dmc-base);color:var(--dmc-on-dark);line-height:1.35rem;font-size:14px;overflow-y:hidden;max-height:calc(6.4rem + 2px);display:block}.dmc-compose::placeholder{color:#80868e}.dmc-compose:focus{outline:0;border-color:var(--dmc-accent);box-shadow:0 0 0 3px var(--dmc-accent-soft)}.dmc-sendbtn{width:2.4rem;height:2.4rem;min-height:0;padding:0;border:0;border-radius:8px;background:var(--dmc-accent-solid);color:#fff;flex:none}.dmc-sendbtn:hover{background:color-mix(in srgb,var(--dmc-accent) 74%,#000)}.dmc-sendbtn:disabled{opacity:1;background:var(--dmc-raised);color:#80868e}.dmc-sendbtn svg{width:17px;height:17px;margin-left:-2px}.dmc-invite{flex:none;width:0;display:flex;flex-direction:column;background:var(--dmc-panel);color:var(--dmc-on-dark);overflow:hidden;transition:width .2s ease;border-left:0 solid var(--dmc-line);font-size:14px}.dmc-invite.is-open{width:22rem;border-left-width:1px}.dmc-invite>*{min-width:22rem}.dmc .dmc-invite ul{margin:0;padding:0;list-style:none}.dmc .dmc-invite li{margin:0}.dmc-inv-body{flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain;padding:.85rem 1rem 1rem;scrollbar-width:thin;scrollbar-color:#43484f transparent}.dmc-inv-input{font:inherit;font-size:14px;width:100%;min-width:0;min-height:2.4rem;padding:.45rem .7rem;border:1px solid #40454c;border-radius:8px;background:var(--dmc-base);color:var(--dmc-on-dark)}.dmc-inv-input::placeholder{color:#80868e}.dmc-inv-input:focus{outline:0;border-color:var(--dmc-accent);box-shadow:0 0 0 3px var(--dmc-accent-soft)}.dmc-inv-search{position:relative;display:block}.dmc-inv-search .dmc-inv-input{padding-left:2.2rem}.dmc-inv-sico{position:absolute;left:.7rem;top:50%;transform:translateY(-50%);display:inline-flex;color:#80868e;pointer-events:none}.dmc-inv-sico svg{width:16px;height:16px}.dmc-inv-label{margin:1rem 0 .4rem;font-size:.72rem;font-weight:600;text-transform:uppercase;letter-spacing:.06em;color:var(--dmc-muted-dark)}.dmc-inv-chips{margin-top:.75rem}.dmc-inv-chips ul{display:flex;flex-wrap:wrap;gap:.35rem}.dmc-inv-chip{display:inline-flex;align-items:center;gap:.35rem;max-width:100%;padding:.2rem .25rem .2rem .6rem;border-radius:6px;background:var(--dmc-accent-soft);color:var(--dmc-accent-text);font-size:.8rem}.dmc-inv-chipname{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:#f1f4ff;font-weight:500}.dmc-inv-chipkind{font-size:.68rem;opacity:.85}.dmc-inv-chipx{display:grid;place-items:center;width:1.4rem;height:1.4rem;border:0;border-radius:4px;background:transparent;color:inherit;cursor:pointer;padding:0}.dmc-inv-chipx:hover{background:#ffffff1a}.dmc-inv-chipx:focus-visible{outline:2px solid var(--dmc-accent);outline-offset:1px}.dmc-inv-chipx svg{width:13px;height:13px}.dmc-inv-list{list-style:none;margin:0;padding:0;display:grid;gap:2px}.dmc-inv-row{width:100%;display:grid;grid-template-columns:auto 1fr auto auto;align-items:center;gap:.6rem;padding:.45rem .5rem;border:1px solid transparent;border-radius:8px;background:transparent;color:inherit;font:inherit;text-align:left;cursor:pointer}.dmc-inv-row:hover{background:var(--dmc-raised)}.dmc-inv-row:focus-visible{outline:2px solid var(--dmc-accent);outline-offset:-2px}.dmc-inv-row.is-picked{background:var(--dmc-accent-soft)}.dmc-inv-av{width:2rem;height:2rem;border-radius:50%;display:grid;place-items:center;background:#454a52;color:#fff;font-weight:600;font-size:.82rem}.dmc-inv-who{min-width:0;display:flex;flex-direction:column}.dmc-inv-name{font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.dmc-inv-mail{font-size:.75rem;color:var(--dmc-muted-dark);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.dmc-inv-kind{font-size:.68rem;font-weight:600;padding:.1rem .4rem;border-radius:4px;border:1px solid #4a4f57;color:var(--dmc-muted-dark)}.dmc-inv-kind[data-kind=user]{border-color:transparent;background:#3a3f46;color:#c9cdd2}.dmc-inv-tick{width:1.25rem;height:1.25rem;border-radius:50%;display:grid;place-items:center;border:1.5px solid #5a6069;color:transparent}.dmc-inv-tick svg{width:12px;height:12px}.dmc-inv-row.is-picked .dmc-inv-tick{background:var(--dmc-accent-solid);border-color:transparent;color:#fff}.dmc-inv-only{margin:.25rem 0 0;color:var(--dmc-on-dark);font-size:.88rem;line-height:1.5}.dmc-inv-empty{margin:.5rem .25rem;color:var(--dmc-muted-dark);font-size:.85rem;line-height:1.45}.dmc-inv-pair{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.4fr) auto;gap:.4rem}.dmc-inv-add{width:2.4rem;min-height:2.4rem;padding:0;background:var(--dmc-raised);border-color:#40454c;color:var(--dmc-on-dark)}.dmc-inv-add:hover{background:#3a3f46;border-color:#5a6069}.dmc-inv-add svg{width:17px;height:17px}.dmc-inv-foot{flex:none;border-top:1px solid var(--dmc-line);padding:.75rem 1rem calc(.85rem + env(safe-area-inset-bottom,0px));display:grid;gap:.55rem}.dmc-inv-via{border:0;margin:0;padding:0;min-width:0;display:flex;flex-wrap:wrap;align-items:center;gap:.35rem 1rem}.dmc-inv-via legend{float:left;width:100%;margin:0 0 .1rem;padding:0;font-size:.72rem;font-weight:600;text-transform:uppercase;letter-spacing:.06em;color:var(--dmc-muted-dark)}.dmc-inv-via label{display:inline-flex;align-items:center;gap:.35rem;cursor:pointer;font-size:.86rem}.dmc-inv-via input{width:16px;height:16px;margin:0;accent-color:var(--dmc-accent)}.dmc-inv-hint{margin:0;font-size:.75rem;line-height:1.45;color:#80868e}.dmc-inv-status{margin:0;font-size:.82rem;line-height:1.4;color:#9fcb8d}.dmc-inv-status.is-err{color:#ff8a85}.dmc-inv-results{list-style:none;margin:0;padding:0;display:grid;gap:.25rem;max-height:7.5rem;overflow-y:auto;font-size:.8rem}.dmc-inv-res{display:flex;gap:.4rem;line-height:1.35;color:var(--dmc-muted-dark)}.dmc-inv-res:before{content:"";flex:none;width:6px;height:6px;margin-top:.4em;border-radius:50%;background:#6fb56a}.dmc-inv-res.is-err:before{background:#e0605a}.dmc-inv-rname{color:var(--dmc-on-dark);font-weight:500;flex:none;max-width:45%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dmc-inv-link{font-size:.78rem;color:var(--dmc-muted-dark)}.dmc-inv-btns{display:flex;gap:.5rem}.dmc-inv-btns .dmc-btn{flex:1;min-height:2.5rem}.dmc-inv-copy svg,.dmc-inv-send svg{width:16px;height:16px}.dmc-inv-send:disabled{opacity:1;background:var(--dmc-raised);color:#80868e}.dmc-tool--invite svg{width:20px;height:20px}@media(max-width:760px){.dmc.dmc--app{padding:1rem .75rem}.dmc-panel{grid-template-columns:1fr;gap:1.1rem;padding:1rem}.dmc-panel--msg{padding:2rem 1.25rem}.dmc-title{font-size:1.15rem}.dmc-top{padding:0 .75rem}.dmc-waiting{left:.5rem;right:.5rem;top:calc(44px + .5rem);width:auto}.dmc-bar{display:flex;justify-content:space-around;gap:.25rem;padding:.45rem .4rem calc(.45rem + env(safe-area-inset-bottom,0px))}.dmc-bar-mid{display:contents}.dmc-tool{width:44px;height:44px;min-width:0;padding:0;background:var(--dmc-raised)}.dmc-tool.is-off{background:#d9453f33}.dmc-lbl{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%)}.dmc-bar-side:empty{display:none}.dmc-bar-end{display:contents}.dmc-btn--leave{width:52px;padding:0;background:var(--dmc-red)}.dmc-badge{top:-4px;right:-4px}.dmc-tile--me{right:.5rem;bottom:.5rem}.dmc.dmc--app:has(.dmc-call.is-chat){z-index:1000}.dmc-chat{position:fixed;inset:0;z-index:1000;width:auto;border:0;transform:translateY(100%);transition:transform .22s ease,visibility 0s .22s;visibility:hidden}.dmc-call.is-chat .dmc-chat{width:auto;transform:none;visibility:visible;transition:transform .22s ease}.dmc-chat>*{min-width:0}.dmc-chat-back{display:grid}.dmc-chat-close{display:none}.dmc-chat-head{padding:0 .75rem 0 .35rem}.dmc-send{padding-bottom:calc(.75rem + env(safe-area-inset-bottom,0px))}.dmc.dmc--app:has(.dmc-invite.is-open){z-index:1000}.dmc-invite{position:fixed;inset:0;z-index:1000;width:auto;border:0;transform:translateY(100%);transition:transform .22s ease,visibility 0s .22s;visibility:hidden}.dmc-invite.is-open{width:auto;border:0;transform:none;visibility:visible;transition:transform .22s ease}.dmc-invite>*{min-width:0}}@media(max-width:760px)and (orientation:portrait){.dmc-tile--me{width:clamp(6rem,26%,8.5rem);aspect-ratio:3 / 4}}@media(prefers-reduced-motion:reduce){.dmc *,.dmc *:after{animation:none!important;transition:none!important}}
|
|
1
|
+
.dmc{--dmc-accent: #4f7cff;--dmc-accent-solid: color-mix(in srgb, var(--dmc-accent) 84%, #000);--dmc-accent-soft: color-mix(in srgb, var(--dmc-accent) 18%, transparent);--dmc-accent-text: color-mix(in srgb, var(--dmc-accent) 55%, #fff);--dmc-gap: 8px;--dmc-base: #1b1d21;--dmc-panel: #25282d;--dmc-raised: #2f3338;--dmc-line: #363a40;--dmc-on-dark: #e8eaed;--dmc-muted-dark: #9aa0a8;--dmc-red: #d9453f;--dmc-red-hover: #c23a35;--dmc-page: #f3f4f6;--dmc-card: #fff;--dmc-ink: #1f2328;--dmc-ink-muted: #5b6470;--dmc-border: #dde0e4;--dmc-offset: 0px;--dmc-left: 0px;--dmc-right: 0px;display:flex;flex-direction:column;gap:1rem;color:var(--dmc-ink)}.dmc [hidden]{display:none!important}html.dmc-page,html.dmc-page body{overflow:hidden!important}.dmc.dmc--app{position:fixed;z-index:40;top:var(--dmc-offset);left:var(--dmc-left);right:var(--dmc-right);bottom:0;margin:0;background:var(--dmc-page);overflow-y:auto;overscroll-behavior:contain;padding:2.5rem 1rem;align-items:center;justify-content:safe center;gap:1rem}.dmc.dmc--app[data-stage=in]{padding:0;background:var(--dmc-base);overflow:hidden}.dmc.dmc--app>*{width:min(60rem,100%);flex:none}.dmc.dmc--app>.dmc-call{width:100%;flex:1 1 auto}.dmc-head{display:flex;align-items:center;gap:.75rem;flex-wrap:wrap}.dmc.dmc--app:not([data-stage=check])>.dmc-head{width:min(28rem,100%);justify-content:center;text-align:center}.dmc-title{font-size:1.35rem;font-weight:600;margin:0;flex:1 1 auto;letter-spacing:-.01em;color:var(--dmc-ink)}.dmc-btn{font:inherit;font-size:.9rem;cursor:pointer;padding:.5rem .95rem;min-height:2.5rem;border-radius:8px;border:1px solid var(--dmc-border);background:var(--dmc-card);color:inherit;display:inline-flex;align-items:center;justify-content:center;gap:.45rem;position:relative;font-weight:500;transition:background-color .15s,border-color .15s,color .15s}.dmc-btn:hover{border-color:#b9bec5;background:#f7f8f9}.dmc-btn:disabled{opacity:.55;cursor:default}.dmc-btn:focus-visible,.dmc-compose:focus-visible,.dmc-input:focus-visible{outline:2px solid var(--dmc-accent);outline-offset:2px}.dmc-i{display:inline-flex}.dmc-btn svg,.dmc-count svg,.dmc-note svg,.dmc-chip svg{width:18px;height:18px;flex:none}.dmc-btn.is-off{background:#fdecec;border-color:#f3c4c2;color:#b3261e}.dmc-btn--go{background:var(--dmc-accent-solid);border-color:transparent;color:#fff;font-weight:600}.dmc-btn--go:hover{background:color-mix(in srgb,var(--dmc-accent) 74%,#000);border-color:transparent}.dmc-btn--small{padding:.3rem .65rem;min-height:2rem;font-size:.82rem}.dmc-panel{display:grid;grid-template-columns:minmax(0,3fr) minmax(0,2fr);gap:1.75rem;align-items:center;background:var(--dmc-card);border:1px solid var(--dmc-border);border-radius:12px;padding:1.5rem;box-shadow:0 1px 2px #1018280d,0 8px 24px #1018280f}.dmc-panel--msg{display:flex;flex-direction:column;align-items:center;text-align:center;padding:2.5rem 2rem;gap:.25rem;width:min(28rem,100%)!important;align-self:center}.dmc-panel--msg h2{margin:.75rem 0 .25rem;font-size:1.2rem;font-weight:600;color:var(--dmc-ink)}.dmc-panel--msg p{color:var(--dmc-ink-muted);margin:0 0 1.25rem;font-size:.92rem;line-height:1.5}.dmc-pulse{width:64px;height:64px;border-radius:50%;display:grid;place-items:center;color:var(--dmc-accent-solid);background:var(--dmc-accent-soft);box-shadow:0 0 color-mix(in srgb,var(--dmc-accent) 35%,transparent);animation:dmc-pulse 2.2s infinite}.dmc-pulse svg{width:26px;height:26px}@keyframes dmc-pulse{70%{box-shadow:0 0 0 16px transparent}to{box-shadow:0 0 0 0 transparent}}.dmc-preview{position:relative;aspect-ratio:16 / 9;background:var(--dmc-base);border-radius:10px;overflow:hidden}.dmc-preview video,.dmc-tile video{width:100%;height:100%;object-fit:cover;display:block}.dmc-self,.dmc-self-preview{transform:scaleX(-1)}.dmc-nocam{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.6rem;color:var(--dmc-muted-dark);margin:0;font-size:.9rem}.dmc-avatar-dot{width:76px;height:76px;border-radius:50%;display:grid;place-items:center;background:var(--dmc-raised);color:var(--dmc-on-dark);font-size:1.8rem;font-weight:600}.dmc-preview-tools{position:absolute;left:0;right:0;bottom:.75rem;display:flex;justify-content:center;gap:.5rem}.dmc-round{width:44px;height:44px;min-height:0;padding:0;border-radius:10px;border:1px solid rgba(255,255,255,.14);background:#1b1d21b8;color:#fff;backdrop-filter:blur(6px)}.dmc-round:hover{background:#2f3338e6;border-color:#ffffff4d}.dmc-round.is-off{background:var(--dmc-red);border-color:transparent;color:#fff}.dmc-check h2{margin:0 0 .35rem;font-size:1.25rem;font-weight:600;color:var(--dmc-ink)}.dmc-label{display:grid;gap:.35rem;margin:1rem 0 .25rem;font-weight:500;font-size:.88rem;color:var(--dmc-ink)}.dmc-input{font:inherit;font-size:.95rem;padding:.55rem .75rem;min-height:2.5rem;border:1px solid var(--dmc-border);border-radius:8px;background:#fff;color:var(--dmc-ink);min-width:0}.dmc-label .dmc-input:focus{outline:0;border-color:var(--dmc-accent);box-shadow:0 0 0 3px var(--dmc-accent-soft)}.dmc-row{display:flex;gap:.5rem;flex-wrap:wrap;margin:.9rem 0}.dmc-chipbtn{font-size:.86rem}.dmc-round.is-on{background:var(--dmc-accent-solid);border-color:transparent;color:#fff}.dmc-chipbtn.is-on{background:var(--dmc-accent-soft);border-color:var(--dmc-accent);color:inherit}.dmc-note{color:var(--dmc-ink-muted);font-size:.9rem;line-height:1.45;margin:0}.dmc-note--rec{display:flex;gap:.5rem;align-items:flex-start;font-size:.85rem;margin:.25rem 0 0}.dmc-note--rec svg{color:var(--dmc-red);fill:currentColor;width:10px;height:10px;margin-top:.35rem}.dmc-error{color:#b3261e;font-weight:500;font-size:.9rem;margin:.5rem 0 0}.dmc-check .dmc-btn--go{width:100%;min-height:2.75rem;margin-top:1rem;font-size:.95rem}.dmc-call{display:flex;color:var(--dmc-on-dark);background:var(--dmc-base);overflow:hidden;color-scheme:dark;min-height:0;font-size:14px}.dmc-stage{position:relative;flex:1 1 auto;min-width:0;display:flex;flex-direction:column}.dmc-top{order:-1;flex:none;display:flex;align-items:center;justify-content:space-between;gap:1rem;height:44px;padding:0 1rem;background:var(--dmc-panel);border-bottom:1px solid var(--dmc-line)}.dmc-top-l{display:flex;align-items:center;gap:.75rem;min-width:0}.dmc-top .dmc-top-title{margin:0;font-size:.92rem;line-height:1.3;font-weight:600;letter-spacing:0;color:var(--dmc-on-dark);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.dmc-timer{flex:none;font-size:.82rem;color:var(--dmc-muted-dark);font-variant-numeric:tabular-nums;padding-left:.75rem;border-left:1px solid var(--dmc-line)}.dmc-top-r{display:flex;align-items:center;gap:.6rem;flex:none}.dmc-rec{display:inline-flex;align-items:center;gap:.4rem;color:#ff8a85;background:#d9453f29;border:1px solid rgba(217,69,63,.45);border-radius:6px;padding:.12rem .5rem;font-size:.74rem;font-weight:600;letter-spacing:.02em}.dmc-rec-dot{width:7px;height:7px;border-radius:50%;background:#ff5a52;animation:dmc-blink 1.4s infinite}@keyframes dmc-blink{50%{opacity:.3}}.dmc-count{display:inline-flex;align-items:center;gap:.35rem;font-size:.82rem;color:var(--dmc-muted-dark);font-variant-numeric:tabular-nums}.dmc-count svg{width:16px;height:16px}.dmc-main{position:relative;flex:1 1 auto;min-height:0}.dmc-remote{position:absolute;inset:var(--dmc-gap);display:flex;flex-wrap:wrap;align-content:center;justify-content:center;gap:var(--dmc-gap);--cols: 1;--rows: 1}.dmc-cell{min-width:0;min-height:0;display:flex;width:var(--tile-w, calc((100% - (var(--cols) - 1) * var(--dmc-gap)) / var(--cols)));height:var(--tile-h, calc((100% - (var(--rows) - 1) * var(--dmc-gap)) / var(--rows)))}.dmc-main[data-tiles="2"] .dmc-remote{--cols: 2}.dmc-main[data-tiles="3"] .dmc-remote,.dmc-main[data-tiles="4"] .dmc-remote{--cols: 2;--rows: 2}.dmc-main[data-tiles="5"] .dmc-remote,.dmc-main[data-tiles="6"] .dmc-remote{--cols: 3;--rows: 2}.dmc-tile{position:relative;margin:0;background:var(--dmc-panel);border-radius:8px;overflow:hidden}.dmc-cell .dmc-tile{flex:1 1 auto;width:100%;height:100%}.dmc-tile video{position:relative;z-index:1}.dmc-avatar{position:absolute;inset:0;display:grid;place-items:center;z-index:0}.dmc-avatar span{width:clamp(3rem,18%,6rem);aspect-ratio:1;border-radius:50%;display:grid;place-items:center;background:var(--dmc-raised);color:var(--dmc-on-dark);font-size:clamp(1.1rem,2.4vw,2.2rem);font-weight:600}.dmc-tile--me .dmc-avatar{z-index:2;background:var(--dmc-panel)}.dmc-chip{position:absolute;left:.5rem;bottom:.5rem;z-index:3;display:inline-flex;align-items:center;gap:.3rem;max-width:calc(100% - 1rem);padding:.18rem .5rem;border-radius:6px;background:#1b1d21c7;color:var(--dmc-on-dark);font-size:.78rem;font-weight:500;line-height:1.35}.dmc-chip>span:last-child{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.dmc-chip-mute{display:inline-flex;color:#ff8a85}.dmc-chip-mute svg{width:13px;height:13px}.dmc-tile--me{position:absolute;z-index:2;right:calc(var(--dmc-gap) * 2);bottom:calc(var(--dmc-gap) * 2);width:clamp(10rem,17%,15rem);aspect-ratio:16 / 9;box-shadow:0 6px 20px #00000073;outline:1px solid rgba(255,255,255,.14)}.dmc-tile--me .dmc-chip{left:.35rem;bottom:.35rem;font-size:.7rem;padding:.1rem .4rem}.dmc-main[data-tiles="0"] .dmc-tile--me{inset:0;width:auto;aspect-ratio:auto;border-radius:0;outline:0;box-shadow:none}.dmc-main[data-tiles="0"] .dmc-tile--me .dmc-chip{left:.75rem;bottom:.75rem;font-size:.78rem;padding:.18rem .5rem}.dmc-main[data-tiles="0"] .dmc-tile--me:after{content:"";position:absolute;inset:0;z-index:2;background:#1b1d218c;pointer-events:none}.dmc-alone{position:absolute;inset:0;z-index:2;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;padding:1rem;pointer-events:none}.dmc-alone-title{margin:0;font-size:1.1rem;font-weight:600;color:#fff}.dmc-alone-sub{margin:.35rem 0 0;color:#c9cdd2;font-size:.88rem}.dmc-waiting{position:absolute;z-index:4;top:calc(44px + .75rem);right:.75rem;width:min(20rem,calc(100% - 1.5rem));padding:.85rem;border-radius:10px;background:var(--dmc-raised);box-shadow:0 12px 32px #00000073;border:1px solid #40454c;animation:dmc-drop .2s ease}@keyframes dmc-drop{0%{opacity:0;transform:translateY(-4px)}}.dmc-waiting h3{display:flex;align-items:center;gap:.5rem;margin:0 0 .7rem;font-size:.85rem;font-weight:600;color:var(--dmc-on-dark)}.dmc-waiting-dot{width:8px;height:8px;border-radius:50%;background:var(--dmc-accent);box-shadow:0 0 0 3px var(--dmc-accent-soft)}.dmc-waiting ul{list-style:none;margin:0;padding:0;display:grid;gap:.7rem;max-height:14rem;overflow-y:auto}.dmc-waiting li{display:grid;grid-template-columns:auto 1fr;align-items:center;gap:.4rem .6rem}.dmc-winitial{width:2rem;height:2rem;border-radius:50%;display:grid;place-items:center;background:#454a52;color:#fff;font-weight:600;font-size:.85rem}.dmc-wname{font-weight:500;color:var(--dmc-on-dark);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.dmc-wbtns{grid-column:2;display:flex;gap:.4rem}.dmc-waiting .dmc-btn svg{width:15px;height:15px}.dmc-waiting .dmc-btn--go{background:var(--dmc-accent-solid);color:#fff}.dmc-btn--ghost{background:transparent;color:var(--dmc-on-dark);border-color:#4a4f57}.dmc-btn--ghost:hover{background:#ffffff0f;border-color:#626872}.dmc-bar{flex:none;display:grid;grid-template-columns:1fr auto 1fr;align-items:center;gap:.5rem;padding:.5rem 1rem;background:var(--dmc-panel);border-top:1px solid var(--dmc-line)}.dmc-bar-mid{display:flex;align-items:center;gap:.35rem}.dmc-bar-end{display:flex;justify-content:flex-end}.dmc-tool{flex-direction:column;gap:3px;min-width:4.25rem;height:3.5rem;min-height:0;padding:.3rem .6rem;border-radius:10px;border:1px solid transparent;background:transparent;color:var(--dmc-on-dark);flex:none;font-weight:400}.dmc-tool:hover{background:var(--dmc-raised);border-color:transparent}.dmc-tool svg{width:20px;height:20px}.dmc-lbl{font-size:.72rem;line-height:1;color:var(--dmc-muted-dark);white-space:nowrap}.dmc-tool:hover .dmc-lbl{color:var(--dmc-on-dark)}.dmc-tool.is-off{background:transparent;color:#ff8a85}.dmc-tool.is-off:hover{background:#d9453f24}.dmc-tool.is-off .dmc-lbl{color:#ff8a85}.dmc-tool.is-on{background:var(--dmc-accent-soft);color:var(--dmc-accent-text)}.dmc-tool.is-on .dmc-lbl{color:var(--dmc-accent-text)}.dmc-btn--rec svg{color:var(--dmc-red);fill:currentColor;width:16px;height:16px;margin:2px 0}.dmc-btn--rec.is-on{background:#d9453f2e;color:#ff8a85}.dmc-btn--rec.is-on .dmc-lbl{color:#ff8a85}.dmc-btn--rec.is-on svg{color:#ff5a52;animation:dmc-blink 1.4s infinite}.dmc-btn--leave{background:var(--dmc-red);color:#fff;flex-direction:row;gap:.45rem;height:2.75rem;min-width:0;padding:0 1.1rem;border-radius:10px;font-weight:600}.dmc-btn--leave:hover{background:var(--dmc-red-hover)}.dmc-btn--leave .dmc-lbl,.dmc-btn--leave:hover .dmc-lbl{color:#fff;font-size:.85rem}.dmc-btn--leave svg{width:18px;height:18px}.dmc-badge{position:absolute;top:2px;right:10px;min-width:1.1rem;height:1.1rem;padding:0 .28rem;border-radius:999px;background:var(--dmc-accent-solid);color:#fff;font-size:.66rem;font-weight:600;line-height:1.1rem;text-align:center;box-shadow:0 0 0 2px var(--dmc-panel)}.dmc-chat{flex:none;width:0;display:flex;flex-direction:column;background:var(--dmc-panel);color:var(--dmc-on-dark);overflow:hidden;transition:width .2s ease;border-left:1px solid var(--dmc-line);font-size:14px}.dmc-call:not(.is-chat) .dmc-chat{border-left-width:0}.dmc-call.is-chat .dmc-chat{width:20rem}.dmc-chat>*{min-width:20rem}.dmc-chat-head{flex:none;display:flex;align-items:center;gap:.5rem;height:44px;padding:0 .5rem 0 1rem;border-bottom:1px solid var(--dmc-line)}.dmc-chat-titles{flex:1;min-width:0;display:flex;align-items:baseline;gap:.6rem}.dmc-chat-titles h3{margin:0;font-size:.92rem;font-weight:600;color:var(--dmc-on-dark)}.dmc-chat-titles span{font-size:.75rem;color:var(--dmc-muted-dark);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.dmc-chat-back,.dmc-chat-close{width:2rem;height:2rem;border:0;border-radius:8px;background:transparent;color:var(--dmc-muted-dark);cursor:pointer;display:grid;place-items:center}.dmc-chat-back:hover,.dmc-chat-close:hover{background:var(--dmc-raised);color:var(--dmc-on-dark)}.dmc-chat-back:focus-visible,.dmc-chat-close:focus-visible{outline:2px solid var(--dmc-accent);outline-offset:1px}.dmc-chat-back svg,.dmc-chat-close svg{width:18px;height:18px}.dmc-chat-back{display:none}.dmc-chat-body{position:relative;flex:1;min-height:0;display:flex}.dmc-messages{list-style:none;margin:0;padding:.5rem 1rem 1rem;flex:1;overflow-y:auto;overscroll-behavior:contain;scrollbar-width:thin;scrollbar-color:#43484f transparent}.dmc-chat-empty{position:absolute;inset:auto 1.5rem 45%;margin:0;text-align:center;color:var(--dmc-muted-dark);font-size:.85rem;line-height:1.5}.dmc-msg{display:flex;flex-direction:column;margin-top:3px}.dmc-msg.is-first{margin-top:.85rem}.dmc-msg.is-mine{align-items:flex-end}.dmc-msg.is-other{align-items:flex-start}.dmc-msg.is-sys{align-items:center;margin-top:.6rem}.dmc-msg.is-sys+.dmc-msg.is-sys:not(.has-day){margin-top:.2rem}.dmc-msg.has-day.is-first{margin-top:.35rem}.dmc-day{align-self:stretch;display:flex;align-items:center;gap:.75rem;margin:.5rem 0;color:var(--dmc-muted-dark);font-size:.7rem;font-weight:600;text-transform:uppercase;letter-spacing:.06em}.dmc-day:before,.dmc-day:after{content:"";flex:1;height:1px;background:var(--dmc-line)}.dmc-sys{color:#80868e;font-size:.76rem;line-height:1.4;text-align:center}.dmc-meta{display:flex;align-items:baseline;gap:.5rem;margin:0 .15rem .2rem;max-width:88%}.dmc-from{font-size:.8rem;font-weight:500;color:#8fb0ff;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.dmc-time{font-size:.7rem;color:#80868e;font-variant-numeric:tabular-nums;flex:none}.dmc-bubble{max-width:88%;padding:.45rem .7rem;border-radius:8px;background:var(--dmc-raised);color:var(--dmc-on-dark);line-height:1.42;overflow-wrap:anywhere}.is-mine .dmc-bubble{background:color-mix(in srgb,var(--dmc-accent) 20%,var(--dmc-panel));color:#f1f4ff}.dmc-text{white-space:pre-wrap}.dmc-msg[data-hue="1"] .dmc-from{color:#6fc2ad}.dmc-msg[data-hue="2"] .dmc-from{color:#d6ad72}.dmc-msg[data-hue="3"] .dmc-from{color:#bf9fdc}.dmc-msg[data-hue="4"] .dmc-from{color:#e0948e}.dmc-msg[data-hue="5"] .dmc-from{color:#9fcb8d}.dmc-msg[data-hue="6"] .dmc-from{color:#7fbad6}.dmc-msg[data-hue="7"] .dmc-from{color:#d49fba}.dmc-newchip{position:absolute;left:50%;bottom:.75rem;transform:translate(-50%);display:inline-flex;align-items:center;gap:.35rem;cursor:pointer;border:1px solid #40454c;border-radius:8px;padding:.35rem .7rem;background:var(--dmc-raised);color:var(--dmc-on-dark);font:inherit;font-size:.78rem;box-shadow:0 4px 14px #0006}.dmc-newchip svg{width:14px;height:14px;color:var(--dmc-accent-text)}.dmc-send{flex:none;display:flex;align-items:flex-end;gap:.5rem;padding:.75rem 1rem;border-top:1px solid var(--dmc-line)}.dmc-compose{flex:1;resize:none;border:1px solid #40454c;border-radius:8px;padding:.5rem .7rem;min-height:0;background:var(--dmc-base);color:var(--dmc-on-dark);line-height:1.35rem;font-size:14px;overflow-y:hidden;max-height:calc(6.4rem + 2px);display:block}.dmc-compose::placeholder{color:#80868e}.dmc-compose:focus{outline:0;border-color:var(--dmc-accent);box-shadow:0 0 0 3px var(--dmc-accent-soft)}.dmc-sendbtn{width:2.4rem;height:2.4rem;min-height:0;padding:0;border:0;border-radius:8px;background:var(--dmc-accent-solid);color:#fff;flex:none}.dmc-sendbtn:hover{background:color-mix(in srgb,var(--dmc-accent) 74%,#000)}.dmc-sendbtn:disabled{opacity:1;background:var(--dmc-raised);color:#80868e}.dmc-sendbtn svg{width:17px;height:17px;margin-left:-2px}.dmc-invite{flex:none;width:0;display:flex;flex-direction:column;background:var(--dmc-panel);color:var(--dmc-on-dark);overflow:hidden;transition:width .2s ease;border-left:0 solid var(--dmc-line);font-size:14px}.dmc-invite.is-open{width:22rem;border-left-width:1px}.dmc-invite>*{min-width:22rem}.dmc .dmc-invite ul{margin:0;padding:0;list-style:none}.dmc .dmc-invite li{margin:0}.dmc-inv-body{flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain;padding:.85rem 1rem 1rem;scrollbar-width:thin;scrollbar-color:#43484f transparent}.dmc-inv-input{font:inherit;font-size:14px;width:100%;min-width:0;min-height:2.4rem;padding:.45rem .7rem;border:1px solid #40454c;border-radius:8px;background:var(--dmc-base);color:var(--dmc-on-dark)}.dmc-inv-input::placeholder{color:#80868e}.dmc-inv-input:focus{outline:0;border-color:var(--dmc-accent);box-shadow:0 0 0 3px var(--dmc-accent-soft)}.dmc-inv-search{position:relative;display:block}.dmc-inv-search .dmc-inv-input{padding-left:2.2rem}.dmc-inv-sico{position:absolute;left:.7rem;top:50%;transform:translateY(-50%);display:inline-flex;color:#80868e;pointer-events:none}.dmc-inv-sico svg{width:16px;height:16px}.dmc-inv-label{margin:1rem 0 .4rem;font-size:.72rem;font-weight:600;text-transform:uppercase;letter-spacing:.06em;color:var(--dmc-muted-dark)}.dmc-inv-chips{margin-top:.75rem}.dmc-inv-chips ul{display:flex;flex-wrap:wrap;gap:.35rem}.dmc-inv-chip{display:inline-flex;align-items:center;gap:.35rem;max-width:100%;padding:.2rem .25rem .2rem .6rem;border-radius:6px;background:var(--dmc-accent-soft);color:var(--dmc-accent-text);font-size:.8rem}.dmc-inv-chipname{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:#f1f4ff;font-weight:500}.dmc-inv-chipkind{font-size:.68rem;opacity:.85}.dmc-inv-chipx{display:grid;place-items:center;width:1.4rem;height:1.4rem;border:0;border-radius:4px;background:transparent;color:inherit;cursor:pointer;padding:0}.dmc-inv-chipx:hover{background:#ffffff1a}.dmc-inv-chipx:focus-visible{outline:2px solid var(--dmc-accent);outline-offset:1px}.dmc-inv-chipx svg{width:13px;height:13px}.dmc-inv-list{list-style:none;margin:0;padding:0;display:grid;gap:2px}.dmc-inv-row{width:100%;display:grid;grid-template-columns:auto 1fr auto auto;align-items:center;gap:.6rem;padding:.45rem .5rem;border:1px solid transparent;border-radius:8px;background:transparent;color:inherit;font:inherit;text-align:left;cursor:pointer}.dmc-inv-row:hover{background:var(--dmc-raised)}.dmc-inv-row:focus-visible{outline:2px solid var(--dmc-accent);outline-offset:-2px}.dmc-inv-row.is-picked{background:var(--dmc-accent-soft)}.dmc-inv-av{width:2rem;height:2rem;border-radius:50%;display:grid;place-items:center;background:#454a52;color:#fff;font-weight:600;font-size:.82rem}.dmc-inv-who{min-width:0;display:flex;flex-direction:column}.dmc-inv-name{font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.dmc-inv-mail{font-size:.75rem;color:var(--dmc-muted-dark);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.dmc-inv-kind{font-size:.68rem;font-weight:600;padding:.1rem .4rem;border-radius:4px;border:1px solid #4a4f57;color:var(--dmc-muted-dark)}.dmc-inv-kind[data-kind=user]{border-color:transparent;background:#3a3f46;color:#c9cdd2}.dmc-inv-tick{width:1.25rem;height:1.25rem;border-radius:50%;display:grid;place-items:center;border:1.5px solid #5a6069;color:transparent}.dmc-inv-tick svg{width:12px;height:12px}.dmc-inv-row.is-picked .dmc-inv-tick{background:var(--dmc-accent-solid);border-color:transparent;color:#fff}.dmc-inv-only{margin:.25rem 0 0;color:var(--dmc-on-dark);font-size:.88rem;line-height:1.5}.dmc-inv-empty{margin:.5rem .25rem;color:var(--dmc-muted-dark);font-size:.85rem;line-height:1.45}.dmc-inv-pair{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.4fr) auto;gap:.4rem}.dmc-inv-add{width:2.4rem;min-height:2.4rem;padding:0;background:var(--dmc-raised);border-color:#40454c;color:var(--dmc-on-dark)}.dmc-inv-add:hover{background:#3a3f46;border-color:#5a6069}.dmc-inv-add svg{width:17px;height:17px}.dmc-inv-foot{flex:none;border-top:1px solid var(--dmc-line);padding:.75rem 1rem calc(.85rem + env(safe-area-inset-bottom,0px));display:grid;gap:.55rem}.dmc-inv-via{border:0;margin:0;padding:0;min-width:0;display:flex;flex-wrap:wrap;align-items:center;gap:.35rem 1rem}.dmc-inv-via legend{float:left;width:100%;margin:0 0 .1rem;padding:0;font-size:.72rem;font-weight:600;text-transform:uppercase;letter-spacing:.06em;color:var(--dmc-muted-dark)}.dmc-inv-via label{display:inline-flex;align-items:center;gap:.35rem;cursor:pointer;font-size:.86rem}.dmc-inv-via input{width:16px;height:16px;margin:0;accent-color:var(--dmc-accent)}.dmc-inv-hint{margin:0;font-size:.75rem;line-height:1.45;color:#80868e}.dmc-inv-status{margin:0;font-size:.82rem;line-height:1.4;color:#9fcb8d}.dmc-inv-status.is-err{color:#ff8a85}.dmc-inv-results{list-style:none;margin:0;padding:0;display:grid;gap:.25rem;max-height:7.5rem;overflow-y:auto;font-size:.8rem}.dmc-inv-res{display:flex;gap:.4rem;line-height:1.35;color:var(--dmc-muted-dark)}.dmc-inv-res:before{content:"";flex:none;width:6px;height:6px;margin-top:.4em;border-radius:50%;background:#6fb56a}.dmc-inv-res.is-err:before{background:#e0605a}.dmc-inv-rname{color:var(--dmc-on-dark);font-weight:500;flex:none;max-width:45%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dmc-inv-link{font-size:.78rem;color:var(--dmc-muted-dark)}.dmc-inv-btns{display:flex;gap:.5rem}.dmc-inv-btns .dmc-btn{flex:1;min-height:2.5rem}.dmc-inv-copy svg,.dmc-inv-send svg{width:16px;height:16px}.dmc-inv-send:disabled{opacity:1;background:var(--dmc-raised);color:#80868e}.dmc-tool--invite svg{width:20px;height:20px}@media(max-width:760px){.dmc.dmc--app{padding:1rem .75rem}.dmc-panel{grid-template-columns:1fr;gap:1.1rem;padding:1rem}.dmc-panel--msg{padding:2rem 1.25rem}.dmc-title{font-size:1.15rem}.dmc-top{padding:0 .75rem}.dmc-waiting{left:.5rem;right:.5rem;top:calc(44px + .5rem);width:auto}.dmc-bar{display:flex;justify-content:space-around;gap:.25rem;padding:.45rem .4rem calc(.45rem + env(safe-area-inset-bottom,0px))}.dmc-bar-mid{display:contents}.dmc-tool{width:44px;height:44px;min-width:0;padding:0;background:var(--dmc-raised)}.dmc-tool.is-off{background:#d9453f33}.dmc-lbl{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%)}.dmc-bar-side:empty{display:none}.dmc-bar-end{display:contents}.dmc-btn--leave{width:52px;padding:0;background:var(--dmc-red)}.dmc-badge{top:-4px;right:-4px}.dmc-tile--me{right:.5rem;bottom:.5rem}.dmc.dmc--app:has(.dmc-call.is-chat){z-index:1000}.dmc-chat{position:fixed;inset:0;z-index:1000;width:auto;border:0;transform:translateY(100%);transition:transform .22s ease,visibility 0s .22s;visibility:hidden}.dmc-call.is-chat .dmc-chat{width:auto;transform:none;visibility:visible;transition:transform .22s ease}.dmc-chat>*{min-width:0}.dmc-chat-back{display:grid}.dmc-chat-close{display:none}.dmc-chat-head{padding:0 .75rem 0 .35rem}.dmc-send{padding-bottom:calc(.75rem + env(safe-area-inset-bottom,0px))}.dmc.dmc--app:has(.dmc-invite.is-open){z-index:1000}.dmc-invite{position:fixed;inset:0;z-index:1000;width:auto;border:0;transform:translateY(100%);transition:transform .22s ease,visibility 0s .22s;visibility:hidden}.dmc-invite.is-open{width:auto;border:0;transform:none;visibility:visible;transition:transform .22s ease}.dmc-invite>*{min-width:0}}@media(max-width:760px)and (orientation:portrait){.dmc-tile--me{width:clamp(6rem,26%,8.5rem);aspect-ratio:3 / 4}}@media(prefers-reduced-motion:reduce){.dmc *,.dmc *:after{animation:none!important;transition:none!important}}
|
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
* The screen is domma-reactive (M) - stages, controls, people waiting, chat -
|
|
13
13
|
* and plain code only where bindings cannot reach: the <video> elements'
|
|
14
14
|
* streams, the recording canvas, and the RTCPeerConnections themselves.
|
|
15
|
+
* Background blur is blur.js, imported the first time it is turned on; the choice
|
|
16
|
+
* is remembered in this browser (localStorage 'dm-call:blur').
|
|
15
17
|
* Browsers connect to each other directly (a mesh - up to 6 people); the
|
|
16
18
|
* site only passes the messages that set that up (see _lib/call/routes.js).
|
|
17
19
|
* Every piece of text goes in through bindings (textContent), never as HTML.
|
|
@@ -44,6 +46,7 @@ const TEMPLATE = `
|
|
|
44
46
|
<div class="dmc-preview-tools">
|
|
45
47
|
<button type="button" class="dmc-btn dmc-round" data-on-click="toggleMic" data-bind-class="!micOn.value && 'is-off'" data-bind-aria-pressed="!micOn.value" data-bind-aria-label="micOn.value ? 'Turn the microphone off' : 'Turn the microphone on'">${pair('micOn.value', 'mic', 'mic-off')}</button>
|
|
46
48
|
<button type="button" class="dmc-btn dmc-round" data-on-click="toggleCam" data-bind-class="!camOn.value && 'is-off'" data-bind-aria-pressed="!camOn.value" data-bind-aria-label="camOn.value ? 'Turn the camera off' : 'Turn the camera on'">${pair('camOn.value', 'video', 'video-off')}</button>
|
|
49
|
+
<button type="button" class="dmc-btn dmc-round dmc-round--blur" data-on-click="toggleBlur" data-bind-hidden="!blurOffered.value" data-bind-disabled="blurBusy.value" data-bind-class="blurOn.value && 'is-on'" data-bind-aria-pressed="blurOn.value" data-bind-aria-label="blurOn.value ? 'Stop blurring your background' : 'Blur your background'" data-bind-title="blurLabel.value">${ico('sparkles')}</button>
|
|
47
50
|
</div>
|
|
48
51
|
</div>
|
|
49
52
|
<div class="dmc-check">
|
|
@@ -54,7 +57,9 @@ const TEMPLATE = `
|
|
|
54
57
|
<div class="dmc-row">
|
|
55
58
|
<button type="button" class="dmc-btn dmc-chipbtn" data-on-click="toggleMic" data-bind-class="!micOn.value && 'is-off'" data-bind-aria-pressed="!micOn.value">${pair('micOn.value', 'mic', 'mic-off')}<span data-bind-text="micOn.value ? 'Mic on' : 'Mic off'"></span></button>
|
|
56
59
|
<button type="button" class="dmc-btn dmc-chipbtn" data-on-click="toggleCam" data-bind-class="!camOn.value && 'is-off'" data-bind-aria-pressed="!camOn.value">${pair('camOn.value', 'video', 'video-off')}<span data-bind-text="camOn.value ? 'Camera on' : 'Camera off'"></span></button>
|
|
60
|
+
<button type="button" class="dmc-btn dmc-chipbtn" data-on-click="toggleBlur" data-bind-hidden="!blurOffered.value" data-bind-disabled="blurBusy.value" data-bind-class="blurOn.value && 'is-on'" data-bind-aria-pressed="blurOn.value">${ico('sparkles')}<span data-bind-text="blurLabel.value"></span></button>
|
|
57
61
|
</div>
|
|
62
|
+
<p class="dmc-note" data-bind-hidden="!blurNote.value" data-bind-text="blurNote.value" role="status"></p>
|
|
58
63
|
<p class="dmc-note dmc-note--rec" data-bind-hidden="!recordingAllowed.value">${ico('circle')}<span>The host may record this call. You will see a red Recording notice while they do.</span></p>
|
|
59
64
|
<p class="dmc-error" data-bind-hidden="!error.value" data-bind-text="error.value" role="alert"></p>
|
|
60
65
|
<button type="button" class="dmc-btn dmc-btn--go" data-on-click="join" data-bind-disabled="busy.value">${ico('phone')}<span>Join the call</span></button>
|
|
@@ -118,6 +123,7 @@ const TEMPLATE = `
|
|
|
118
123
|
<div class="dmc-bar-mid">
|
|
119
124
|
<button type="button" class="dmc-btn dmc-tool" data-on-click="toggleMic" data-bind-class="!micOn.value && 'is-off'" data-bind-aria-pressed="!micOn.value" data-bind-aria-label="micOn.value ? 'Mute' : 'Unmute'">${pair('micOn.value', 'mic', 'mic-off')}<span class="dmc-lbl" data-bind-text="micOn.value ? 'Mute' : 'Unmute'"></span></button>
|
|
120
125
|
<button type="button" class="dmc-btn dmc-tool" data-on-click="toggleCam" data-bind-class="!camOn.value && 'is-off'" data-bind-aria-pressed="!camOn.value" data-bind-aria-label="camOn.value ? 'Stop video' : 'Start video'">${pair('camOn.value', 'video', 'video-off')}<span class="dmc-lbl" data-bind-text="camOn.value ? 'Stop video' : 'Start video'"></span></button>
|
|
126
|
+
<button type="button" class="dmc-btn dmc-tool dmc-tool--blur" data-on-click="toggleBlur" data-bind-hidden="!blurOffered.value" data-bind-disabled="blurBusy.value" data-bind-class="blurOn.value && 'is-on'" data-bind-aria-pressed="blurOn.value" data-bind-aria-label="blurOn.value ? 'Stop blurring your background' : 'Blur your background'" data-bind-title="blurOn.value ? 'Stop blurring your background' : 'Blur your background'">${ico('sparkles')}<span class="dmc-lbl" data-bind-text="blurLabel.value"></span></button>
|
|
121
127
|
<button type="button" class="dmc-btn dmc-tool dmc-tool--share" data-on-click="toggleShare" data-bind-hidden="!canShare" data-bind-class="sharing.value && 'is-on'" data-bind-aria-pressed="sharing.value" data-bind-aria-label="sharing.value ? 'Stop sharing' : 'Share your screen'">${ico('monitor')}<span class="dmc-lbl" data-bind-text="sharing.value ? 'Stop share' : 'Share'"></span></button>
|
|
122
128
|
<button type="button" class="dmc-btn dmc-tool dmc-tool--chat" data-on-click="toggleSide" data-bind-class="sideOpen.value && 'is-on'" data-bind-aria-pressed="sideOpen.value" data-bind-aria-label="chatLabel.value">${ico('message-circle')}<span class="dmc-lbl">Chat</span><span class="dmc-badge" data-bind-hidden="!unread.value || sideOpen.value" data-bind-text="unreadText.value"></span></button>
|
|
123
129
|
<button type="button" class="dmc-btn dmc-tool dmc-tool--invite" data-slot="invite" data-on-click="toggleInvite" data-bind-hidden="!canInvite.value" data-bind-class="inviteOpen.value && 'is-on'" data-bind-aria-pressed="inviteOpen.value" aria-label="Add people">${ico('user-plus')}<span class="dmc-lbl">Add people</span></button>
|
|
@@ -235,6 +241,17 @@ function mount(el) {
|
|
|
235
241
|
}
|
|
236
242
|
|
|
237
243
|
// ---- State the screen shows -------------------------------------------------------
|
|
244
|
+
// Blur needs WebAssembly, a canvas that can become a video track, and WebGL2 (MediaPipe reads frames through it
|
|
245
|
+
// even when it segments on the CPU) - offered only where all three are there.
|
|
246
|
+
const canBlur = typeof WebAssembly === 'object' && typeof HTMLCanvasElement.prototype.captureStream === 'function'
|
|
247
|
+
&& (() => {
|
|
248
|
+
try {
|
|
249
|
+
const gl = document.createElement('canvas').getContext('webgl2');
|
|
250
|
+
gl?.getExtension('WEBGL_lose_context')?.loseContext(); // only asked; give the context straight back
|
|
251
|
+
return Boolean(gl);
|
|
252
|
+
} catch { return false; }
|
|
253
|
+
})();
|
|
254
|
+
let blurWanted = false; // what the caller last asked for (a slow start may finish after they changed their mind)
|
|
238
255
|
const stage = M.observable('check'); // check | waiting | in | ended | away | error
|
|
239
256
|
const name = M.observable(cfg.name || '');
|
|
240
257
|
const micOn = M.observable(true);
|
|
@@ -274,6 +291,14 @@ function mount(el) {
|
|
|
274
291
|
const canSend = M.computed(() => /\S/.test(draft.value || ''));
|
|
275
292
|
const title = M.observable(cfg.title || 'Call');
|
|
276
293
|
|
|
294
|
+
// ---- Background blur (blur.js, loaded the first time it is turned on) ------------------
|
|
295
|
+
const hasCam = M.observable(false);
|
|
296
|
+
const blurOn = M.observable(false);
|
|
297
|
+
const blurBusy = M.observable(false);
|
|
298
|
+
const blurNote = M.observable('');
|
|
299
|
+
const blurOffered = M.computed(() => canBlur && hasCam.value);
|
|
300
|
+
const blurLabel = M.computed(() => (blurBusy.value ? 'Starting blur…' : blurOn.value ? 'Blur on' : 'Blur'));
|
|
301
|
+
|
|
277
302
|
// ---- Add people (the host, when the Tool allows it: join answers canInvite) ----------------
|
|
278
303
|
const canInvite = M.observable(false);
|
|
279
304
|
// What the Tool offers this host (join's inviteOptions): search the team (+ contacts when typed), contacts and
|
|
@@ -325,13 +350,69 @@ function mount(el) {
|
|
|
325
350
|
// peers is kept in step with tiles (a plain observable, not a computed over a computed).
|
|
326
351
|
const stopTiles = M.effect(() => { tiles.value = String(Math.min(6, peers.value.length)); });
|
|
327
352
|
|
|
353
|
+
let blur = null; // {track, stop, stream} while the background is blurred
|
|
354
|
+
const BLUR_KEY = 'dm-call:blur';
|
|
355
|
+
/** The picture this browser sends while not sharing a screen: the blurred one, or the camera's. */
|
|
356
|
+
const camVideo = () => blur?.track || local?.getVideoTracks()[0] || null;
|
|
357
|
+
|
|
328
358
|
const selfVideos = () => el.querySelectorAll('.dmc-self, .dmc-self-preview');
|
|
329
|
-
const
|
|
359
|
+
const selfStream = () => screen || blur?.stream || local;
|
|
360
|
+
const showSelf = () => { const s = selfStream(); selfVideos().forEach(v => { v.srcObject = s; }); };
|
|
361
|
+
/** Each connection's video sender: the camera's, or one added for a screen when there was no camera. */
|
|
362
|
+
const videoSender = (entry) => entry.pc.getSenders().find(s => s.track?.kind === 'video') || entry.videoSender || null;
|
|
363
|
+
/** Put the current camera picture (blurred or not) on every connection - not while a screen is shared. */
|
|
364
|
+
const sendCam = () => { if (!screen) pcs.forEach(entry => videoSender(entry)?.replaceTrack(camVideo())); };
|
|
365
|
+
|
|
366
|
+
async function startBlurring() {
|
|
367
|
+
const cam = local?.getVideoTracks()[0];
|
|
368
|
+
if (!cam || blur || blurBusy.value) return;
|
|
369
|
+
blurBusy.value = true;
|
|
370
|
+
blurNote.value = '';
|
|
371
|
+
try {
|
|
372
|
+
const mod = await import(new URL(`./blur.js${new URL(import.meta.url).search}`, import.meta.url).href);
|
|
373
|
+
const started = await mod.startBlur(cam);
|
|
374
|
+
if (!local || local.getVideoTracks()[0] !== cam || !blurWanted) { started.stop(); return; } // left, switched off or lost the camera meanwhile
|
|
375
|
+
blur = Object.assign(started, {stream: new MediaStream([started.track])});
|
|
376
|
+
blur.track.enabled = camOn.value;
|
|
377
|
+
blur.paused = Boolean(screen);
|
|
378
|
+
// It stops itself if this machine cannot keep it going: back to the plain camera, and say so.
|
|
379
|
+
blur.track.addEventListener('ended', () => {
|
|
380
|
+
if (blur !== started) return;
|
|
381
|
+
blur = null; blurOn.value = false; sendCam(); showSelf();
|
|
382
|
+
blurSay(started.reason === 'slow' ? 'Background blur turned off - this device could not keep up.'
|
|
383
|
+
: 'Background blur stopped working, so it is off. You can try turning it on again.');
|
|
384
|
+
});
|
|
385
|
+
blurOn.value = true;
|
|
386
|
+
sendCam();
|
|
387
|
+
showSelf();
|
|
388
|
+
} catch (err) {
|
|
389
|
+
console.warn('[call] blur:', err?.message || err);
|
|
390
|
+
blurOn.value = false;
|
|
391
|
+
blurSay('Background blur could not start in this browser.');
|
|
392
|
+
try { localStorage.removeItem(BLUR_KEY); } catch { /* storage blocked */ }
|
|
393
|
+
} finally { blurBusy.value = false; }
|
|
394
|
+
}
|
|
395
|
+
/** A blur notice: under the buttons before the call, and as a line in the chat (this browser only) once in it. */
|
|
396
|
+
let blurSaid = 0;
|
|
397
|
+
function blurSay(text) {
|
|
398
|
+
blurNote.value = text;
|
|
399
|
+
if (stage.value === 'in') addItem({id: `blur-${Date.now()}-${++blurSaid}`, kind: 'system', at: new Date().toISOString(), text});
|
|
400
|
+
}
|
|
401
|
+
function stopBlurring() {
|
|
402
|
+
const was = blur;
|
|
403
|
+
blur = null;
|
|
404
|
+
blurOn.value = false;
|
|
405
|
+
if (!was) return;
|
|
406
|
+
sendCam();
|
|
407
|
+
showSelf();
|
|
408
|
+
was.stop();
|
|
409
|
+
}
|
|
330
410
|
|
|
331
411
|
async function getDevices() {
|
|
332
412
|
try {
|
|
333
413
|
local = await navigator.mediaDevices.getUserMedia({video: {width: {ideal: 1280}, height: {ideal: 720}}, audio: true});
|
|
334
414
|
deviceNote.value = 'Your camera and microphone are working.';
|
|
415
|
+
hasCam.value = local.getVideoTracks().length > 0;
|
|
335
416
|
} catch {
|
|
336
417
|
try {
|
|
337
418
|
local = await navigator.mediaDevices.getUserMedia({audio: true});
|
|
@@ -345,10 +426,13 @@ function mount(el) {
|
|
|
345
426
|
}
|
|
346
427
|
}
|
|
347
428
|
showSelf();
|
|
429
|
+
try { blurWanted = localStorage.getItem(BLUR_KEY) === '1'; } catch { /* storage blocked */ }
|
|
430
|
+
if (blurWanted && canBlur && hasCam.value) startBlurring();
|
|
348
431
|
}
|
|
349
432
|
const setTracks = () => {
|
|
350
433
|
local?.getAudioTracks().forEach(t => { t.enabled = micOn.value; });
|
|
351
434
|
local?.getVideoTracks().forEach(t => { t.enabled = camOn.value; });
|
|
435
|
+
if (blur) blur.track.enabled = camOn.value;
|
|
352
436
|
};
|
|
353
437
|
|
|
354
438
|
// ---- Peer connections (perfect negotiation) ----------------------------------------
|
|
@@ -357,7 +441,8 @@ function mount(el) {
|
|
|
357
441
|
const pc = new RTCPeerConnection({iceServers});
|
|
358
442
|
const entry = {pc, polite: me.id > peer.id, makingOffer: false, ignore: false, stream: new MediaStream(), chain: Promise.resolve()};
|
|
359
443
|
pcs.set(peer.id, entry);
|
|
360
|
-
const
|
|
444
|
+
const cam = camVideo();
|
|
445
|
+
const out = [...(local?.getAudioTracks() || []), ...(screen ? screen.getVideoTracks() : cam ? [cam] : [])];
|
|
361
446
|
out.forEach(track => pc.addTrack(track, local));
|
|
362
447
|
pc.ontrack = (e) => { entry.stream.addTrack(e.track); attach(); if (e.track.kind === 'audio') addToMix(e.track); };
|
|
363
448
|
pc.onicecandidate = (e) => { if (e.candidate) post('signal', {to: peer.id, data: {candidate: e.candidate}}); };
|
|
@@ -606,6 +691,8 @@ function mount(el) {
|
|
|
606
691
|
clearInterval(clockTimer); clockTimer = null;
|
|
607
692
|
stage.value = kind;
|
|
608
693
|
stopRecording();
|
|
694
|
+
blurWanted = false;
|
|
695
|
+
stopBlurring();
|
|
609
696
|
for (const id of [...pcs.keys()]) dropPeer(id);
|
|
610
697
|
[local, screen].forEach(s => s?.getTracks().forEach(t => t.stop()));
|
|
611
698
|
me = null;
|
|
@@ -672,7 +759,7 @@ function mount(el) {
|
|
|
672
759
|
title, people, tiles, chatLabel, askName: cfg.askName !== false, canShare: Boolean(navigator.mediaDevices?.getDisplayMedia), canRecord,
|
|
673
760
|
stage, name, initial, micOn, camOn, sharing, recording, iRecord, recordingAllowed, host, busy, error, deviceNote, sideOpen, unread,
|
|
674
761
|
unreadText, draft, canSend, peers, waiting, waitingTitle, messages, newBelow, endTitle, endText, meCaption, countText, countTitle,
|
|
675
|
-
clock, chatSub, aloneText,
|
|
762
|
+
clock, chatSub, aloneText, blurOn, blurBusy, blurNote, blurOffered, blurLabel,
|
|
676
763
|
canInvite, inviteSearch, inviteTyped, inviteCopy, inviteOpen, query, found, picked, typedName, typedEmail, viaNotify, viaMessage, viaEmail, canMessage,
|
|
677
764
|
inviteBusy, inviteNote, inviteBad, sent, copiedLink, inviteSub, searchHint, foundTitle, emptyText, viaHint, canSendInvites, sendLabel,
|
|
678
765
|
|
|
@@ -749,6 +836,13 @@ function mount(el) {
|
|
|
749
836
|
},
|
|
750
837
|
|
|
751
838
|
toggleMic() { micOn.value = !micOn.value; setTracks(); },
|
|
839
|
+
toggleBlur() {
|
|
840
|
+
if (blurBusy.value) return;
|
|
841
|
+
blurWanted = !blurOn.value;
|
|
842
|
+
try { if (blurWanted) localStorage.setItem(BLUR_KEY, '1'); else localStorage.removeItem(BLUR_KEY); } catch { /* storage blocked */ }
|
|
843
|
+
blurNote.value = '';
|
|
844
|
+
if (blurWanted) startBlurring(); else stopBlurring();
|
|
845
|
+
},
|
|
752
846
|
toggleCam() {
|
|
753
847
|
if (!local?.getVideoTracks().length) { error.value = 'No camera is available.'; return; }
|
|
754
848
|
camOn.value = !camOn.value; setTracks();
|
|
@@ -826,15 +920,14 @@ function mount(el) {
|
|
|
826
920
|
},
|
|
827
921
|
|
|
828
922
|
async toggleShare() {
|
|
829
|
-
//
|
|
830
|
-
// (remembered, so sharing again reuses it rather than adding another).
|
|
831
|
-
const videoSender = (entry) => entry.pc.getSenders().find(s => s.track?.kind === 'video') || entry.videoSender || null;
|
|
923
|
+
// A screen goes out on the camera's sender, or one added for it when there was no camera
|
|
924
|
+
// (remembered as entry.videoSender, so sharing again reuses it rather than adding another).
|
|
832
925
|
if (sharing.value) {
|
|
833
926
|
screen?.getTracks().forEach(t => t.stop());
|
|
834
927
|
screen = null;
|
|
835
928
|
sharing.value = false;
|
|
836
|
-
|
|
837
|
-
|
|
929
|
+
if (blur) blur.paused = false;
|
|
930
|
+
sendCam();
|
|
838
931
|
showSelf();
|
|
839
932
|
return;
|
|
840
933
|
}
|
|
@@ -843,6 +936,7 @@ function mount(el) {
|
|
|
843
936
|
const track = screen.getVideoTracks()[0];
|
|
844
937
|
track.onended = () => { if (sharing.value) model.toggleShare(); };
|
|
845
938
|
sharing.value = true;
|
|
939
|
+
if (blur) blur.paused = true; // nobody sees it while the screen is shown
|
|
846
940
|
pcs.forEach(entry => {
|
|
847
941
|
const sender = videoSender(entry);
|
|
848
942
|
if (sender) sender.replaceTrack(track);
|
|
@@ -909,7 +1003,7 @@ function mount(el) {
|
|
|
909
1003
|
el.querySelector('.dmc-messages')?.addEventListener('scroll', () => { if (newBelow.value && nearBottom()) newBelow.value = false; }, {passive: true});
|
|
910
1004
|
window.addEventListener('pagehide', () => { if (me) post('leave', {}, {keepalive: true}); });
|
|
911
1005
|
getDevices().then(setTracks);
|
|
912
|
-
return () => { stopLayout?.(); stopAttach?.(); stopScan?.(); stopTiles?.(); clearInterval(clockTimer); handleBindings?.dispose?.(); };
|
|
1006
|
+
return () => { stopLayout?.(); stopAttach?.(); stopScan?.(); stopTiles?.(); clearInterval(clockTimer); blurWanted = false; stopBlurring(); handleBindings?.dispose?.(); };
|
|
913
1007
|
}
|
|
914
1008
|
|
|
915
1009
|
document.querySelectorAll('[data-dm-call]').forEach(mount);
|