From a41b23a5e29a0b5333ee5d9b1ab65fdff9d010af Mon Sep 17 00:00:00 2001 From: Andrey Date: Wed, 16 Sep 2026 12:03:27 +0300 Subject: [PATCH] =?UTF-8?q?docs:=20=D0=BE=D0=B1=D0=BB=D0=BE=D0=B6=D0=BA?= =?UTF-8?q?=D0=B0=20=D0=B8=20=D1=81=D1=81=D1=8B=D0=BB=D0=BA=D0=B8=20=D0=B2?= =?UTF-8?q?=20README,=20design/=20=D0=B2=D0=BD=D0=B5=20=D0=B3=D0=B8=D1=82?= =?UTF-8?q?=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Дизайн-хендоффы (design/) — рабочие материалы, а не часть продукта: сняты с отслеживания и добавлены в .gitignore. В обоих README перед оглавлением — обложка рабочего места (1024px, сжата из исходных 3840px) и строка ссылок: телеграм-канал, сайт, центр помощи. Co-Authored-By: Claude Opus 5 --- .github/assets/cover.jpg | Bin 0 -> 90006 bytes .gitignore | 3 + README.md | 14 + README.ru.md | 14 + .../# UI для онлайн звонка/.thumbnail | Bin 5622 -> 0 bytes .../design-system/_adherence.oxlintrc.json | 508 --- .../_ds/design-system/_ds_bundle.js | 3489 ----------------- .../_ds/design-system/_ds_manifest.json | 1 - .../_ds/design-system/styles.css | 4 - .../_ds/design-system/tokens/colors.css | 88 - .../_ds/design-system/tokens/effects.css | 40 - .../_ds/design-system/tokens/spacing.css | 36 - .../_ds/design-system/tokens/typography.css | 42 - .../# UI для онлайн звонка/image-slot.js | 1197 ------ .../# UI для онлайн звонка/support.js | 1768 --------- .../Аудиозвонок.dc.html | 352 -- .../Публичная страница звонка.dc.html | 135 - .../Экран звонка.dc.html | 410 -- .../baseline/Chatballs Baseline v2/README.md | 195 - .../Chatballs Baseline v2/_ds/styles.css | 4 - .../_ds/tokens/colors.css | 88 - .../_ds/tokens/effects.css | 40 - .../_ds/tokens/spacing.css | 36 - .../_ds/tokens/typography.css | 42 - .../Chatballs Baseline v2/assets/av-1.png | Bin 58566 -> 0 bytes .../Chatballs Baseline v2/assets/av-2.png | Bin 61778 -> 0 bytes .../Chatballs Baseline v2/assets/av-3.png | Bin 62414 -> 0 bytes .../Chatballs Baseline v2/assets/av-4.png | Bin 63090 -> 0 bytes .../assets/logo-mark.svg | 4 - .../baseline/Chatballs Baseline v2/support.js | 1911 --------- .../Агенты Baseline.dc.html | 316 -- .../Админ Baseline.dc.html | 276 -- .../База знаний Baseline.dc.html | 879 ----- .../Контакты Baseline.dc.html | 326 -- .../Настройки Baseline.dc.html | 369 -- .../Порталы Baseline.dc.html | 764 ---- .../Профиль Baseline.dc.html | 279 -- .../Сотрудники Baseline.dc.html | 433 -- .../Чат сотрудника Baseline.dc.html | 673 ---- .../Знания/Knowledge Library Baseline.dc.html | 667 ---- design/baseline/Знания/README.md | 38 - .../design-system/_adherence.oxlintrc.json | 508 --- .../Знания/_ds/design-system/_ds_bundle.js | 3489 ----------------- .../_ds/design-system/_ds_manifest.json | 1 - .../Знания/_ds/design-system/styles.css | 4 - .../_ds/design-system/tokens/colors.css | 88 - .../_ds/design-system/tokens/effects.css | 40 - .../_ds/design-system/tokens/spacing.css | 36 - .../_ds/design-system/tokens/typography.css | 42 - .../baseline/Знания/assets/chatballs-mark.svg | 4 - design/baseline/Знания/support.js | 1841 --------- design/baseline/Онбординг/README.md | 272 -- design/baseline/Онбординг/support.js | 1911 --------- .../Онбординг/Онбординг Chatballs.dc.html | 815 ---- .../baseline/Очередь и уведомления/README.md | 54 - .../baseline/Очередь и уведомления/support.js | 1911 --------- .../Очередь и уведомления Baseline.dc.html | 385 -- design/baseline/Сотрудники/.thumbnail | Bin 12946 -> 0 bytes .../design-system/_adherence.oxlintrc.json | 508 --- .../_ds/design-system/_ds_bundle.js | 3489 ----------------- .../_ds/design-system/_ds_manifest.json | 1 - .../Сотрудники/_ds/design-system/styles.css | 4 - .../_ds/design-system/tokens/colors.css | 88 - .../_ds/design-system/tokens/effects.css | 40 - .../_ds/design-system/tokens/spacing.css | 36 - .../_ds/design-system/tokens/typography.css | 42 - design/baseline/Сотрудники/favicon.svg | 4 - design/baseline/Сотрудники/support.js | 1768 --------- .../Сотрудники (новая модель).dc.html | 862 ---- 69 files changed, 31 insertions(+), 33653 deletions(-) create mode 100644 .github/assets/cover.jpg delete mode 100644 design/baseline/# UI для онлайн звонка/.thumbnail delete mode 100644 design/baseline/# UI для онлайн звонка/_ds/design-system/_adherence.oxlintrc.json delete mode 100644 design/baseline/# UI для онлайн звонка/_ds/design-system/_ds_bundle.js delete mode 100644 design/baseline/# UI для онлайн звонка/_ds/design-system/_ds_manifest.json delete mode 100644 design/baseline/# UI для онлайн звонка/_ds/design-system/styles.css delete mode 100644 design/baseline/# UI для онлайн звонка/_ds/design-system/tokens/colors.css delete mode 100644 design/baseline/# UI для онлайн звонка/_ds/design-system/tokens/effects.css delete mode 100644 design/baseline/# UI для онлайн звонка/_ds/design-system/tokens/spacing.css delete mode 100644 design/baseline/# UI для онлайн звонка/_ds/design-system/tokens/typography.css delete mode 100644 design/baseline/# UI для онлайн звонка/image-slot.js delete mode 100644 design/baseline/# UI для онлайн звонка/support.js delete mode 100644 design/baseline/# UI для онлайн звонка/Аудиозвонок.dc.html delete mode 100644 design/baseline/# UI для онлайн звонка/Публичная страница звонка.dc.html delete mode 100644 design/baseline/# UI для онлайн звонка/Экран звонка.dc.html delete mode 100644 design/baseline/Chatballs Baseline v2/README.md delete mode 100644 design/baseline/Chatballs Baseline v2/_ds/styles.css delete mode 100644 design/baseline/Chatballs Baseline v2/_ds/tokens/colors.css delete mode 100644 design/baseline/Chatballs Baseline v2/_ds/tokens/effects.css delete mode 100644 design/baseline/Chatballs Baseline v2/_ds/tokens/spacing.css delete mode 100644 design/baseline/Chatballs Baseline v2/_ds/tokens/typography.css delete mode 100644 design/baseline/Chatballs Baseline v2/assets/av-1.png delete mode 100644 design/baseline/Chatballs Baseline v2/assets/av-2.png delete mode 100644 design/baseline/Chatballs Baseline v2/assets/av-3.png delete mode 100644 design/baseline/Chatballs Baseline v2/assets/av-4.png delete mode 100644 design/baseline/Chatballs Baseline v2/assets/logo-mark.svg delete mode 100644 design/baseline/Chatballs Baseline v2/support.js delete mode 100644 design/baseline/Chatballs Baseline v2/Агенты Baseline.dc.html delete mode 100644 design/baseline/Chatballs Baseline v2/Админ Baseline.dc.html delete mode 100644 design/baseline/Chatballs Baseline v2/База знаний Baseline.dc.html delete mode 100644 design/baseline/Chatballs Baseline v2/Контакты Baseline.dc.html delete mode 100644 design/baseline/Chatballs Baseline v2/Настройки Baseline.dc.html delete mode 100644 design/baseline/Chatballs Baseline v2/Порталы Baseline.dc.html delete mode 100644 design/baseline/Chatballs Baseline v2/Профиль Baseline.dc.html delete mode 100644 design/baseline/Chatballs Baseline v2/Сотрудники Baseline.dc.html delete mode 100644 design/baseline/Chatballs Baseline v2/Чат сотрудника Baseline.dc.html delete mode 100644 design/baseline/Знания/Knowledge Library Baseline.dc.html delete mode 100644 design/baseline/Знания/README.md delete mode 100644 design/baseline/Знания/_ds/design-system/_adherence.oxlintrc.json delete mode 100644 design/baseline/Знания/_ds/design-system/_ds_bundle.js delete mode 100644 design/baseline/Знания/_ds/design-system/_ds_manifest.json delete mode 100644 design/baseline/Знания/_ds/design-system/styles.css delete mode 100644 design/baseline/Знания/_ds/design-system/tokens/colors.css delete mode 100644 design/baseline/Знания/_ds/design-system/tokens/effects.css delete mode 100644 design/baseline/Знания/_ds/design-system/tokens/spacing.css delete mode 100644 design/baseline/Знания/_ds/design-system/tokens/typography.css delete mode 100644 design/baseline/Знания/assets/chatballs-mark.svg delete mode 100644 design/baseline/Знания/support.js delete mode 100644 design/baseline/Онбординг/README.md delete mode 100644 design/baseline/Онбординг/support.js delete mode 100644 design/baseline/Онбординг/Онбординг Chatballs.dc.html delete mode 100644 design/baseline/Очередь и уведомления/README.md delete mode 100644 design/baseline/Очередь и уведомления/support.js delete mode 100644 design/baseline/Очередь и уведомления/Очередь и уведомления Baseline.dc.html delete mode 100644 design/baseline/Сотрудники/.thumbnail delete mode 100644 design/baseline/Сотрудники/_ds/design-system/_adherence.oxlintrc.json delete mode 100644 design/baseline/Сотрудники/_ds/design-system/_ds_bundle.js delete mode 100644 design/baseline/Сотрудники/_ds/design-system/_ds_manifest.json delete mode 100644 design/baseline/Сотрудники/_ds/design-system/styles.css delete mode 100644 design/baseline/Сотрудники/_ds/design-system/tokens/colors.css delete mode 100644 design/baseline/Сотрудники/_ds/design-system/tokens/effects.css delete mode 100644 design/baseline/Сотрудники/_ds/design-system/tokens/spacing.css delete mode 100644 design/baseline/Сотрудники/_ds/design-system/tokens/typography.css delete mode 100644 design/baseline/Сотрудники/favicon.svg delete mode 100644 design/baseline/Сотрудники/support.js delete mode 100644 design/baseline/Сотрудники/Сотрудники (новая модель).dc.html diff --git a/.github/assets/cover.jpg b/.github/assets/cover.jpg new file mode 100644 index 0000000000000000000000000000000000000000..fcd32a53e3193b991ee36afd902bfcbef1f13129 GIT binary patch literal 90006 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 literal 0 HcmV?d00001 diff --git a/.gitignore b/.gitignore index b6fa06e..ce26aa3 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,6 @@ Chatballs.zip # Служебный каталог агента: локальные настройки запуска, а не часть продукта. .claude/ + +# Дизайн-хендоффы: рабочие материалы, а не часть продукта. +design/ diff --git a/README.md b/README.md index be8648c..17160fc 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,20 @@ --- +

+ Chatballs workspace: dialogs, conversation and contact card +

+ +

+ Telegram channel +  ·  + Website +  ·  + Help center +

+ +--- + ## Table of contents - [Overview](#overview) diff --git a/README.ru.md b/README.ru.md index 6d639b9..32ac906 100644 --- a/README.ru.md +++ b/README.ru.md @@ -23,6 +23,20 @@ --- +

+ Рабочее место Chatballs: список диалогов, переписка и карточка контакта +

+ +

+ Телеграм-канал +  ·  + Сайт +  ·  + Центр помощи +

+ +--- + ## Содержание - [Что это](#что-это) diff --git a/design/baseline/# UI для онлайн звонка/.thumbnail b/design/baseline/# UI для онлайн звонка/.thumbnail deleted file mode 100644 index 0914090f7fc220091779810de670f13356f52f60..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5622 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 JSXAttribute > JSXIdentifier[name!=/^(?:actor|initials|color|key|ref|className|style|children)$/]", - "message": " doesn't accept that prop. Declared props: actor, initials, color." - }, - { - "selector": "JSXOpeningElement[name.name='ActorBadge'] > JSXAttribute[name.name='actor'] > Literal[value!=/^(?:ai|human)$/]", - "message": " actor must be one of 'ai' | 'human'." - }, - { - "selector": "JSXOpeningElement[name.name='AttentionStatus'] > JSXAttribute > JSXIdentifier[name!=/^(?:level|label|key|ref|className|style|children)$/]", - "message": " doesn't accept that prop. Declared props: level, label." - }, - { - "selector": "JSXOpeningElement[name.name='AttentionStatus'] > JSXAttribute[name.name='level'] > Literal[value!=/^(?:ok|attention|critical)$/]", - "message": " level must be one of 'ok' | 'attention' | 'critical'." - }, - { - "selector": "JSXOpeningElement[name.name='Avatar'] > JSXAttribute > JSXIdentifier[name!=/^(?:initials|color|size|key|ref|className|style|children)$/]", - "message": " doesn't accept that prop. Declared props: initials, color, size." - }, - { - "selector": "JSXOpeningElement[name.name='Button'] > JSXAttribute > JSXIdentifier[name!=/^(?:children|variant|size|disabled|onClick|type|key|ref|className|style|children)$/]", - "message": "' + - ' ' + - ''; - this._frame = root.querySelector('.frame'); - this._ring = root.querySelector('.ring'); - this._img = root.querySelector('.frame img'); - this._empty = root.querySelector('.empty'); - this._cap = root.querySelector('.cap'); - this._sub = root.querySelector('.sub'); - this._spill = root.querySelector('.spill'); - this._ctl = root.querySelector('.ctl'); - this._credit = root.querySelector('.credit'); - this._attrError = root.querySelector('.attr-error'); - // Credit clicks open the link, not browse/reframe. - this._credit.addEventListener('click', (e) => e.stopPropagation()); - this._credit.addEventListener('dblclick', (e) => e.stopPropagation()); - this._ghost = root.querySelector('.ghost'); - this._err = null; - this._input = root.querySelector('input'); - this._depth = 0; - this._gen = 0; - // Encode-in-flight marker (the owning _ingest generation): while set, - // the same-src "nothing in flight" clear in _render must not fire — - // the stored value still points at the OLD image until the encode - // lands, so that clear would unmask the stale image mid-replace. - this._swapGen = 0; - // Render-owned swap in flight: set when _render assigns a new src, - // cleared only by the img's own load/error (or the empty branch). - // img.complete CANNOT stand in for this — setting src only QUEUES - // the current-request swap (a microtask), so synchronously after an - // assignment, complete still reports the OLD settled request. The - // pick path does exactly that: the host sets src, credit, and - // credit-href back-to-back in one task, and renders #2/#3 would - // read the stale complete === true and drop the mask one render - // after it was set. - this._loadPending = false; - // See _render's empty branch: a transient attribution-error wipe of a - // showing image must make the follow-up render a replacement (spinner), - // not a first fill (blank frame). - this._hidShowing = false; - this._view = { s: 1, x: 0, y: 0 }; - this._subFn = () => this._render(); - // Shadow-DOM listeners live with the shadow DOM — bound once here so - // disconnect/reconnect (e.g. React remount) doesn't stack handlers. - this._empty.addEventListener('click', () => this._input.click()); - root.addEventListener('click', (e) => { - const act = e.target && e.target.getAttribute && e.target.getAttribute('data-act'); - if (!act) return; - // The hidden controls are opacity-0 but still tabbable — without - // this gate a keyboard user could drive them on a read-only share - // link (mirrors the dblclick handler's editable gate). - if (!this.hasAttribute('data-editable')) return; - if (act === 'replace') { - this._exitReframe(true); - // Host-owned picker (Unsplash modal; it also offers local import). - this.dispatchEvent(new CustomEvent('image-slot:pick', { - bubbles: true, composed: true, detail: { id: this.id || null } - })); - } - if (act === 'edit') { - if (!this._reframes()) return; - if (this.hasAttribute('data-reframe')) this._exitReframe(true); - else this._enterReframe(); - } - }); - this._input.addEventListener('change', () => { - const f = this._input.files && this._input.files[0]; - if (f) this._ingest(f); - this._input.value = ''; - }); - // naturalWidth/Height aren't known until load — re-apply so the cover - // baseline is computed from real dimensions, not the 100%×100% fallback. - // load/error also release the replacement-in-flight mask (via the - // single discipline in _releaseMask): the swap is only revealed once - // the new image can actually paint (on error the frame shows its - // background, same as a fresh slot with a broken src). - this._img.addEventListener('load', () => { - this._loadPending = false; - this._releaseMask(true); - this._applyView(); - }); - this._img.addEventListener('error', () => { - this._loadPending = false; - this._releaseMask(true); - }); - // Gated only on editable — any filled slot can be repositioned/scaled, - // regardless of fit. Share links (no writeFile) stay static. - this.addEventListener('dblclick', (e) => { - if (!this.hasAttribute('data-editable') || !this._reframes()) return; - e.preventDefault(); - if (this.hasAttribute('data-reframe')) this._exitReframe(true); - else this._enterReframe(); - }); - // Pan + resize both originate on the spill layer. A handle pointerdown - // drives an aspect-locked resize anchored at the opposite corner; any - // other pointerdown on the spill pans. Offsets are frame-% so a - // reframed slot survives responsive resize / PPTX export. - this._spill.addEventListener('pointerdown', (e) => { - if (e.button !== 0 || !this.hasAttribute('data-reframe')) return; - e.preventDefault(); - e.stopPropagation(); - this._spill.setPointerCapture(e.pointerId); - const rect = this.getBoundingClientRect(); - const fw = rect.width || 1, fh = rect.height || 1; - const corner = e.target.getAttribute && e.target.getAttribute('data-c'); - let move; - if (corner) { - // Resize about the OPPOSITE corner. Viewport-px throughout (rect - // fw/fh, not clientWidth) so the math survives a transform:scale() - // ancestor — deck_stage renders slides scaled-to-fit. - const iw = this._img.naturalWidth || 1, ih = this._img.naturalHeight || 1; - const contain = (this.getAttribute('fit') || 'cover').toLowerCase() === 'contain'; - const base = contain ? Math.min(fw / iw, fh / ih) : Math.max(fw / iw, fh / ih); - const sx = corner.includes('e') ? 1 : -1; - const sy = corner.includes('s') ? 1 : -1; - const s0 = this._view.s; - const w0 = iw * base * s0, h0 = ih * base * s0; - const cx0 = (50 + this._view.x) / 100 * fw; - const cy0 = (50 + this._view.y) / 100 * fh; - const ox = cx0 - sx * w0 / 2, oy = cy0 - sy * h0 / 2; - const diag0 = Math.hypot(w0, h0); - const ux = sx * w0 / diag0, uy = sy * h0 / diag0; - move = (ev) => { - const proj = (ev.clientX - rect.left - ox) * ux + - (ev.clientY - rect.top - oy) * uy; - const s = clampS(s0 * proj / diag0); - const d = diag0 * s / s0; - this._view.s = s; - this._view.x = (ox + ux * d / 2) / fw * 100 - 50; - this._view.y = (oy + uy * d / 2) / fh * 100 - 50; - this._clampView(); - this._applyView(); - }; - } else { - this.setAttribute('data-panning', ''); - const start = { px: e.clientX, py: e.clientY, x: this._view.x, y: this._view.y }; - move = (ev) => { - this._view.x = start.x + (ev.clientX - start.px) / fw * 100; - this._view.y = start.y + (ev.clientY - start.py) / fh * 100; - this._clampView(); - this._applyView(); - }; - } - const up = () => { - try { this._spill.releasePointerCapture(e.pointerId); } catch {} - this._spill.removeEventListener('pointermove', move); - this._spill.removeEventListener('pointerup', up); - this._spill.removeEventListener('pointercancel', up); - this.removeAttribute('data-panning'); - this._dragUp = null; - }; - // Stashed so _exitReframe (Escape / outside-click mid-drag) can - // tear the capture + listeners down synchronously. - this._dragUp = up; - this._spill.addEventListener('pointermove', move); - this._spill.addEventListener('pointerup', up); - this._spill.addEventListener('pointercancel', up); - }); - // Wheel zoom stays available inside reframe mode as a trackpad nicety — - // zooms toward the cursor (offset' = cursor·(1-k) + offset·k). - this.addEventListener('wheel', (e) => { - if (!this.hasAttribute('data-reframe')) return; - e.preventDefault(); - const r = this.getBoundingClientRect(); - const cx = (e.clientX - r.left) / r.width * 100 - 50; - const cy = (e.clientY - r.top) / r.height * 100 - 50; - const prev = this._view.s; - const next = clampS(prev * Math.pow(1.0015, -e.deltaY)); - if (next === prev) return; - const k = next / prev; - this._view.s = next; - this._view.x = cx * (1 - k) + this._view.x * k; - this._view.y = cy * (1 - k) + this._view.y * k; - this._clampView(); - this._applyView(); - }, { passive: false }); - } - - connectedCallback() { - // Warn once per page — an id-less slot works for the session but - // cannot persist, and two id-less slots would share nothing. - if (!this.id && !ImageSlot._warned) { - ImageSlot._warned = true; - console.warn(' without an id will not persist its dropped image.'); - } - this.addEventListener('dragenter', this); - this.addEventListener('dragover', this); - this.addEventListener('dragleave', this); - this.addEventListener('drop', this); - subs.add(this._subFn); - // The host may inject window.omelette.writeFile AFTER the first render; - // re-render on hover so the editable-gated controls reliably appear. - this.addEventListener('pointerenter', this._subFn); - // width%/height% in _applyView encode the frame aspect at call time — - // a host resize (responsive grid, pane divider) would stretch the - // image until the next _render. Re-render on size change: _render() - // re-seeds _view from stored before clamp/apply, so a shrink→grow - // cycle round-trips instead of ratcheting x/y toward the narrower - // frame's clamp range. - this._ro = new ResizeObserver(() => this._render()); - this._ro.observe(this); - load(); - this._render(); - } - - disconnectedCallback() { - subs.delete(this._subFn); - this.removeEventListener('pointerenter', this._subFn); - this.removeEventListener('dragenter', this); - this.removeEventListener('dragover', this); - this.removeEventListener('dragleave', this); - this.removeEventListener('drop', this); - if (this._ro) { this._ro.disconnect(); this._ro = null; } - // commit=false: a disconnect is not a user intent — committing here - // would persist whatever half-finished drag a React remount or DOM - // splice happened to interrupt. Deliberate exits commit on their own - // paths (Escape/click-out/toggle), and unloads commit via pagehide. - this._exitReframe(false); - } - - _enterReframe() { - if (this.hasAttribute('data-reframe')) return; - this.setAttribute('data-reframe', ''); - this._signalReframe(true); - // Best-effort commit when the document unloads mid-reframe (a host - // navigation racing the enter signal, a manual reload, tab close): - // the sidecar write rides the host bridge, which outlives this - // document, so the crop survives even though the mode dies with the - // DOM. Held on the instance so _exitReframe detaches exactly what - // was attached. - this._pagehide = () => { this._exitReframe(true); flushNow(); }; - window.addEventListener('pagehide', this._pagehide); - // Promote spill to the top layer, then keep it pinned over the frame: - // scroll/resize cover the common cases, and a per-frame rect check - // catches layout shifts that fire neither (an image above finishing - // load, streamed DOM pushing the slot down, an ancestor transform - // change) so the overlay can't detach from the frame. - try { this._spill.showPopover(); } catch {} - // After the spill, so the controls stack above it in the top layer. - try { this._ctl.showPopover(); } catch {} - this._reposition = () => { if (this.hasAttribute('data-reframe')) this._applyView(); }; - window.addEventListener('scroll', this._reposition, true); - window.addEventListener('resize', this._reposition); - this._lastRect = ''; - this._watch = () => { - if (!this.hasAttribute('data-reframe')) return; - const r = this.getBoundingClientRect(); - const key = r.left + ',' + r.top + ',' + r.width + ',' + r.height; - if (key !== this._lastRect) { this._lastRect = key; this._applyView(); } - this._watchId = requestAnimationFrame(this._watch); - }; - this._watchId = requestAnimationFrame(this._watch); - this._applyView(); - // Close on click outside (the spill handler stopPropagation()s so - // in-image drags don't reach this) and on Escape. Listeners are held - // on the instance so _exitReframe / disconnectedCallback can detach - // exactly what was attached. - this._outside = (e) => { - if (e.composedPath && e.composedPath().includes(this)) return; - this._exitReframe(true); - }; - this._esc = (e) => { if (e.key === 'Escape') this._exitReframe(true); }; - document.addEventListener('pointerdown', this._outside, true); - document.addEventListener('keydown', this._esc, true); - } - - _exitReframe(commit) { - if (!this.hasAttribute('data-reframe')) return; - if (this._dragUp) this._dragUp(); - this.removeAttribute('data-reframe'); - this.removeAttribute('data-panning'); - if (this._outside) document.removeEventListener('pointerdown', this._outside, true); - if (this._esc) document.removeEventListener('keydown', this._esc, true); - this._outside = this._esc = null; - if (this._reposition) { - window.removeEventListener('scroll', this._reposition, true); - window.removeEventListener('resize', this._reposition); - this._reposition = null; - } - if (this._watchId) { cancelAnimationFrame(this._watchId); this._watchId = 0; } - if (this._pagehide) { - window.removeEventListener('pagehide', this._pagehide); - this._pagehide = null; - } - try { this._spill.hidePopover(); } catch {} - try { this._ctl.hidePopover(); } catch {} - this._ctl.style.left = ''; this._ctl.style.top = ''; - if (commit) this._commitView(); - this._signalReframe(false); - } - - // Reframe state lives only in this DOM until commit, invisible to the - // host's dirty signals — announce enter/exit so the host can hold - // auto-reloads for exactly the gesture (the guest bundle forwards - // image-slot:reframe to the host as imageSlotReframe). Dispatched on - // the element (composed, so it escapes shadow roots) while connected; - // a disconnected exit (disconnectedCallback) falls back to document so - // the host still hears it. - _signalReframe(active) { - const target = this.isConnected ? this : document; - target.dispatchEvent(new CustomEvent('image-slot:reframe', { - bubbles: true, composed: true, - detail: { active: active, id: this.id || null } - })); - } - - // Public: host's "Import from computer" calls this to run local browse. - openFilePicker() { this._exitReframe(true); this._input.click(); } - - attributeChangedCallback() { if (this.shadowRoot) this._render(); } - - // handleEvent — one listener object for all four drag events keeps the - // add/remove symmetric and the depth counter correct. - handleEvent(e) { - if (e.type === 'dragenter' || e.type === 'dragover') { - // Without preventDefault the browser never fires 'drop'. - e.preventDefault(); - e.stopPropagation(); - if (e.dataTransfer) e.dataTransfer.dropEffect = 'copy'; - if (e.type === 'dragenter') this._depth++; - this.setAttribute('data-over', ''); - } else if (e.type === 'dragleave') { - // dragenter/leave fire for every descendant crossing — count depth - // so hovering the icon inside the empty state doesn't flicker. - if (--this._depth <= 0) { this._depth = 0; this.removeAttribute('data-over'); } - } else if (e.type === 'drop') { - e.preventDefault(); - e.stopPropagation(); - this._depth = 0; - this.removeAttribute('data-over'); - const f = e.dataTransfer && e.dataTransfer.files && e.dataTransfer.files[0]; - if (f) this._ingest(f); - } - } - - async _ingest(file) { - this._setError(null); - if (!file || ACCEPT.indexOf(file.type) < 0) { - this._setError('Drop a PNG, JPEG, WebP, or AVIF image.'); - return; - } - // toDataUrl can take hundreds of ms on a large photo. A Clear or a - // newer drop during that window would be clobbered when this await - // resumes — bump + capture a generation so stale encodes bail. - const gen = ++this._gen; - // Replacing a shown image: surface the swap through the encode too, - // not just the decode — otherwise the old photo sits there with no - // feedback while the canvas re-encode runs. An empty slot keeps its - // placeholder (no spinner) until the encode lands, as before. - // _swapGen guards the mask against re-renders DURING the encode - // (pointerenter, ResizeObserver, another slot's store write): the - // stored value still resolves to the old image there, so _render's - // same-src clear would otherwise unmask it mid-replace. - if (this.hasAttribute('data-filled')) { - this.setAttribute('data-swapping', ''); - this._swapGen = gen; - } - try { - const w = this.clientWidth || this.offsetWidth || MAX_DIM; - const url = await toDataUrl(file, w); - if (gen !== this._gen) return; - // Only exit reframe once the new image is in hand — a rejected type - // or decode failure leaves the in-progress crop untouched. - this._exitReframe(false); - // Clear BEFORE setSlot: its synchronous re-render must see no - // pending encode, so a byte-identical re-upload (same data URL, no - // load event coming) still clears the mask via the complete branch. - this._swapGen = 0; - const val = { u: url, s: 1, x: 0, y: 0 }; - setSlot(this.id || '', val); - // Keep a session-local copy for id-less slots so the drop still - // shows, even though it cannot persist. - if (!this.id) { this._local = val; this._render(); } - } catch (err) { - if (gen !== this._gen) return; - this._swapGen = 0; - // Reveal the kept old image — unless another replacement (a - // remote pick's src swap) is still in flight, in which case the - // mask stays until THAT image settles (its load/error releases). - this._releaseMask(); - this._setError('Could not read that image.'); - console.warn(' ingest failed:', err); - } - } - - _setError(msg) { - if (this._err) { this._err.remove(); this._err = null; } - if (!msg) return; - const d = document.createElement('div'); - d.className = 'err'; d.textContent = msg; - this.shadowRoot.appendChild(d); - this._err = d; - setTimeout(() => { if (this._err === d) { d.remove(); this._err = null; } }, 3000); - } - - // Reframing (pan/resize) is available on any filled slot — the user can - // always reposition/scale. `fit` only sets the initial baseline (see - // _geom): contain starts fully-visible, cover starts frame-filling. - _reframes() { - return this.hasAttribute('data-filled'); - } - - // The single release discipline for the replacement-in-flight mask - // (data-swapping). The mask comes off only when BOTH hold: - // - no encode is pending (_swapGen) — mid-encode the stored value - // still resolves to the old image, so any reveal paints it; - // - the frame img has settled on its current src — an unsettled src - // means some replacement is still in flight (e.g. a remote pick), - // whoever started it, and revealing would paint the previous - // frame. The load/error listeners pass settled=true (the event IS - // the settlement signal, per spec complete is true by then); - // other callers rely on the complete flag (covers loaded AND - // failed). - // Every release path funnels through here EXCEPT _render's empty - // branch (the img is being cleared — nothing will ever settle). - _releaseMask(settled) { - if ( - !this._swapGen && - !this._loadPending && - (settled || this._img.complete) - ) { - this.removeAttribute('data-swapping'); - } - } - - // Baseline geometry, shared by clamp/apply/resize. `base` is the scale at - // view-scale s=1: cover = fill the frame (overflow on the looser axis), - // contain = fit fully inside (letterboxed). Zooming a contain image past - // s where it overflows naturally becomes a crop. Null until the img has - // loaded (naturalWidth is 0 before that) or when the slot has no layout - // box — ResizeObserver fires with a 0×0 rect under display:none, and - // clamping against a degenerate 1×1 frame would silently pull the stored - // pan toward zero. - _geom() { - const iw = this._img.naturalWidth, ih = this._img.naturalHeight; - const fw = this.clientWidth, fh = this.clientHeight; - if (!iw || !ih || !fw || !fh) return null; - const contain = (this.getAttribute('fit') || 'cover').toLowerCase() === 'contain'; - const base = contain - ? Math.min(fw / iw, fh / ih) - : Math.max(fw / iw, fh / ih); - return { iw, ih, fw, fh, base }; - } - - _clampView() { - // Pan range on each axis is half the overflow past the frame edge. - const g = this._geom(); - if (!g) return; - const mx = Math.max(0, (g.iw * g.base * this._view.s / g.fw - 1) * 50); - const my = Math.max(0, (g.ih * g.base * this._view.s / g.fh - 1) * 50); - this._view.x = Math.max(-mx, Math.min(mx, this._view.x)); - this._view.y = Math.max(-my, Math.min(my, this._view.y)); - } - - _applyView() { - const g = this._geom(); - // Top-layer controls: pin to the frame's top-right in viewport px - // (the same 8px inset as the in-frame layout; unscaled — top-layer UI - // reads as chrome, not page content). BEFORE the geometry branch: - // placement needs only the frame rect, and a not-yet-loaded or broken - // src must not leave the promoted strip floating unpositioned. Gated - // on the popover actually being open: without the Popover API, - // showPopover() threw (swallowed in _enterReframe), .ctl stays in - // its in-frame absolute layout, and viewport-px coordinates would - // shove it off-frame — and matches(':popover-open') itself throws - // there (unknown pseudo-class), hence the try/catch. - if (this.hasAttribute('data-reframe')) { - let onTop = false; - try { onTop = this._ctl.matches(':popover-open'); } catch {} - if (onTop) { - const r = this.getBoundingClientRect(); - this._ctl.style.left = (r.right - 8) + 'px'; - this._ctl.style.top = (r.top + 8) + 'px'; - } - } - if (!g) { - // Dimensions not known yet (before img load) — centered fit so there - // is no flash of an unpositioned image before the geometry lands. - const contain = (this.getAttribute('fit') || 'cover').toLowerCase() === 'contain'; - this._img.style.width = '100%'; - this._img.style.height = '100%'; - this._img.style.left = '50%'; - this._img.style.top = '50%'; - this._img.style.objectFit = contain ? 'contain' : 'cover'; - return; - } - // Baseline (cover-fill or contain-fit) × view scale. Width/height and - // left/top are all frame-% — depends only on the frame aspect ratio, so - // a responsive resize keeps the same crop. The spill layer mirrors the - // same box so its corners = image corners. - const k = g.base * this._view.s; - const w = (g.iw * k / g.fw * 100) + '%'; - const h = (g.ih * k / g.fh * 100) + '%'; - const l = (50 + this._view.x) + '%'; - const t = (50 + this._view.y) + '%'; - this._img.style.width = w; this._img.style.height = h; - this._img.style.left = l; this._img.style.top = t; - this._img.style.objectFit = ''; - if (this.hasAttribute('data-reframe')) { - // Top-layer spill: position in viewport px over the frame. The top - // layer escapes ancestor transforms entirely, so EVERY term must be - // in viewport units: getBoundingClientRect gives the frame's scaled - // origin AND size, and the rect/layout ratio rescales the ghost — - // sizing from layout px alone renders it 1/scale too large under a - // scaled deck slide. Inner ghost + handles stay box-relative. - const r = this.getBoundingClientRect(); - const sx = g.fw ? r.width / g.fw : 1; - const sy = g.fh ? r.height / g.fh : 1; - this._spill.style.width = (g.iw * k * sx) + 'px'; - this._spill.style.height = (g.ih * k * sy) + 'px'; - this._spill.style.left = (r.left + (50 + this._view.x) / 100 * r.width) + 'px'; - this._spill.style.top = (r.top + (50 + this._view.y) / 100 * r.height) + 'px'; - } - } - - _commitView() { - const v = { s: this._view.s, x: this._view.x, y: this._view.y }; - if (this._userUrl) v.u = this._userUrl; - // Framing-only (no u) persists too so an author-src slot remembers its - // crop; clearing the sidecar still falls through to src=. - if (this.id) setSlot(this.id, v); - else { this._local = v; } - } - - _render() { - // Shape / mask. Presets use border-radius so the dashed ring can - // follow the rounded outline; clip-path is only applied for an - // explicit `mask` (the ring is hidden there since a rectangle - // dashed border chopped by an arbitrary polygon looks broken). - const mask = this.getAttribute('mask'); - const shape = (this.getAttribute('shape') || 'rounded').toLowerCase(); - let radius = ''; - if (shape === 'circle') radius = '50%'; - else if (shape === 'pill') radius = '9999px'; - else if (shape === 'rounded') { - const n = parseFloat(this.getAttribute('radius')); - radius = (Number.isFinite(n) ? n : 12) + 'px'; - } - this._frame.style.borderRadius = mask ? '' : radius; - this._frame.style.clipPath = mask || ''; - this._ring.style.borderRadius = mask ? '' : radius; - this._ring.style.display = mask ? 'none' : ''; - - // Controls and reframe entry gate on this so share links stay read-only. - const editable = !!(window.omelette && window.omelette.writeFile); - this.toggleAttribute('data-editable', editable); - this._sub.style.display = editable ? '' : 'none'; - - // Content. The sidecar is also writable by the agent's write_file - // tool, so its value isn't guaranteed canvas-originated — only accept - // data:image/ URLs from it. The `src` attribute is author-controlled - // (Claude wrote it into the HTML) so it passes through unchanged. - let stored = this.id ? getSlot(this.id) : this._local; - if (stored && stored.u && !/^data:image\//i.test(stored.u)) stored = null; - const srcAttr = this.getAttribute('src') || ''; - this._userUrl = (stored && stored.u) || null; - const url = this._userUrl || srcAttr; - // Don't clobber an in-flight reframe with a store-triggered re-render. - if (!this.hasAttribute('data-reframe')) { - this._view = { - s: stored && Number.isFinite(stored.s) ? clampS(stored.s) : 1, - x: stored && Number.isFinite(stored.x) ? stored.x : 0, - y: stored && Number.isFinite(stored.y) ? stored.y : 0, - }; - } - this._cap.textContent = this.getAttribute('placeholder') || 'Drop an image'; - // Toggle via style.display — the [hidden] attribute alone loses to - // the display:flex / display:block rules in the stylesheet above. - // An Unsplash src with no credit attribute must NOT render — showing - // the photo uncredited is the Unsplash-terms violation itself. The - // error tile replaces the photo until the credit is written. A - // user-dropped image is the user's own content and always renders. - // Trimmed: credit is agent/user-editable content, and a whitespace- - // only value must count as missing — otherwise it would suppress the - // error tile AND render an empty credit box (no text, no links), - // exactly the unattributed state this gate exists to prevent. - const credit = (this.getAttribute('credit') || '').trim(); - const attrError = !!( - !credit && !this._userUrl && srcAttr && isUnsplashHost(srcAttr) - ); - this.toggleAttribute('data-attribution-error', attrError); - if (url && !attrError) { - const prev = this._img.getAttribute('src'); - if (prev !== url) { - // Replacing an already-shown image: mark the swap BEFORE setting - // src so the stale frame is never revealed (see the data-swapping - // stylesheet rules). First fill (prev empty) keeps the existing - // placeholder-until-load behavior — no spinner. _hidShowing - // covers the pick path's transient attribution-error wipe: prev - // is gone, but an image WAS showing, so this is a replacement. - if (prev || this._hidShowing) this.setAttribute('data-swapping', ''); - // Mark the swap BEFORE assigning src: complete keeps reporting - // the old settled request until the browser's - // update-the-image-data microtask runs, so same-task re-renders - // (the pick path's credit/credit-href setAttributes) need this - // flag, not complete, to know a load is in flight. - this._loadPending = true; - this._img.src = url; - this._ghost.src = url; - } else { - // Same-src re-render — release if settled, so an ingest-set - // spinner can't stick after a byte-identical re-upload (same - // data URL, no further load event ever fires). - this._releaseMask(); - } - this._hidShowing = false; - this._img.style.display = 'block'; - this._empty.style.display = 'none'; - this.setAttribute('data-filled', ''); - this._clampView(); - this._applyView(); - } else { - this.removeAttribute('data-swapping'); - // The src is being removed — no load/error will ever fire for it. - this._loadPending = false; - // A transient attribution-error wipe of a showing image happens on - // the pick path: the host sets src one setAttribute before credit, - // so render N hides the old image (attrError) and render N+1 - // restores a URL. Remember the wipe so that restore renders as a - // replacement (spinner), not a first fill (blank frame). - this._hidShowing = attrError && !!this._img.getAttribute('src'); - this._img.style.display = 'none'; - this._img.removeAttribute('src'); - this._ghost.removeAttribute('src'); - // The error tile owns the blocked-photo state; .empty stays for - // the genuinely-empty slot. - this._empty.style.display = attrError ? 'none' : 'flex'; - this.removeAttribute('data-filled'); - } - - // Credit belongs to the author src, so a user drop hides it. - // textContent + the http(s)-only funnel keep external strings inert. - const showCredit = !!(url && credit && !this._userUrl && !attrError); - this._credit.textContent = ''; - if (showCredit) { - // Validate once (resolved against the document, http(s) only), - // then append the terms-required utm referral params to links - // that point back at unsplash.com. - let href = ''; - const rawHref = this.getAttribute('credit-href') || ''; - if (rawHref) { - try { - const u = new URL(rawHref, document.baseURI); - if (u.protocol === 'http:' || u.protocol === 'https:') { - href = withReferral(u.href); - } - } catch {} - } - const mkLink = (text, linkHref) => { - const a = document.createElement('a'); - a.setAttribute('target', '_blank'); - a.setAttribute('rel', 'noopener noreferrer'); - a.setAttribute('href', linkHref); - a.textContent = text; - return a; - }; - // Unsplash's prescribed credit is TWO links — the photographer's - // name to their profile (credit-href) and 'Unsplash' to the - // homepage. Render that split whenever the text has the canonical - // shape; other text keeps the legacy single-link rendering. - const m = /^Photo by (.+) on Unsplash$/.exec(credit); - if (m) { - this._credit.appendChild(document.createTextNode('Photo by ')); - this._credit.appendChild( - href ? mkLink(m[1], href) : document.createTextNode(m[1]) - ); - this._credit.appendChild(document.createTextNode(' on ')); - this._credit.appendChild(mkLink('Unsplash', UNSPLASH_HOMEPAGE_HREF)); - } else if (href) { - this._credit.appendChild(mkLink(credit, href)); - } else { - this._credit.textContent = credit; - } - } - this.toggleAttribute('data-credit', showCredit); - } - } - - if (!customElements.get('image-slot')) { - customElements.define('image-slot', ImageSlot); - } -})(); diff --git a/design/baseline/# UI для онлайн звонка/support.js b/design/baseline/# UI для онлайн звонка/support.js deleted file mode 100644 index 6ca00e3..0000000 --- a/design/baseline/# UI для онлайн звонка/support.js +++ /dev/null @@ -1,1768 +0,0 @@ -// GENERATED from dc-runtime/src/*.ts — do not edit. Rebuild with `cd dc-runtime && bun run build`. -"use strict"; -(() => { - var __defProp = Object.defineProperty; - var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); - - // src/react.ts - function getReact() { - const R = window.React; - if (!R) throw new Error("dc-runtime: window.React is not available yet"); - return R; - } - function getReactDOM() { - const RD = window.ReactDOM; - if (!RD) throw new Error("dc-runtime: window.ReactDOM is not available yet"); - return RD; - } - var h = ((...args) => getReact().createElement( - ...args - )); - - // src/parse.ts - function parseDcDocument(doc) { - const dc = doc.querySelector("x-dc"); - if (!dc) return null; - const scriptEl = doc.querySelector("script[data-dc-script]"); - const { props, preview } = parseDataProps( - scriptEl?.getAttribute("data-props") ?? null - ); - return { - template: dc.innerHTML, - js: scriptEl ? scriptEl.textContent || "" : "", - props, - preview - }; - } - function parseDcText(src) { - const openMatch = /]*)?>/.exec(src); - if (!openMatch) return null; - const close = src.lastIndexOf(""); - if (close === -1 || close < openMatch.index) return null; - const template = src.slice(openMatch.index + openMatch[0].length, close); - const doc = new DOMParser().parseFromString(src, "text/html"); - const scriptEl = doc.querySelector("script[data-dc-script]"); - const { props, preview } = parseDataProps( - scriptEl?.getAttribute("data-props") ?? null - ); - return { - template, - js: scriptEl ? scriptEl.textContent || "" : "", - props, - preview - }; - } - function parseDataProps(raw) { - if (!raw) return { props: null, preview: null }; - let parsed; - try { - parsed = JSON.parse(raw); - } catch { - return { props: null, preview: null }; - } - if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) { - return { props: null, preview: null }; - } - const obj = parsed; - const preview = obj.$preview && typeof obj.$preview === "object" ? obj.$preview : null; - const rest = {}; - for (const k of Object.keys(obj)) { - if (k[0] !== "$") rest[k] = obj[k]; - } - return { props: Object.keys(rest).length ? rest : null, preview }; - } - function dcNameFromPath(pathname) { - let p = pathname || ""; - try { - p = decodeURIComponent(p); - } catch { - } - const base = p.split("/").pop() || "Root"; - return base.replace(/\.dc\.html$/, "").replace(/\.html?$/, "") || "Root"; - } - - // src/boot.ts - var BASE_CSS = ` - .sc-placeholder{background:color-mix(in srgb,currentColor 8%,transparent); - border:1px solid color-mix(in srgb,currentColor 50%,transparent); - border-radius:2px;box-sizing:border-box;overflow:hidden} - @keyframes sc-shine{0%{background-position:100% 50%}100%{background-position:0% 50%}} - html.sc-dc-streaming .sc-placeholder, - html.sc-dc-streaming .sc-interp.sc-missing{position:relative; - background:color-mix(in srgb,currentColor 5%,transparent); - border-color:transparent} - html.sc-dc-streaming .sc-placeholder::before, - html.sc-dc-streaming .sc-interp.sc-missing::before{content:''; - position:absolute;inset:0;pointer-events:none; - background:linear-gradient(90deg,rgba(217,119,87,0) 25%,rgba(247,225,211,.95) 37%,rgba(217,119,87,0) 63%); - background-size:400% 100%;animation:sc-shine 1.4s ease infinite} - html.sc-dc-streaming .sc-placeholder:nth-child(n+9 of .sc-placeholder)::before, - html.sc-dc-streaming .sc-interp.sc-missing:nth-child(n+9 of .sc-interp.sc-missing)::before{animation:none; - background:color-mix(in srgb,currentColor 8%,transparent)} - .sc-placeholder-error{padding:4px 8px;font:11px/1.4 ui-monospace,monospace; - color:color-mix(in srgb,currentColor 70%,transparent);word-break:break-word} - .sc-interp.sc-missing{display:inline-block;width:2em;height:1em;overflow:hidden; - vertical-align:text-bottom;background:rgba(255,255,255,.3);border:1px solid rgba(0,0,0,.5); - border-radius:2px;box-sizing:border-box;color:transparent; - user-select:none} - .sc-interp.sc-unresolved{font-family:ui-monospace,monospace;font-size:.85em; - color:color-mix(in srgb,currentColor 50%,transparent); - background:color-mix(in srgb,currentColor 10%,transparent);border-radius:3px; - padding:0 3px} - .sc-host.sc-has-error{position:relative} - .sc-logic-error{position:absolute;top:8px;left:8px;z-index:2147483647;max-width:60ch; - padding:6px 10px;background:#b00020;color:#fff;font:12px/1.4 ui-monospace,monospace; - border-radius:4px;white-space:pre-wrap;pointer-events:none} - /* Mirrors PRINT_BASELINE_CSS in apps/web deck-stage-export.ts \u2014 keep both - in sync until dc-runtime regains a build step. */ - @media print { - @page { margin: 0.5cm; } - figure, table { break-inside: avoid; } - #dc-root, #dc-root > .sc-host { height: auto; } - *, *::before, *::after { - print-color-adjust: exact; -webkit-print-color-adjust: exact; - backdrop-filter: none !important; -webkit-backdrop-filter: none !important; - animation-delay: -99s !important; animation-duration: .001s !important; - animation-iteration-count: 1 !important; animation-fill-mode: both !important; - animation-play-state: running !important; transition-duration: 0s !important; - } - } - `; - var FULL_PAGE_CSS = "html,body{height:100%;margin:0}#dc-root,#dc-root>.sc-host{height:100%}"; - function rootNameForDocument(doc, loc) { - let bootPath = loc.pathname || ""; - if (!/\.dc\.html?$/i.test(safeDecode(bootPath))) { - try { - bootPath = new URL(doc.baseURI || "/").pathname; - } catch { - } - } - return dcNameFromPath(bootPath); - } - function safeDecode(s) { - try { - return decodeURIComponent(s); - } catch { - return s; - } - } - function boot(runtime, doc = document) { - const parsed = parseDcDocument(doc); - if (!parsed) return null; - const React = getReact(); - const rootName = rootNameForDocument(doc, location); - runtime.markFetched(rootName); - runtime.setRootName(rootName); - runtime.adoptParsed(rootName, parsed); - if (!window.__resources) { - fetch(location.href).then((res) => res.ok ? res.text() : "").then((t) => { - const raw = t ? parseDcText(t) : null; - if (raw?.template) runtime.updateHtml(rootName, raw.template); - }).catch(() => { - }); - } - const dc = doc.querySelector("x-dc"); - const hostEl = doc.createElement("div"); - hostEl.id = "dc-root"; - dc.replaceWith(hostEl); - if (!parsed.preview) { - const s = doc.createElement("style"); - s.textContent = FULL_PAGE_CSS; - doc.head.appendChild(s); - } - const Root = runtime.getDC(rootName); - const entry = runtime.registry.get(rootName); - function StandaloneRoot() { - const [, setTick] = React.useState(0); - React.useEffect(() => { - const sub = () => setTick((n) => n + 1); - entry.subs.add(sub); - return () => { - entry.subs.delete(sub); - }; - }, []); - const defaults = React.useMemo(() => { - const d = {}; - for (const k in entry.propsMeta || {}) { - const v = entry.propsMeta?.[k]?.default; - if (v !== void 0) d[k] = v; - } - return d; - }, [entry.propsMeta]); - return h(Root, { ...defaults, ...entry.propOverrides || {} }); - } - const ReactDOM = getReactDOM(); - if (ReactDOM.createRoot) - ReactDOM.createRoot(hostEl).render(h(StandaloneRoot)); - else ReactDOM.render(h(StandaloneRoot), hostEl); - return rootName; - } - - // src/expr.ts - var IDENT_RE = /^[A-Za-z_$][A-Za-z0-9_$]*/; - var NUMBER_RE = /^-?\d+(\.\d+)?$/; - function resolve(vals, src) { - const expr = String(src).trim(); - if (!expr) return void 0; - if (expr[0] === "(" && expr[expr.length - 1] === ")" && parensWrapWhole(expr)) { - return resolve(vals, expr.slice(1, -1)); - } - const eq = findTopLevelEquality(expr); - if (eq) { - const lv = resolve(vals, expr.slice(0, eq.index)); - const rv = resolve(vals, expr.slice(eq.index + eq.op.length)); - switch (eq.op) { - case "===": - return lv === rv; - case "!==": - return lv !== rv; - case "==": - return lv == rv; - default: - return lv != rv; - } - } - if (expr[0] === "!") return !resolve(vals, expr.slice(1)); - if (expr === "true") return true; - if (expr === "false") return false; - if (expr === "null") return null; - if (expr === "undefined") return void 0; - if (NUMBER_RE.test(expr)) return Number(expr); - if (expr.length >= 2 && (expr[0] === '"' || expr[0] === "'") && expr[expr.length - 1] === expr[0]) { - return expr.slice(1, -1); - } - return resolvePath(vals, expr); - } - function parensWrapWhole(expr) { - let depth = 0; - for (let i = 0; i < expr.length - 1; i++) { - if (expr[i] === "(") depth++; - else if (expr[i] === ")") { - depth--; - if (depth === 0) return false; - } - } - return true; - } - function findTopLevelEquality(expr) { - let depth = 0; - for (let i = 0; i < expr.length; i++) { - const c = expr[i]; - if (c === "[" || c === "(") depth++; - else if (c === "]" || c === ")") depth--; - else if (depth === 0 && (c === "=" || c === "!") && expr[i + 1] === "=") { - if (i > 0 && (expr[i - 1] === "=" || expr[i - 1] === "!")) continue; - if (!expr.slice(0, i).trim()) continue; - const op = expr[i + 2] === "=" ? c + "==" : c + "="; - return { index: i, op }; - } - } - return null; - } - function resolvePath(vals, expr) { - const head = expr.match(IDENT_RE); - if (!head) return void 0; - let cur = vals == null ? void 0 : vals[head[0]]; - let i = head[0].length; - while (i < expr.length) { - if (expr[i] === ".") { - const m = expr.slice(i + 1).match(IDENT_RE) || expr.slice(i + 1).match(/^\d+/); - if (!m) return void 0; - cur = cur == null ? void 0 : cur[m[0]]; - i += 1 + m[0].length; - } else if (expr[i] === "[") { - let depth = 1; - let j = i + 1; - while (j < expr.length && depth > 0) { - if (expr[j] === "[") depth++; - else if (expr[j] === "]") { - depth--; - if (depth === 0) break; - } - j++; - } - if (depth !== 0) return void 0; - const key = resolve(vals, expr.slice(i + 1, j)); - cur = cur == null ? void 0 : cur[key]; - i = j + 1; - } else { - return void 0; - } - } - return cur; - } - - // src/encode.ts - var CAMEL_ATTR = "sc-camel-"; - var INLINE_TEXT_TAGS = new Set( - "a abbr b bdi bdo br cite code del dfn em i ins kbd mark q s samp small span strike strong sub sup u var wbr".split( - " " - ) - ); - var RAW_WRAP = { - select: "sc-raw-select", - table: "sc-raw-table", - tbody: "sc-raw-tbody", - thead: "sc-raw-thead", - tfoot: "sc-raw-tfoot", - tr: "sc-raw-tr", - td: "sc-raw-td", - th: "sc-raw-th", - caption: "sc-raw-caption" - }; - var RAW_UNWRAP = Object.fromEntries( - Object.entries(RAW_WRAP).map(([k, v]) => [v, k]) - ); - var EVENT_MAP = { - onclick: "onClick", - onchange: "onChange", - oninput: "onInput", - onsubmit: "onSubmit", - onkeydown: "onKeyDown", - onkeyup: "onKeyUp", - onkeypress: "onKeyPress", - onmousedown: "onMouseDown", - onmouseup: "onMouseUp", - onmouseenter: "onMouseEnter", - onmouseleave: "onMouseLeave", - onfocus: "onFocus", - onblur: "onBlur", - ondoubleclick: "onDoubleClick", - oncontextmenu: "onContextMenu", - onmousemove: "onMouseMove", - onmouseover: "onMouseOver", - onmouseout: "onMouseOut", - onpointerdown: "onPointerDown", - onpointerup: "onPointerUp", - onpointermove: "onPointerMove", - onpointerenter: "onPointerEnter", - onpointerleave: "onPointerLeave", - onpointercancel: "onPointerCancel", - onpointerover: "onPointerOver", - onpointerout: "onPointerOut", - ongotpointercapture: "onGotPointerCapture", - onlostpointercapture: "onLostPointerCapture", - ontouchstart: "onTouchStart", - ontouchend: "onTouchEnd", - ontouchmove: "onTouchMove", - ontouchcancel: "onTouchCancel", - ondragstart: "onDragStart", - ondragend: "onDragEnd", - ondragenter: "onDragEnter", - ondragleave: "onDragLeave", - ondragover: "onDragOver", - onanimationstart: "onAnimationStart", - onanimationend: "onAnimationEnd", - onanimationiteration: "onAnimationIteration", - ontransitionend: "onTransitionEnd" - }; - var ATTRS = `(?:[^>"']|"[^"]*"|'[^']*')*`; - var IMPORT_SELF_CLOSE_RE = new RegExp( - "<(x-import|dc-import)(" + ATTRS + ")/>", - "gi" - ); - var CAMEL_ATTR_RE = /(\s)([a-z]+[A-Z][A-Za-z0-9]*)(\s*=)/g; - function encodeCamelAttrs(html) { - return html.replace( - CAMEL_ATTR_RE, - (_, sp, name, eq) => sp + CAMEL_ATTR + name.replace(/[A-Z]/g, (c) => "-" + c.toLowerCase()) + eq - ); - } - function encodeCase(html) { - html = html.replace( - IMPORT_SELF_CLOSE_RE, - (_, t, a) => "<" + t + a + ">" - ); - html = html.replace(/)/gi, "/gi, ""); - html = encodeCamelAttrs(html); - for (const [real, alias] of Object.entries(RAW_WRAP)) { - html = html.replace( - new RegExp("(])", "gi"), - "$1" + alias - ); - } - return html; - } - function kebabToCamel(s) { - return s.replace(/-([a-z])/g, (_, c) => c.toUpperCase()); - } - function cssToObj(css) { - const o = {}; - for (const decl of css.split(";")) { - const i = decl.indexOf(":"); - if (i < 0) continue; - const prop = decl.slice(0, i).trim(); - o[prop.startsWith("--") ? prop : kebabToCamel(prop)] = decl.slice(i + 1).trim(); - } - return o; - } - function compileAttr(raw) { - const whole = raw.match(/^\s*\{\{([\s\S]+?)\}\}\s*$/); - if (whole) { - const path = whole[1]; - return (vals) => resolve(vals, path); - } - if (raw.includes("{{")) { - const parts = raw.split(/\{\{([\s\S]+?)\}\}/g); - return (vals) => parts.map((s, i) => i & 1 ? resolve(vals, s) ?? "" : s).join(""); - } - return () => raw; - } - - // src/compile.ts - function collectProps(node, kind, host) { - const propGetters = []; - const pseudoClasses = []; - let hintSize = null; - for (const { name, value } of [...node.attributes]) { - if (name === "sc-name" || name === "data-dc-tpl") continue; - let key = name; - if (key.startsWith(CAMEL_ATTR)) - key = kebabToCamel(key.slice(CAMEL_ATTR.length)); - if (key === "hint-size") { - hintSize = value; - continue; - } - if (key.startsWith("style-")) { - pseudoClasses.push(host.pseudoClass(key.slice(6), value)); - continue; - } - if (kind !== "dom") { - if (key.includes("-") && !(kind === "x-import" && (key.startsWith("aria-") || key.startsWith("data-")))) - key = kebabToCamel(key); - } else { - if (key === "class") key = "className"; - else if (key === "for") key = "htmlFor"; - else if (key.startsWith("on")) - key = EVENT_MAP[key] || "on" + key[2].toUpperCase() + key.slice(3); - } - propGetters.push([key, compileAttr(value)]); - } - return { propGetters, pseudoClasses, hintSize }; - } - var HOST_STYLE_PROPS = /* @__PURE__ */ new Set([ - "position", - "left", - "right", - "top", - "bottom", - "inset", - "width", - "height", - "z-index", - "transform" - ]); - function hostPositionStyle(style) { - const all = typeof style === "string" ? cssToObj(style) : style != null && typeof style === "object" ? style : null; - if (!all) return void 0; - const out = {}; - for (const [k, v] of Object.entries(all)) { - const kebab = k.replace(/[A-Z]/g, (c) => "-" + c.toLowerCase()); - if (HOST_STYLE_PROPS.has(kebab)) out[k] = v; - } - return Object.keys(out).length ? out : void 0; - } - function compileTemplate(html, host) { - const tpl = document.createElement("template"); - //! nosemgrep: direct-inner-html-assignment - tpl.innerHTML = encodeCase(html); - let tplN = 0; - (function stamp(node) { - if (node.nodeType === Node.ELEMENT_NODE) { - node.setAttribute("data-dc-tpl", String(tplN++)); - } - for (const c of node.childNodes) stamp(c); - })(tpl.content); - const builders = walkChildren(tpl.content, host); - const render = ((vals, ctx) => builders.map((b, i) => b(vals || {}, ctx, i))); - render.__annotated = tpl.innerHTML; - return render; - } - function walkChildren(node, host) { - return [...node.childNodes].map((c) => walk(c, host)).filter((b) => b != null); - } - function walk(node, host) { - if (node.nodeType === Node.TEXT_NODE) return walkText(node); - if (node.nodeType !== Node.ELEMENT_NODE) return null; - const el = node; - const tag = el.tagName.toLowerCase(); - if (tag === "sc-for") return walkFor(el, host); - if (tag === "sc-if") return walkIf(el, host); - if (tag === "x-import") return walkXImport(el, host); - if (tag === "sc-helmet") return host.helmet(el); - if (tag === "dc-import") return walkComponent(el, host); - return walkElement(el, host); - } - var warnedHoles = /* @__PURE__ */ new Set(); - function warnUnresolved(ctx, what) { - const key = (ctx?.__name || "?") + "\0" + what; - if (warnedHoles.has(key)) return; - warnedHoles.add(key); - console.warn("[dc-runtime] " + (ctx?.__name || "template") + ": " + what); - } - function walkText(node) { - const txt = node.nodeValue ?? ""; - if (!txt.includes("{{")) { - if (!txt.trim() && !txt.includes(" ")) return null; - return () => txt; - } - const parts = txt.split(/\{\{([\s\S]+?)\}\}/g); - return (vals, ctx, key) => h( - getReact().Fragment, - { key }, - ...parts.map((p, i) => { - if (!(i & 1)) return p; - const v = resolve(vals, p); - if (v === void 0) { - if (!ctx?.__streamingNow) { - if (document.body?.hasAttribute("data-dc-editor-on")) { - return h( - "span", - { key: i, className: "sc-interp sc-unresolved" }, - "{{ " + p.trim() + " }}" - ); - } - warnUnresolved( - ctx, - "{{ " + p.trim() + " }} never resolved \u2014 rendered as empty" - ); - return null; - } - return h( - "span", - { key: i, className: "sc-interp sc-missing" }, - p.trim() - ); - } - if (getReact().isValidElement(v) || Array.isArray(v)) { - return h(getReact().Fragment, { key: i }, v); - } - if (v === null || typeof v === "boolean") return null; - return h("span", { key: i, className: "sc-interp" }, String(v)); - }) - ); - } - function walkFor(el, host) { - const listGet = compileAttr(el.getAttribute("list") || ""); - const asName = el.getAttribute("as") || "item"; - const hintN = parseInt(el.getAttribute("hint-placeholder-count") || "0", 10); - const kids = walkChildren(el, host); - const listSrc = el.getAttribute("list") || ""; - return (vals, ctx, key) => { - let list = listGet(vals); - if (!Array.isArray(list)) { - if (!ctx?.__streamingNow) { - if (list !== void 0 && list !== null) { - warnUnresolved( - ctx, - 'sc-for list="' + listSrc + '" is not an array (' + typeof list + ")" - ); - } - list = []; - } else { - list = hintN > 0 ? Array(hintN).fill(void 0) : []; - } - } - return h( - getReact().Fragment, - { key }, - list.map((item, i) => { - const sub = { ...vals, [asName]: item, $index: i }; - return h( - getReact().Fragment, - { key: i }, - kids.map((b, j) => b(sub, ctx, j)) - ); - }) - ); - }; - } - function walkIf(el, host) { - const valGet = compileAttr(el.getAttribute("value") || ""); - const hintRaw = el.getAttribute("hint-placeholder-val"); - const hintGet = hintRaw != null ? compileAttr(hintRaw) : null; - const kids = walkChildren(el, host); - return (vals, ctx, key) => { - let v = valGet(vals); - if (v === void 0 && hintGet && ctx?.__streamingNow) v = hintGet(vals); - return v ? h( - getReact().Fragment, - { key }, - kids.map((b, j) => b(vals, ctx, j)) - ) : null; - }; - } - function walkComponent(el, host) { - const name = el.getAttribute("name") || el.getAttribute("component") || ""; - el.removeAttribute("name"); - el.removeAttribute("component"); - const tplId = el.getAttribute("data-dc-tpl"); - const styleRaw = el.getAttribute("style"); - el.removeAttribute("style"); - const styleGet = styleRaw != null ? compileAttr(styleRaw) : null; - const { propGetters, hintSize } = collectProps(el, "dc-import", host); - const kids = walkChildren(el, host); - return (vals, ctx, key) => { - const props = { - key, - __hintSize: hintSize, - __tplId: tplId, - __hostStyle: styleGet ? hostPositionStyle(styleGet(vals)) : void 0 - }; - for (const [k, g] of propGetters) { - const v = g(vals); - if (k === "dcProps") { - if (v && typeof v === "object") Object.assign(props, v); - continue; - } - props[k] = v; - } - if (kids.length) props.children = kids.map((b, j) => b(vals, ctx, j)); - return h(host.component(name), props); - }; - } - function walkXImport(el, host) { - const globalNameGet = compileAttr( - el.getAttribute("component-from-global-scope") || "" - ); - const exportNameGet = compileAttr( - el.getAttribute("component") || el.getAttribute("name") || "" - ); - const fromRaw = el.getAttribute("from") || (el.getAttribute("component-from-global-scope") ? "" : el.getAttribute("src") || el.getAttribute("import") || ""); - const urls = fromRaw.trim() ? fromRaw.trim().split(/\s+/) : []; - const url = urls.length ? urls[urls.length - 1] : ""; - const kindOf = (u) => /\.(jsx|tsx)(\?|#|$)/i.test(u) ? "jsx" : "js"; - const tplId = el.getAttribute("data-dc-tpl"); - const styleRaw = el.getAttribute("style"); - el.removeAttribute("style"); - const styleGet = styleRaw != null ? compileAttr(styleRaw) : null; - const wrap = tplId != null || styleGet != null; - const { propGetters, hintSize } = collectProps(el, "x-import", host); - const hasContent = el.children.length > 0 || !!(el.textContent || "").trim(); - const kids = hasContent ? walkChildren(el, host) : []; - const urlBindable = fromRaw.includes("{{"); - if (urls.length && !urlBindable) { - let prev; - for (const u of urls) prev = host.loadExternal(kindOf(u), u, prev); - } - const evalName = (g, vals) => { - const v = g(vals); - const s = v == null ? "" : String(v); - return s.includes("{{") ? "" : s; - }; - return (vals, ctx, key) => { - const globalName = evalName(globalNameGet, vals); - const name = globalName || evalName(exportNameGet, vals); - const C = !name || urlBindable ? null : globalName ? host.resolveExternalGlobal(url, globalName) : host.resolveExternal(url, name); - const hostStyle = styleGet ? hostPositionStyle(styleGet(vals)) : void 0; - const wrapper = wrap ? { - key, - className: "sc-host-x", - "data-dc-tpl": tplId, - style: hostStyle || { display: "contents" } - } : null; - if (!C) { - const error = urlBindable ? "x-import `from` cannot contain {{ \u2026 }} \u2014 module URLs are resolved at parse time; use a literal URL" : host.resolveExternalError(url, name); - const ph = host.placeholder({ - key: wrapper ? void 0 : key, - name, - hintSize, - error - }); - return wrapper ? h("div", wrapper, ph) : ph; - } - const props = wrapper ? {} : { key }; - let unresolvedHole = false; - for (const [k, g] of propGetters) { - if (k === "component" || k === "componentFromGlobalScope" || k === "from") { - continue; - } - const v = g(vals); - if (v === void 0) unresolvedHole = true; - if (k === "dcProps") { - if (v && typeof v === "object") Object.assign(props, v); - continue; - } - props[k] = v; - } - if (unresolvedHole && ctx?.__htmlStreamingNow) { - const ph = host.placeholder({ - key: wrapper ? void 0 : key, - name, - hintSize, - error: null - }); - return wrapper ? h("div", wrapper, ph) : ph; - } - if (kids.length) props.children = kids.map((b, j) => b(vals, ctx, j)); - return wrapper ? h("div", wrapper, h(C, props)) : h(C, props); - }; - } - function contentKey(el) { - const clone = el.cloneNode(true); - for (const d of clone.querySelectorAll("*")) { - while (d.attributes.length) d.removeAttribute(d.attributes[0].name); - } - const s = clone.innerHTML; - let h2 = 5381; - for (let i = 0; i < s.length; i++) h2 = (h2 << 5) + h2 + s.charCodeAt(i) | 0; - return s.length + "." + (h2 >>> 0).toString(36); - } - var NEVER_CONTENT_KEYED = new Set( - "script style textarea option title select canvas iframe video audio".split( - " " - ) - ); - var NOT_INLINE_SELECTOR = ":not(" + [...INLINE_TEXT_TAGS].join(",") + ")"; - function walkElement(el, host) { - const realTag = RAW_UNWRAP[el.localName] || el.localName; - const tplId = el.getAttribute("data-dc-tpl"); - const inlineOnly = el.childNodes.length > 0 && !NEVER_CONTENT_KEYED.has(realTag) && el.querySelector(NOT_INLINE_SELECTOR) === null; - const keySuffix = inlineOnly ? "|" + contentKey(el) : ""; - const { propGetters, pseudoClasses } = collectProps(el, "dom", host); - const kids = walkChildren(el, host); - return (vals, ctx, key) => { - const props = { - key: key + keySuffix, - "data-dc-tpl": tplId - }; - for (const [k, g] of propGetters) { - let v = g(vals); - if (k === "style" && typeof v === "string") v = cssToObj(v); - if ((k === "value" || k === "checked") && v === void 0) { - v = k === "checked" ? false : ""; - } - props[k] = v; - } - if (pseudoClasses.length) { - props.className = [props.className, ...pseudoClasses].filter(Boolean).join(" "); - } - return h(realTag, props, ...kids.map((b, j) => b(vals, ctx, j))); - }; - } - - // src/logic.ts - var StreamableLogic = class { - constructor(props) { - __publicField(this, "props"); - __publicField(this, "state", {}); - /** Back-pointer to the wrapper component, installed after construction. */ - __publicField(this, "__host"); - this.props = props || {}; - } - setState(update, cb) { - this.__host && this.__host.__setLogicState(update, cb); - } - forceUpdate() { - this.__host && this.__host.forceUpdate(); - } - componentDidMount() { - } - componentDidUpdate(_prevProps) { - } - componentWillUnmount() { - } - /** The flat object the template renders against (merged over props). */ - renderVals() { - return {}; - } - }; - function evalDcLogic(src) { - //! nosemgrep: eval-and-function-constructor - const fn = new Function( - "DCLogic", - "StreamableLogic", - "React", - src + '\n;return (typeof Component!=="undefined"&&Component)||undefined;' - ); - return fn(StreamableLogic, StreamableLogic, getReact()); - } - - // src/component.ts - function shallowEqual(a, b) { - if (!b) return false; - const ak = Object.keys(a).filter((k) => k !== "children"); - const bk = Object.keys(b).filter((k) => k !== "children"); - if (ak.length !== bk.length) return false; - for (const k of ak) if (a[k] !== b[k]) return false; - return true; - } - function Placeholder({ - name, - hintSize, - streaming, - error - }) { - const [w, hgt] = (hintSize || "100%,60px").split(","); - return h( - "div", - { - className: "sc-placeholder" + (streaming ? " sc-streaming" : ""), - style: { width: w.trim(), height: hgt && hgt.trim() }, - title: name - }, - error ? h( - "div", - { className: "sc-placeholder-error" }, - (name ? name + ": " : "") + error - ) : null - ); - } - function hintToMin(hint) { - if (!hint) return void 0; - const [w, hgt] = hint.split(","); - return { minWidth: w.trim(), minHeight: hgt && hgt.trim() }; - } - function createComponentFactory(registry, ensureFetched) { - const React = getReact(); - const AncestorContext = React.createContext([]); - class StreamableComponent extends React.Component { - constructor(props) { - super(props); - __publicField(this, "__name"); - __publicField(this, "__sub"); - __publicField(this, "__needsDidMount", false); - /** Snapshot of the registry's streaming flags taken at render time — - * builders read it off the RenderCtx (this) to pick placeholder vs - * render-nothing for unresolved values. */ - __publicField(this, "__streamingNow", false); - __publicField(this, "__htmlStreamingNow", false); - /** When a construct throws, remember the (class, registry.ver, props) - * triple so render-time reconcile doesn't re-attempt it on every parent - * re-render. A registry bump (new class, template, external module - * resolving via bumpAll) changes `ver` and breaks the memo so an - * env-dependent constructor can self-heal. */ - __publicField(this, "__failedLogic", null); - __publicField(this, "__failedUserProps", null); - __publicField(this, "__failedVer", -1); - /** Per-instance constructor error — kept here (not on the registry entry) - * so one instance's successful construct can't hide a sibling's failure, - * and a construct can never wipe an eval error `updateJs` recorded on - * `r.logicError`. */ - __publicField(this, "__ctorError", null); - __publicField(this, "logic"); - this.__name = props.__name; - this.state = { __v: 0, __err: null }; - this.__sub = () => { - if (this.state.__err) this.setState({ __err: null }); - this.forceUpdate(); - }; - this.__makeLogic(registry.get(this.__name).Logic, null); - ensureFetched(this.__name); - } - /** Error-boundary hook: a render crash anywhere in this DC's subtree - * (its own template, an x-import'd component, a child DC without its - * own deeper boundary) lands here instead of unmounting the page. */ - static getDerivedStateFromError(e) { - return { __err: e instanceof Error && e.message ? e.message : String(e) }; - } - componentDidCatch(e, info) { - console.error( - "[dc-runtime] render error in <" + this.__name + ">:", - e, - info?.componentStack || "" - ); - } - /** Instantiate the logic class (or the no-op base) and adopt `prevState` - * over its initial state — used both at mount and on hot-swap. */ - __makeLogic(Logic, prevState) { - const L = Logic || StreamableLogic; - try { - this.logic = new L(this.__userProps()); - this.__failedLogic = null; - this.__failedUserProps = null; - this.__ctorError = null; - } catch (e) { - console.error(e); - this.__failedLogic = Logic; - this.__failedUserProps = this.__userProps(); - this.__failedVer = registry.get(this.__name).ver; - this.__ctorError = this.__name + ": " + (e instanceof Error && e.message ? e.message : String(e)); - this.logic = new StreamableLogic( - this.__userProps() - ); - } - this.logic.__host = this; - if (prevState) - this.logic.state = { ...this.logic.state || {}, ...prevState }; - } - /** The props the author's logic + template see — internal __-prefixed - * wiring stripped. */ - __userProps() { - const { __name, __hintSize, __tplId, __hostStyle, ...rest } = this.props; - return rest; - } - __setLogicState(update, cb) { - const prev = this.logic.state; - const patch = typeof update === "function" ? update(prev) : update; - this.logic.state = { ...prev, ...patch }; - this.setState((s) => ({ __v: s.__v + 1 }), cb); - } - /** Swap the logic instance when the registry's Logic class changed - * (streaming completion, hot reload). State carries over; didMount - * re-fires after the swap commits so refs exist. */ - __reconcileLogic() { - const r = registry.get(this.__name); - const Next = r.Logic; - const Cur = this.logic.constructor; - if (Next === Cur || !Next && Cur === StreamableLogic || Next === this.__failedLogic && r.ver === this.__failedVer && shallowEqual(this.__userProps(), this.__failedUserProps)) { - return; - } - if (!this.__needsDidMount) { - try { - this.logic.componentWillUnmount(); - } catch (e) { - console.error(e); - } - } - this.__makeLogic(Next, this.logic.state); - this.__needsDidMount = true; - } - componentDidMount() { - registry.get(this.__name).subs.add(this.__sub); - try { - this.logic.componentDidMount(); - } catch (e) { - console.error(e); - } - } - componentDidUpdate(prevProps) { - this.logic.props = this.__userProps(); - if (this.__needsDidMount) { - if (this.state.__err || !registry.get(this.__name).tpl) return; - this.__needsDidMount = false; - try { - this.logic.componentDidMount(); - } catch (e) { - console.error(e); - } - } else { - try { - this.logic.componentDidUpdate(prevProps); - } catch (e) { - console.error(e); - } - } - } - componentWillUnmount() { - registry.get(this.__name).subs.delete(this.__sub); - if (!this.__needsDidMount) { - try { - this.logic.componentWillUnmount(); - } catch (e) { - console.error(e); - } - } - } - render() { - const r = registry.get(this.__name); - const cls = "sc-host" + (r.htmlStreaming ? " sc-streaming-html" : "") + (r.jsStreaming ? " sc-streaming-js" : ""); - const hintStyle = r.htmlStreaming ? hintToMin(this.props.__hintSize) : void 0; - const hostStyle = this.props.__hostStyle || hintStyle ? { ...hintStyle || {}, ...this.props.__hostStyle || {} } : void 0; - const hostBase = { - className: cls, - style: hostStyle, - "data-sc-name": this.__name, - "data-dc-tpl": this.props.__tplId - }; - const chain = Array.isArray(this.context) ? this.context : []; - if (chain.includes(this.__name)) { - const cycle = [ - ...chain.slice(chain.indexOf(this.__name)), - this.__name - ].join(" \u2192 "); - return h( - "div", - { ...hostBase, className: cls + " sc-has-error" }, - h(Placeholder, { - name: this.__name, - hintSize: this.props.__hintSize, - error: "circular import: " + cycle - }) - ); - } - if (this.state.__err) { - return h( - "div", - { ...hostBase, className: cls + " sc-has-error" }, - h( - "div", - { className: "sc-logic-error", "data-omelette-chrome": "" }, - this.__name + ": " + this.state.__err - ), - h(Placeholder, { - name: this.__name, - hintSize: this.props.__hintSize, - error: this.state.__err - }) - ); - } - this.__reconcileLogic(); - if (!r.tpl) { - return h( - "div", - hostBase, - h(Placeholder, { name: this.__name, hintSize: this.props.__hintSize }) - ); - } - const userProps = this.__userProps(); - this.logic.props = userProps; - let vals = userProps; - let renderErr = r.logicError || this.__ctorError; - try { - vals = { ...userProps, ...this.logic.renderVals() || {} }; - } catch (e) { - console.error(e); - renderErr = this.__name + ".renderVals(): " + (e instanceof Error && e.message ? e.message : String(e)); - } - this.__streamingNow = !!(r.htmlStreaming || r.jsStreaming); - this.__htmlStreamingNow = !!r.htmlStreaming; - return h( - "div", - { ...hostBase, className: cls + (renderErr ? " sc-has-error" : "") }, - renderErr && h( - "div", - { className: "sc-logic-error", "data-omelette-chrome": "" }, - renderErr - ), - h( - AncestorContext.Provider, - { value: [...chain, this.__name] }, - r.tpl(vals, this) - ) - ); - } - } - __publicField(StreamableComponent, "contextType", AncestorContext); - const named = /* @__PURE__ */ new Map(); - function getDC(name) { - const hit = named.get(name); - if (hit) return hit; - function Dispatcher(p) { - const [, setTick] = React.useState(0); - React.useEffect(() => { - const sub = () => setTick((n) => n + 1); - registry.get(name).subs.add(sub); - return () => { - registry.get(name).subs.delete(sub); - }; - }, []); - ensureFetched(name); - return h(StreamableComponent, { ...p, __name: name }); - } - Dispatcher.displayName = name; - named.set(name, Dispatcher); - return Dispatcher; - } - return { - getDC, - StreamableComponent - }; - } - - // src/bundled.ts - function bundledBlob(url) { - const blobs = window.__resourceBlobs; - const b = blobs ? blobs[url.split("#")[0]] : void 0; - return b instanceof Blob ? b : null; - } - - // src/cdn.ts - var REACT_URL = "https://unpkg.com/react@18.3.1/umd/react.production.min.js"; - var REACT_SRI = "sha384-DGyLxAyjq0f9SPpVevD6IgztCFlnMF6oW/XQGmfe+IsZ8TqEiDrcHkMLKI6fiB/Z"; - var REACT_DOM_URL = "https://unpkg.com/react-dom@18.3.1/umd/react-dom.production.min.js"; - var REACT_DOM_SRI = "sha384-gTGxhz21lVGYNMcdJOyq01Edg0jhn/c22nsx0kyqP0TxaV5WVdsSH1fSDUf5YJj1"; - var BABEL_URL = "https://unpkg.com/@babel/standalone@7.29.0/babel.min.js"; - var BABEL_SRI = "sha384-m08KidiNqLdpJqLq95G/LEi8Qvjl/xUYll3QILypMoQ65QorJ9Lvtp2RXYGBFj1y"; - function cdnScriptFor(url, sri) { - const res = window.__resources; - const v = res ? res[url] : void 0; - return typeof v === "string" && v ? { src: v } : { src: url, integrity: sri }; - } - - // src/external.ts - var isCustomElementName = (n) => !n.includes(".") && n.includes("-"); - function isRenderableType(g) { - if (typeof g === "function") return !isElementClass(g); - return typeof g === "object" && g !== null && typeof g.$$typeof === "symbol"; - } - function resolveDottedPath(root, name) { - let cur = root; - for (const seg of name.split(".")) { - if (cur == null) return void 0; - cur = cur[seg]; - } - return cur; - } - var GLOBAL_POLL_INTERVAL_MS = 50; - var GLOBAL_POLL_TIMEOUT_MS = 3e4; - function createExternalModules(onResolved) { - const cache = /* @__PURE__ */ new Map(); - let babelLoading = null; - const reportedMissing = /* @__PURE__ */ new Map(); - const polling = /* @__PURE__ */ new Set(); - function ensureBabel() { - if (window.Babel) return Promise.resolve(); - if (babelLoading) return babelLoading; - const babel = cdnScriptFor(BABEL_URL, BABEL_SRI); - babelLoading = new Promise((res, rej) => { - const s = document.createElement("script"); - s.src = babel.src; - if (babel.integrity) { - s.integrity = babel.integrity; - s.crossOrigin = "anonymous"; - } - s.onload = () => res(); - s.onerror = rej; - document.head.appendChild(s); - }); - return babelLoading; - } - const pending = /* @__PURE__ */ new Map(); - function load(kind, url, after) { - const existing = pending.get(url); - if (existing) return existing; - cache.set(url, null); - console.info("[dc-runtime] x-import: loading", url, "(" + kind + ")"); - const ready = Promise.all([ - kind === "jsx" ? ensureBabel() : Promise.resolve(), - after ?? Promise.resolve() - ]); - const p = ready.then(() => { - const pre = bundledBlob(url); - if (pre) return pre.text(); - return fetch(url).then((r) => { - if (!r.ok) throw new Error("HTTP " + r.status); - return r.text(); - }); - }).then((src) => { - const code = kind === "jsx" ? window.Babel.transform(src, { - filename: url, - presets: ["react", "typescript"] - }).code : src; - const module = { exports: {} }; - const before = new Set(Object.keys(window)); - //! nosemgrep: eval-and-function-constructor - new Function("React", "module", "exports", "require", code)( - getReact(), - module, - module.exports, - () => ({}) - ); - const globals = {}; - for (const k of Object.keys(window)) { - if (!before.has(k) && typeof window[k] === "function") { - globals[k] = window[k]; - } - } - cache.set(url, { mod: module.exports, globals }); - console.info( - "[dc-runtime] x-import: loaded", - url, - "\u2014 exports:", - Object.keys(module.exports), - "window globals:", - Object.keys(globals) - ); - onResolved(); - }).catch((e) => { - cache.set(url, { - mod: {}, - globals: {}, - error: "failed to load: " + (e instanceof Error && e.message ? e.message : String(e)) - }); - console.error( - "[dc-runtime] x-import: FAILED to load", - url, - "(" + kind + ")", - e - ); - onResolved(); - }); - pending.set(url, p); - return p; - } - function resolve2(url, name) { - const entry = cache.get(url); - if (!entry) return null; - const { mod, globals } = entry; - const C = mod && mod[name] || globals && globals[name] || typeof window !== "undefined" && window[name] || mod && mod.default; - if (typeof C === "function") return C; - const key = url + "\0" + name; - if (!reportedMissing.has(key)) { - reportedMissing.set( - key, - entry.error || 'no export named "' + name + '" (has: ' + Object.keys(mod).join(", ") + ")" - ); - console.error( - "[dc-runtime] x-import: module", - url, - "loaded but has no component named", - JSON.stringify(name), - "\u2014 available exports:", - Object.keys(mod), - "window globals:", - Object.keys(globals), - ". The module must `module.exports = {" + name + "}` or set `window." + name + "`." - ); - } - return null; - } - function waitForGlobal(name) { - if (polling.has(name)) return; - polling.add(name); - const started = Date.now(); - const isCE = isCustomElementName(name); - const tick = () => { - const found = isCE ? customElements.get(name) : isRenderableType(resolveDottedPath(window, name)); - if (found) { - polling.delete(name); - onResolved(); - return; - } - if (Date.now() - started >= GLOBAL_POLL_TIMEOUT_MS) { - console.warn( - "[dc-runtime] x-import: global", - JSON.stringify(name), - "never appeared on window after " + GLOBAL_POLL_TIMEOUT_MS + "ms" - ); - return; - } - setTimeout(tick, GLOBAL_POLL_INTERVAL_MS); - }; - setTimeout(tick, GLOBAL_POLL_INTERVAL_MS); - } - function resolveGlobal(url, name) { - const isCE = isCustomElementName(name); - if (!url) { - if (isCE) { - if (customElements.get(name)) return name; - waitForGlobal(name); - return null; - } - const g2 = resolveDottedPath(window, name); - if (isRenderableType(g2)) return g2; - waitForGlobal(name); - return null; - } - const entry = cache.get(url); - if (!entry) return null; - if (isCE && customElements.get(name)) return name; - const g = entry.globals[name] ?? resolveDottedPath(window, name); - if (isRenderableType(g)) return g; - if (name.includes(".")) return null; - const key = url + "\0global\0" + name; - if (!reportedMissing.has(key)) { - reportedMissing.set(key, null); - if (isCE && !customElements.get(name)) { - console.warn( - "[dc-runtime] x-import:", - url, - "loaded but no custom element", - JSON.stringify(name), - "is registered and window." + name + " is not a function \u2014 rendering <" + name + "> as an unknown element." - ); - } - } - return name; - } - function getError(url, name) { - const entry = cache.get(url); - if (entry?.error) return entry.error; - return reportedMissing.get(url + "\0" + name) || null; - } - return { load, resolve: resolve2, resolveGlobal, getError }; - } - function isElementClass(g) { - try { - return typeof g === "function" && typeof HTMLElement !== "undefined" && g.prototype instanceof HTMLElement; - } catch { - return false; - } - } - - // src/atomics.ts - var ATOMIC_CSS = ( - // layout - ".fx{display:flex}.col{display:flex;flex-direction:column}.grid{display:grid}.ac{align-items:center}.jc{justify-content:center}.jb{justify-content:space-between}.f1{flex:1}.noshrink{flex-shrink:0}.wrap{flex-wrap:wrap}.fw5{font-weight:500}.fw6{font-weight:600}.fw7{font-weight:700}.fw8{font-weight:800}.fs11{font-size:11px}.fs12{font-size:12px}.fs13{font-size:13px}.fs14{font-size:14px}.fs15{font-size:15px}.fs16{font-size:16px}.fs20{font-size:20px}.fs22{font-size:22px}.upper{text-transform:uppercase}.tc{text-align:center}.nowrap{white-space:nowrap}.gap8{gap:8px}.gap10{gap:10px}.gap12{gap:12px}.gap16{gap:16px}.gap24{gap:24px}.m0{margin:0}.mt8{margin-top:8px}.mt12{margin-top:12px}.mt16{margin-top:16px}.mb8{margin-bottom:8px}.mb12{margin-bottom:12px}.mb16{margin-bottom:16px}.posrel{position:relative}.posabs{position:absolute}.round{border-radius:50%}.ohide{overflow:hidden}.bbox{box-sizing:border-box}.pointer{cursor:pointer}.w100{width:100%}.b0{border:none}" - ); - - // src/helmet.ts - var DESIGN_DOC_MODE_RE = /]*\bname\s*=\s*["']design_doc_mode["'][^>]*\b(?:content|value)\s*=\s*["'](\w+)["']/i; - var CANVAS_BG_LIGHT = "#f0eee6"; - var CANVAS_BG_DARK = "#2e2c26"; - function createHelmetManager(doc, isStreaming) { - const mounted = /* @__PURE__ */ new Set(); - const live = /* @__PURE__ */ new Map(); - let designDocMode = null; - let canvasStyleEl = null; - let appTheme = "light"; - try { - const ds = doc.documentElement.dataset.theme; - appTheme = ds === "dark" || ds === "light" ? ds : new URLSearchParams(doc.defaultView?.location.search ?? "").get( - "theme" - ) === "dark" ? "dark" : "light"; - } catch { - } - function applyCanvasBg() { - if (!canvasStyleEl) return; - const bg = appTheme === "dark" ? CANVAS_BG_DARK : CANVAS_BG_LIGHT; - canvasStyleEl.textContent = `html,body{background:${bg}}#dc-root>.sc-host{position:relative}`; - } - function postDesignMode(mode) { - if (window.parent === window) return; - try { - window.parent.postMessage({ type: "__dc_design_mode", mode }, "*"); - } catch { - } - } - function setDesignDocMode(mode) { - if (mode === designDocMode) return; - designDocMode = mode; - postDesignMode(mode); - if (mode === "canvas") { - doc.documentElement.setAttribute("data-dc-canvas", ""); - canvasStyleEl = doc.createElement("style"); - canvasStyleEl.setAttribute("data-dc-canvas", ""); - applyCanvasBg(); - doc.head.appendChild(canvasStyleEl); - } else { - doc.documentElement.removeAttribute("data-dc-canvas"); - canvasStyleEl?.remove(); - canvasStyleEl = null; - } - } - window.addEventListener("message", (e) => { - const type = e.data && e.data.type; - if (type === "__dc_theme") { - const t = e.data.theme; - if (t === "light" || t === "dark") { - appTheme = t; - applyCanvasBg(); - } - return; - } - if (!designDocMode || type !== "__dc_probe") return; - postDesignMode(designDocMode); - }); - function compile(node) { - const raw = [...node.children]; - const helmetClosed = node.nextSibling != null || node.parentNode?.nextSibling != null; - if (node.hasAttribute("data-dc-atomics") && !mounted.has("__dc-atomics")) { - mounted.add("__dc-atomics"); - const el = doc.createElement("style"); - el.id = "__dc-atomics"; - el.textContent = ATOMIC_CSS; - doc.head.appendChild(el); - } - return (_vals, ctx) => { - const name = ctx && ctx.__name || ""; - const streaming = !!(name && isStreaming(name)); - for (let i = 0; i < raw.length; i++) { - const child = raw[i]; - const tag = child.tagName; - const mayBePartial = streaming && !helmetClosed && i === raw.length - 1; - if (tag === "SCRIPT") { - if (mayBePartial) continue; - const key = "SCRIPT|" + (child.getAttribute("src") || child.textContent || ""); - if (mounted.has(key)) continue; - mounted.add(key); - const el = doc.createElement("script"); - for (const { name: an, value } of [...child.attributes]) - el.setAttribute(an, value); - if (child.textContent) el.textContent = child.textContent; - doc.head.appendChild(el); - } else if (tag === "LINK" || tag === "META") { - if (mayBePartial) continue; - const key = tag + "|" + (child.getAttribute("href") || child.getAttribute("src") || child.outerHTML); - if (mounted.has(key)) continue; - mounted.add(key); - if (tag === "LINK") { - const rel = (child.getAttribute("rel") || "").toLowerCase().split(/\s+/); - const href = (child.getAttribute("href") || "").trim(); - const res = window.__resources; - const pre = res && rel.includes("stylesheet") && !rel.includes("alternate") ? res[href] : void 0; - const blob = typeof pre === "string" && pre ? bundledBlob(pre) : null; - if (blob) { - const el = doc.createElement("style"); - if (child.hasAttribute("disabled")) { - el.setAttribute("media", "not all"); - } else if (child.getAttribute("media")) { - el.setAttribute("media", child.getAttribute("media")); - } - if (child.getAttribute("title")) - el.setAttribute("title", child.getAttribute("title")); - void blob.text().then((css) => { - el.textContent = css; - }); - doc.head.appendChild(el); - continue; - } - } - doc.head.appendChild(child.cloneNode(true)); - } else { - const key = name + "|" + i; - let el = live.get(key); - if (!el || el.tagName !== tag) { - if (el) el.remove(); - el = doc.createElement(tag.toLowerCase()); - live.set(key, el); - doc.head.appendChild(el); - } - for (const { name: an, value } of [...child.attributes]) { - if (el.getAttribute(an) !== value) el.setAttribute(an, value); - } - if (el.textContent !== child.textContent) - el.textContent = child.textContent; - } - } - return null; - }; - } - return { compile, setDesignDocMode }; - } - - // src/pseudo.ts - function createPseudoSheet(doc) { - let el = null; - const cache = /* @__PURE__ */ new Map(); - let n = 0; - return (pseudo, css) => { - const k = pseudo + "|" + css; - const hit = cache.get(k); - if (hit) return hit; - if (!el) { - el = doc.createElement("style"); - doc.head.appendChild(el); - } - const cls = "scp" + (n++).toString(36); - const sel = pseudo === "before" || pseudo === "after" ? "." + cls + "::" + pseudo : "." + cls + ":" + pseudo; - el.sheet.insertRule(sel + "{" + css + "}", el.sheet.cssRules.length); - cache.set(k, cls); - return cls; - }; - } - - // src/registry.ts - function createRegistry() { - const entries = /* @__PURE__ */ Object.create(null); - function get(name) { - return entries[name] || (entries[name] = { - html: "", - tpl: null, - Logic: null, - jsStreaming: false, - htmlStreaming: false, - ver: 0, - subs: /* @__PURE__ */ new Set(), - fetched: false - }); - } - function bump(name) { - const r = get(name); - r.ver++; - for (const fn of r.subs) fn(); - } - return { - entries, - get, - bump, - bumpAll() { - for (const n in entries) bump(n); - } - }; - } - - // src/runtime.ts - var COMPONENT_DIR = "."; - function createRuntime(doc = document) { - const registry = createRegistry(); - const pseudoClass = createPseudoSheet(doc); - const helmet = createHelmetManager( - doc, - (name) => registry.get(name).htmlStreaming - ); - const external = createExternalModules(() => registry.bumpAll()); - const factory = createComponentFactory(registry, ensureFetched); - const host = { - component: (name) => factory.getDC(name), - placeholder: (props) => h(Placeholder, props), - helmet: (node) => helmet.compile(node), - loadExternal: (kind, url, after) => external.load(kind, url, after), - resolveExternal: (url, name) => external.resolve(url, name), - resolveExternalGlobal: (url, name) => external.resolveGlobal(url, name), - resolveExternalError: (url, name) => external.getError(url, name), - pseudoClass - }; - function ensureFetched(name) { - const r = registry.get(name); - if (r.fetched) return; - r.fetched = true; - const url = COMPONENT_DIR + "/" + encodeURIComponent(name) + ".dc.html"; - const res = window.__resources; - const pre = res ? res[url] : void 0; - const target = typeof pre === "string" && pre ? pre : url; - const blob = bundledBlob(target); - (blob ? blob.text() : fetch(target).then((res2) => { - if (!res2.ok) { - console.error( - '[dc-runtime] sibling fetch for "' + name + '" failed:', - url, - "returned", - res2.status, - "\u2014 the reference renders as an empty placeholder." - ); - return ""; - } - return res2.text(); - })).then((t) => { - if (!t) return; - const parsed = parseDcText(t); - if (!parsed) { - console.error( - '[dc-runtime] sibling fetch for "' + name + '":', - url, - "has no block \u2014 not a Design Component." - ); - return; - } - if (parsed.props) r.propsMeta = parsed.props; - if (parsed.preview) r.preview = parsed.preview; - if (parsed.template && !r.html) updateHtml(name, parsed.template); - if (parsed.js && !r.Logic) updateJs(name, parsed.js); - }).catch( - (e) => console.error( - '[dc-runtime] sibling fetch for "' + name + '" threw:', - url, - e - ) - ); - } - let rootName = null; - function updateHtml(name, html) { - const r = registry.get(name); - r.html = html; - if (name === rootName) { - const mode = DESIGN_DOC_MODE_RE.exec(html)?.[1] ?? null; - if (mode || !r.htmlStreaming) helmet.setDesignDocMode(mode); - } - try { - r.tpl = compileTemplate(html, host); - } catch (e) { - console.error("[dc-runtime] template compile FAILED for", name, e); - } - registry.bump(name); - } - function updateJs(name, src) { - const r = registry.get(name); - const seq = r.jsSeq = (r.jsSeq || 0) + 1; - try { - const Cls = evalDcLogic(src); - if (r.jsSeq !== seq) return; - if (typeof Cls !== "function") { - r.logicError = name + ".dc.html: - - - - - - - - - - - - - -
- -
- - - -
- -
- -
-
-
-
-
-
- -
- -
- {{ statusLabel }} - - {{ channelLabel }} - -
- - {{ timer }} - -
- -
- -
- - - - -
{{ initials }}
-
-
-
{{ peerName }}
-
{{ subCaption }}
-
-
- - - - -
- - Длительность {{ finalDuration }} -
-
-
-
- - -
-
{{ statusIcon }}
-
-
{{ statusTitle }}
-
{{ statusCaption }}
-
-
-
-
- - -
-
- - Отклонить -
-
- - Принять -
-
-
- - -
-
-
- - Микрофон -
-
- - Динамик -
-
-
- -
-
-
- - -
- - Отменить -
-
- - -
- - -
-
- - -
- -
-
- - -
- - -
-
- - -
-
- - Восстанавливаем связь… -
-
- -
-
-
- - -
- - -
-
- - -
- - Отменить -
-
- -
-
- -
Полноэкранный аудиозвонок · тёмная тема, живая осциллограмма собеседника. Переключайте состояния выше.
- -
-
- - - diff --git a/design/baseline/# UI для онлайн звонка/Публичная страница звонка.dc.html b/design/baseline/# UI для онлайн звонка/Публичная страница звонка.dc.html deleted file mode 100644 index 2db593b..0000000 --- a/design/baseline/# UI для онлайн звонка/Публичная страница звонка.dc.html +++ /dev/null @@ -1,135 +0,0 @@ - - - - - - - - - - - - - - - - - - - -
- -
-
E
- Chatballs -
- Защищённое соединение -
- -
-
- -
-
-
- - -
{{ initials }}
-
-
{{ orgName }}
-
приглашает вас на видеозвонок
-
- -
-
-
{{ agentInitials }}
-
-
{{ agentName }}
-
{{ agentRole }} · {{ orgName }}
-
-
- Онлайн -
- -
-
Готовы присоединиться?
-
Проверьте камеру и микрофон перед входом. Мы не записываем видео без предупреждения.
-
- -
-
- - Микрофон -
-
- - Камера -
-
- -
- - -
- -
- - Звонок защищён сквозным шифрованием -
-
-
-
- -
Работает на платформе Chatballs
- -
-
- - - diff --git a/design/baseline/# UI для онлайн звонка/Экран звонка.dc.html b/design/baseline/# UI для онлайн звонка/Экран звонка.dc.html deleted file mode 100644 index 55b9635..0000000 --- a/design/baseline/# UI для онлайн звонка/Экран звонка.dc.html +++ /dev/null @@ -1,410 +0,0 @@ - - - - - - - - - - - - - - - - - - - -
- -
-
Экран звонка
-
Встраиваемый компонент видеозвонка оператора и клиента — для модалки или страницы Hub. Полный жизненный цикл вызова.
-
- -
-
-
- Сторона -
- - -
-
-
- Основные состояния -
- - - -
-
-
-
- Ошибки и завершение -
- - - -
-
- -
- Собеседник - - -
-
-
- -
- -
-
{{ peerInitials }}
-
-
- {{ peerName }} - - - -
- {{ subtitle }} -
-
- -
- - {{ timer }} -
-
- -
- -
- - - - - - -
-
- - - - -
{{ mainInitials }}
-
-
-
{{ mainName }}
-
{{ mainCaption }}
-
-
-
- - -
- - Связь прервана · восстанавливаем соединение… -
-
- - -
-
{{ statusIcon }}
-
-
{{ statusTitle }}
-
{{ statusCaption }}
-
- -
- - - -
-
-
-
- - -
- - - - {{ peerName }} -
-
- - -
- - - - -
-
ВЫ
-
-
- Вы -
-
- -
- - -
-
- - Отклонить -
-
- - Принять -
-
-
- - -
- - Отменить -
-
- - -
-
-
- - Микрофон -
-
- - Камера -
-
-
- - -
-
-
- - -
- - - - -
-
- -
- -
Переключайте сторону и состояние выше. В активном звонке доступны тумблеры «Собеседник», чтобы проверить индикаторы удалённого микрофона и камеры.
- -
-
- - - diff --git a/design/baseline/Chatballs Baseline v2/README.md b/design/baseline/Chatballs Baseline v2/README.md deleted file mode 100644 index 0d5b13a..0000000 --- a/design/baseline/Chatballs Baseline v2/README.md +++ /dev/null @@ -1,195 +0,0 @@ -# Chatbolls — design baseline (SPEC-HUB-0031) - -Два макета: чат сотрудника (роль EMPLOYEE) и оболочка владельца/админа (OWNER = ADMIN). Основания: SPEC-HUB-0031 (§3–6, §8.1, §9), ADR-HUB-0002/0003 (режимы диалога, перехват), ADR-HUB-0041/0043 (контакт-центр, группы, ответственный, видимость). - -## Как открыть - -Любой `*.dc.html` открывается в браузере напрямую (нужен `support.js` рядом). Тема и акцент задаются через Tweaks (`theme`, `accent`) или правкой `default` в `data-props`. - -## Состав - -``` -Чат сотрудника Baseline.dc.html — EMPLOYEE: кадры H, A–G, S1–S2, M1–M2 -Админ Baseline.dc.html — OWNER/ADMIN: кадры A1–A2 -Настройки Baseline.dc.html — OWNER/ADMIN: кадры N1–N7, M -Профиль Baseline.dc.html — все роли: кадры P1–P2, M -Контакты Baseline.dc.html — OWNER/ADMIN: кадры K1–K5, S1 -Агенты Baseline.dc.html — OWNER/ADMIN: кадры G1–G5, S1 -Сотрудники Baseline.dc.html — OWNER/ADMIN: кадры E1–E10 -Порталы Baseline.dc.html — OWNER/ADMIN: кадры PT1–PT8 -_ds/ — токены дизайн-системы -assets/logo-mark.svg — знак -assets/av-1..4.png — аватары (Дмитрий Орлов · Анна Ким · Сергей Крылов · Елена Кузнецова) -support.js — рантайм макетов -``` - -## Админ Baseline — карта кадров - -- **A1 Владелец · Чат** — стартовый экран. Сайдбар: логотип + переключатель организации; навигация ровно 6 пунктов плоским списком (Чат с бейджем очереди · Контакты · Агенты · Сотрудники · Доска · Настройки); блок «Запуск» (3 шага, автоотметка, прогресс, primary на текущий шаг, «Скрыть»); профиль с ролью внизу. В списке диалогов заголовок — **переключатель охвата** (поповер: Все диалоги · Группы · Агенты · Ответственный) — дерево из сайдбара сотрудника у админа переезжает сюда, чтобы навигация осталась плоской (§4.1). Inline-`Hint` первого визита под вкладками. -- **A2 Админ · рабочий режим** — «Запуск» завершён и скрыт, подсказка закрыта, охват «Операторы». От владельца отличается только подписью роли (§3). - -## Настройки Baseline — карта кадров - -Оболочка та же (сайдбар 220, «Настройки» активен). Вместо сплошной ленты из девяти секций — **субменю разделов 240px** и один раздел на экране; ниже субменю — переходы на отдельные экраны «База знаний» и «Аудит действий». Контент 820px, табличные разделы — 1000px. - -- **N1 Организация** — логотип (загрузить/убрать), название, часовой пояс, валюта; «Сохранить» с датой последнего сохранения. -- **N2 Группы** — строки: цветная точка · имя · состав · диалоги · изменить/удалить; создание — одно поле внизу списка. Состав группы — на карточке сотрудника. -- **N3 AI-провайдер** — таблица: название · секрет (маска) · конфиг (endpoint + модель) · статус · «Проверить» · ⋯. Primary «Добавить провайдера» в шапке раздела. -- **N4 Интеграции** — таблица: название (фирменная плитка) · агент · статус · действия. Ошибка почты — красный бейдж и точка-маркер у пункта субменю. -- **N5 Голосовые и звонки** — матрица точка входа × (Голосовые · Аудиозвонки · Видеозвонки), переключатели сохраняются сразу; почта — «недоступно», выключенная точка входа приглушена. -- **N6 Хранилище файлов** — сегмент «На диске установки / Внешнее S3», поля S3 (секреты маской), статус проверки/переноса, «Перенести · Проверить доступ · Сохранить». -- **N7 Демо-данные** — статус, счётчик записей, учётки демо-сотрудников (email/пароль моноширинно), «Удалить демо-данные» danger-outline. -- **M** — мобильный: список разделов ↔ раздел; поля 44px, primary в липком футере. - -### Решения по настройкам - -1. **Профиль — не настройки.** Тема, акцент, аватар, пароль, 2FA, сессии — отдельная страница «Профиль» из меню пользователя (`SidebarUserMenu`). Из «Настроек» убран; SPEC-HUB-0031 §8.6 в части «Профиль» требует уточнения. -2. **Субменю вместо ленты** — отклонение от §8.6 («один экран с вертикальными секциями») по решению владельца: девять секций не помещаются на экран. -3. **OWNER == ADMIN** (§3): макет один для обеих ролей, никаких `canManage`-разветвлений в UI. -4. Порядок разделов и их состав — как в `features/settings/SettingsPage.tsx`; таблицы — `table-card`/`baseline-table`, матрица — `communication-matrix`. - -## Профиль Baseline — карта кадров - -Отдельная страница из меню пользователя (`SidebarUserMenu` → «Профиль»). Шапка: аватар 72 · имя + бейдж роли · email, дата вступления, группы · «Выйти». Две колонки: слева «кто я и как выглядит интерфейс» (Личные данные + Фото · Внешний вид · Уведомления в мессенджер), справа «безопасность» (Смена пароля · 2FA · Активные сессии). - -- **P1 Владелец** — меню пользователя открыто (Уведомления · Профиль · Выйти), навигация видна; Telegram привязан с типами событий (ADR-0015), MAX — «Привязать бота»; TOTP включена; три сессии, текущая отмечена. -- **P2 Сотрудник** — сайдбар без навигации (§3): логотип, организация, профиль. Та же страница; TOTP выключена — жёлтое предупреждение. -- **M** — мобильный: личные данные и внешний вид сразу; уведомления, пароль, 2FA, сессии — список подэкранов. - -Решения: тема — сегмент Светлая / Тёмная / Как в системе (§7.2); акцент — 4 пресета из `shared/appearance.ts` + HEX (§7.3); всё на странице принадлежит учётной записи, а не организации (ADR-0029) — владелец не меняет это у других. Поле «Роль» из формы убрано — роль показана бейджем в шапке. - -## Контакты Baseline — карта кадров - -Источник — `features/sales/clients` и `client-detail` (раздел в коде всё ещё называется `salesClients`). Из текущей таблицы убраны колонка «Продукты» и фильтр по продуктам (ADR-0041, §8.2 «без коммерции»), добавлен фильтр по агенту. Из карточки убраны вкладка «Согласия» (заглушка в коде) и блок «Связанные продукты»; добавлены «Ответственный» и «Заметка» — те же поля, что в контекст-панели чата. - -- **K1 Список** — колонки: Контакт (аватар · имя · CID) · Как связаться (телефон/email + логин) · Каналы (плитки-глифы) · Последний диалог (точка режима · кто ведёт · когда, сортируемая) · Открытые · ⋯. Фильтры: поиск, Каналы, Агенты, чип «С открытым диалогом»; «Экспорт CSV»; пагинация в футере карточки. -- **K2 Список · фильтр** — чип активен, счётчик «6 из 128», «Сбросить». -- **K3 Карточка · Обзор** — шапка (аватар 64 · имя · CID · карандаш · описание · телефон/email/компания/город с «копировать» · бейджи каналов · primary «Открыть диалог»); `UnderlineTabs` Обзор · Диалоги · Идентификаторы · Аудит; сводка 4 числа · лента активности; справа — текущий диалог, ответственный, заметка. -- **K4 Карточка · Диалоги** — тема + превью · агент · канал · группа · статус · дата; клик — в чат. -- **K5 Карточка · Идентификаторы** — идентичности по подключениям со статусом подтверждения; справа — «Возможный дубликат» с «Сравнить и объединить» (ручное объединение с причиной и аудитом, ADR-0006). -- **S1 Пустой список** — «Контакты появятся, когда клиенты напишут вашему агенту» + «Настроить подключения». - -Мобильной версии нет — §9: админ-разделы с телефона в первой итерации не адаптируются. Вкладка «Аудит» — таблица Время · Действие · Объект · Актор · Результат как в коде, отдельно не перерисована. - -## Агенты Baseline — карта кадров - -Источник — `features/agents` (AgentsPage, AgentDetailPage). Агент = канал + AI-конфигурация (ADR-0041 §4); изменения применяются сразу, без релизов (ADR-0023). Вертикальная лента секций из кода пересобрана в две колонки: слева «что он знает и как говорит» (Инструкции · Знания), справа «как работает» (Назначение · Модель · Подключения). - -- **G1 Список** — агент (плитка-бот в цвете агента · имя · код) · группа · статус (AI отвечает / Без AI / Выключен) · модель · подключения (плитки с красным маркером ошибки) · открытые · ⋯. Колонка «Модель» добавлена к §8.3. -- **G2 Создание** — модалка одного шага: название + группа (§4.3). -- **G3 Карточка · Консультант** — рабочий агент. Шапка: плитка · имя · статус · группа · код · открытые · «Остановить AI». Инструкции — три поля (кто он · как говорит · правила). Знания — список с чипами «Выключено» / «Портал» и кнопкой «Выбрать». Подключения: привязанные со статусом и «Отвязать», свободные с «Привязать». -- **G4 Карточка · Поддержка сайта** — виджет привязан: блок «Код вставки виджета» с «Копировать» и подсказкой «вставьте перед » (§8.3); почта в ошибке. -- **G5 Карточка · новый агент** — blocker «провайдер не выбран» с переходом в настройки (ADR-0017), плейсхолдеры инструкций, строка «несохранённые изменения — применятся сразу», «Запустить AI». -- **S1 Пустой список** — «Агент отвечает клиентам первым. Создайте первого» + «Создать агента» (§5.2). - -Решения: лимит дневной стоимости (`dailyCostUsd`, ADR-0023) вынесен в блок «Модель» — в коде поля нет, требует реализации. «Выключить агента» / «Удалить» — вторичные кнопки внизу карточки, как в коде. - -## Порталы Baseline — карта кадров - -Источник — `features/support-portals` + SPEC-HUB-0028 (§1.1–1.3, §3, §6). Переделаны три больные точки: модалка настроек, публичная ссылка и редактор статьи с файлами. - -- **PT1 Список** — таблица: портал (плитка-глобус · имя · язык/дата) · **публичный адрес** (моношрифт + «копировать» + бейдж «свой домен») · статус · материалы (разделы · статьи) · обновлён · ⋯. Меню строки открыто: Открыть материалы · Настройки портала · Копировать ссылку · Открыть портал · Перенести в архив. Архивный портал приглушён и уходит в конец. Поиск и фильтр статуса в шапке карточки, счётчик и пагинация в футере. -- **PT2 Пусто** — «Создайте первый портал поддержки» + «Создать портал» и «Как это выглядит у клиента»; создание доступно всегда (§1.1). -- **PT3 Карточка · материалы** — шапка: хлебные крошки, имя, статус, «6 разделов · 48 статей»; **блок публичного адреса** — глобус в цвете статуса, домен, бейдж «свой домен · подтверждён», кнопки «копировать» и «открыть», рядом резервный hosted-адрес. Действия: «Настройки» (переход на PT4, не модалка), primary «Новая статья», ⋯ (публикация/архив). Ниже — библиотека без вкладок: дерево разделов 260px с вложенностью и счётчиками + тулбар (поиск, язык, статус, «Импорт статей») + таблица Статья · Раздел · Язык · Редакция · Статус · Обновлено · ⋯ (скрепка = файлы в статье). -- **PT4 Настройки · Основные** — субменю 250px (Основные · Адрес и домен · Оформление · Веб-виджет · Публикация и архив), контент 820px: название, **адрес на этой установке** (поле `{portalKey}` + суффикс базового домена инсталляции), основной язык, карточка «Итоговая ссылка» с «Копировать/Открыть». -- **PT5 Настройки · Адрес и домен** — поле домена + статус «Домен ведёт сюда · проверено», одна A-запись на IP установки с копированием, «Сохранить домен · Проверить DNS», «Отключить домен» danger-outline. -- **PT6 Настройки · Оформление** — две темы из каталога установки (`features/help-center/themes/`: `classic` «Классическая», `ocean` «Ocean») карточками с мини-превью на цветах их `manifest.preview`; сегмент схемы Светлая / Тёмная / Как в системе (`manifest.schemes`), «Предпросмотр портала», дата сохранения. Названия и описания тем в UI не придумываются — только из манифеста; отсутствующая в сборке тема показывается как недоступная. -- **PT7 Редактор статьи** — полноэкранный, без модалок. Шапка: «Материалы» назад, крошки, бейдж «черновая редакция 8 · не опубликована», сегмент Текст / Вместе / Просмотр, «Сохранить черновик», primary «Опубликовать». Тело: слева заголовок-инпут + адрес + панель Markdown (H1/H2 · B/I/ссылка/код · списки/цитата/таблица · изображение/файл) + монополе текста + статусная строка (знаки, позиция курсора, автосохранение); в центре живой предпросмотр в теме портала («Как у посетителя»); справа рейка 300px: раздел, язык, краткое описание, **файлы статьи** (drop-зона + список с «вставить в текст» и «копировать Markdown»), **версии** (неизменяемые, опубликованная отмечена). -- **PT8 Редактор · перетаскивание файла** — drop-оверлей поверх текста: «Отпустите, чтобы вставить в текст», подпись про типы и лимит 25 МБ; в рейке новый файл с прогрессом загрузки. - -### Решения по порталам - -1. **Настройки — страница, не модалка.** `Modal width={960}` с пятью секциями подряд заменён маршрутом `/portals/{id}/settings` с субменю разделов; §1.2 в части «modal surface» требует уточнения. -2. **Публичная ссылка — постоянный элемент шапки**, а не текст `` в подзаголовке: копирование, открытие, статус домена и резервный hosted-адрес видны всегда, в том числе у черновика. -3. **Редактор статьи** — split-режим с предпросмотром GFM и панелью Markdown вместо `textarea rows={10}` в карточке. Версии и файлы — в рейке, публикация редакции — из того же экрана. -4. **Файлы статьи требуют API.** В коде вложений у статей портала нет (есть только у `ai/knowledge`): нужны `portals/{id}/articles/{articleId}/attachments/` с возвратом публичного URL для подстановки в Markdown. Импорт YAML остаётся отдельным действием «Импорт статей» в тулбаре. -5. **Hosted-адрес — домен установки, не домен продукта.** Продукт ставится на любой инфраструктуре, базовый домен берётся из `CHATBALLS_HELP_BASE_DOMAIN` (`support_portals/addressing.py`, `portal_views.py` → `address.baseDomain`). В макете он показан как «адрес на этой установке» с примером `help.nord-crm.ru` — в UI нельзя зашивать ни `help.example.com`, ни домен вендора; суффикс и итоговый URL всегда из ответа API. -6. **Никакого подтверждения владения доменом.** Продукт self-hosted: домен и установка у одного владельца — доказывать права самому себе нечего. Остаётся одна A-запись на IP установки и техническая проверка «ведёт ли домен сюда» (нужна для выписки TLS). TXT-запись `_chatballs.…`, `customDomainVerification` и `customDomainVerifiedAt` из UI убраны — их стоит выключить и в коде либо оставить только для мультитенантного SaaS-режима с общим ingress. -7. **Мобильной версии нет** — §9: админ-разделы с телефона в первой итерации не адаптируются. - -## База знаний Baseline — карта кадров - -Источник — `features/ai/knowledge` + SPEC-HUB-0026 §9 (иерархия категорий) и SPEC-HUB-0029 §7 (прикрепление к агенту). Раздел сверстан как карточка портала: та же шапка с состоянием, то же дерево слева, тот же split-редактор. Отделы упразднены ADR-HUB-0041, поэтому вместо `visibility` (`ORGANIZATION`/`DEPARTMENTS`) — состояние «участвует в ответах» и явный список агентов. - -- **KB1 Библиотека** — шапка: «База знаний», бейдж «Индекс актуален», «9 категорий · 84 знания · 1 240 фрагментов», действия «Импорт YAML» и primary «Добавить знание». Ниже — дерево категорий 260px со счётчиками и вложенностью + тулбар (поиск, «В ответах», «Агент») + таблица: чекбокс · знание (иконка типа · заголовок · описание · скрепка вложений) · категория (с родителем) · фрагменты · агенты · в ответах · обновлено (кто) · ⋯. Меню строки: Открыть · Редактировать · Прикрепить к агенту · Переместить · Выключить в ответах · Переиндексировать · Удалить. -- **KB2 Массовый выбор** — тёмная панель операций по центру снизу: «Выбрано 3» + Прикрепить к агенту (primary) · Переместить · Включить · Выключить · Удалить · снять выделение. Выбранные строки подсвечены акцентом. -- **KB3 Прикрепить к агенту** — диалог 560px: сегмент Прикрепить / Открепить, выбор **одного** агента (радио, у агента без AI — статус «без AI»), предупреждение «2 из 3 будут пропущены» с причиной по каждому, примечание про атомарность и `ai.manage`, primary «Прикрепить 1». -- **KB4 Карточка знания** — шапка как у портала: крошки «База знаний / Регламенты / Пошив», заголовок, бейдж «В ответах агента», чипы «12 фрагментов · переиндексировано» и «3 агента», автор правки; действия «Прикрепить к агенту», primary «Редактировать», ⋯. Тело: рендер содержимого (≤720px) + рейка 320px: категория, тумблер «Участвует в ответах», вложения с копированием публичной ссылки, агенты со статусами, блок «Индексация» с «Переиндексировать». -- **KB5 Редактор знания** — тот же полноэкранный split, что PT7: слева заголовок + панель Markdown + монополе + статусная строка (знаки, ≈фрагменты, автосохранение); в центре предпросмотр «внутренний материал» и «Как ответит агент»; справа рейка 300px: категория, краткое описание, тумблер ответов, вложения (drop-зона + список), агенты (только чтение). Primary — «Сохранить и переиндексировать». -- **KB6 Редактор · перетаскивание файла** — drop-оверлей «Отпустите, чтобы приложить к знанию» (файл = вложение + ссылка в тексте), в рейке загрузка с прогрессом, в шапке бейдж «Есть несохранённые правки». -- **KB7 Категории** — дерево строками с ручкой перетаскивания, счётчиками, инлайн-переименованием, «+ вложенная»; удаление недоступно у системной и непустой категории (тайтл объясняет причину), справа карточка правил и предупреждение о непустой категории. -- **KB8 Импорт YAML** — разбор до применения: файл, сводка «создать 5 / обновить 2», таблица документ · `categoryPath` · действие · замечание; недостающие уровни категорий создаются автоматически с пояснением в замечании; primary «Импортировать 7 документов». -- **KB9 Пустая категория** — «В категории «Обучение» пока нет знаний» + «Добавить знание» / «Импорт YAML»; счётчики не подменяются нулями из другой категории. - -### Решения по базе знаний - -1. **Раздел вынесен в сайдбар** — пункт «База знаний» сразу после «Порталов»: внутренние материалы и публичные порталы — соседние сущности, а не вложенность в «Агентов». -2. **`visibility` заменена состоянием «участвует в ответах»** — после ADR-0041 отделов нет, скрывать знание от части организации нечем. Доступность = прикреплённые агенты + флаг участия; в UI это одна колонка и один тумблер вместо матрицы отделов. -3. **Фрагменты показываются как цифра, а не абстракция** — в строке и карточке видно число фрагментов и состояние пересборки: это единственный способ объяснить, почему агент отвечает старым текстом. -4. **Прикрепление — только массовым действием** (SPEC-0029 §6: операция атомарна и журналируется). В карточке и редакторе список агентов — только чтение, изменение уходит в диалог KB3, который честно показывает пропуски. -5. **Импорт разбирается до применения.** Спецификация запрещает неявное создание категорий, поэтому ошибка пути должна быть видна до записи — таблица предпросмотра с построчным действием. -6. **Вложения знания уже есть в API** (`ai/knowledge` attachments), в отличие от статей портала — поэтому drop-зона в редакторе знания реализуема без новых эндпоинтов. - -## Сайдбар — актуально по коду (`layout/Sidebar.tsx`) - -Во всех макетах обновлено 2026-09-06: - -- **Знак Chatballs** — инлайн-SVG из `shared/icons.tsx` (`LogoIcon`): два круга и перемычка с вырезами сверху и снизу, 26px, `currentColor` по `--c-n1`, без подложки. Файл `assets/logo-mark.svg` больше не используется. -- **Пункты** — Чат (badge очереди) · Контакты · Агенты · Сотрудники · **Порталы** · **База знаний** · Настройки. Пункт «Доска» из ранних макетов — ошибка: в коде это `supportPortals` с иконкой `globe`. -- **Иконки** — ровно из `Icon`: `message` · `user` · `robot` · `team` · `globe` · `settings`, stroke-width 1.8. -- **Ширина тянется** — `pane-resizer` на правом краю: невидимая полоса 6px, подсветка акцентом 2px при наведении и перетаскивании; 180–320px, по умолчанию 220, запоминается в браузере; двойной клик — сброс. На ≤1024px ручка скрыта. -- **Шапка** — 56px: знак + переключатель организации (32px, имя + шеврон). -- **Рейка 60px** — только ≤1024px: знак · чат · «развернуть» · аватар; полный сайдбар выезжает поверх контента (кадр E7). -- **Блок «Запуск»** — три шага с автоотметкой, прогресс, primary текущего шага, «×» — скрыть (localStorage). -- **Меню пользователя** — аватар · имя + email · точка непрочитанных + ⋯ (Уведомления · Профиль · Выйти). -- У сотрудника вместо навигации — дерево «Диалоги» (Все · Группы · Агенты); на странице «Профиль» сайдбар без навигации вообще (кадр P2). - -## Сотрудники Baseline — карта кадров - -Источник — `features/employees`. Роли: OWNER = ADMIN по правам, EMPLOYEE — только чат; группа задаёт только видимость диалогов (ADR-0043). - -- **E1 Список** — таблица сотрудник · роль · должность · группы · доступ · статус · последний вход; фильтры поиск/роль/группа, «Добавить сотрудника»; в футере счётчик и напоминание про группы. Здесь же показан сайдбар целиком: блок «Запуск» и подсвеченная ручка ширины. -- **E2 Список · меню строки** — фильтр «Администратор»; действия: карточка · завершить сессии · сбросить пароль · заблокировать. Передачи владения в меню нет. -- **E3 Карточка сотрудника** — Основные данные · Должность и группы · Системная роль (три варианта, владелец замкнут) · Статус и безопасность · Опасная зона (блокировка); справа Учётная запись и Аудит. -- **E4 Карточка владельца** — «Только просмотр», баннер про правило роли, все роли замкнуты; единственное опасное действие — **Передача владения** в опасной зоне. -- **E5 Новый сотрудник** — выезжающая панель 520px, четыре шага: учётные данные · роль (Сотрудник / Администратор) · должность и группы · **пароль первичного доступа**: отправить на рабочую почту (по умолчанию) либо сгенерировать и показать. -- **E6 Новый сотрудник · пароль показать** — выбран второй вариант (на случай проблем с почтой); подпись футера меняется на «Пароль покажем сразу после создания». -- **E7 Пароль показан** — диалог после создания: пароль моноширинно, «Копировать», маскирование глазом, «Отправить на почту вместо этого», предупреждение о единственном показе, «Скопировал, закрыть». Действие в аудите. -- **E8 Сброс пароля** — тот же диалог для сброса владельцем или админом (из карточки сотрудника или меню строки): сессии завершены, при первом входе сотрудник задаёт свой пароль. -- **E9 Передача владения** — открывается только из опасной зоны карточки владельца. Кандидаты — только активные администраторы (чтобы передать сотруднику, его сначала делают админом): выбор, стрелка перехода, новая роль прежнего владельца, предупреждение, чекбокс подтверждения. -- **E10 Сайдбар · рейка 1024** — состояние ≤1024px. - -Решение по паролю: сервер отдаёт пароль в ответе один раз и в открытом виде не хранит; UI показывает его в диалоге с копированием. Требует поддержки в API (`employees/{id}/reset-password/` и создание сотрудника должны возвращать сгенерированный пароль при выборе режима «показать»). - -## Чат сотрудника — карта кадров - -- **A** — рабочий экран, диалог ведёт AI. Композер активен; подсказка над полем: отправка перехватит диалог. Primary «Взять диалог» в строке действий. -- **B** — диалог ждёт человека (AI передал). Фильтр «Ждут человека» активен, системное событие в ленте, подсказка над композером. -- **C** — ведёте вы. Строка действий: «Вернуть AI» · ⋯. Меню ⋯ открыто: Вернуть в очередь · Закрыть диалог · Пометить как спам · Удалить диалог. -- **D** — ведёт другой сотрудник. Композер заблокирован (замок, кто ведёт), вторичное действие «Взять диалог». -- **E** — закрытый диалог. Вкладка «Мои», звонки в карточке и ⋯ скрыты, композер заменён сообщением о закрытии. -- **F** — контекст-панель, вкладка «История»: диалоги контакта, текущий выделен. -- **G** — фильтр по группе: в сайдбаре выбрана «Поддержка», список отфильтрован. В контекст-панели открыто меню переноса в группу. -- **H** (первый кадр) — голосовые: в списке превью «Голосовое сообщение · 0:14» с микрофоном; в ленте плеер (play 30 · волна 2px · длительность) и три состояния расшифровки под волной: текст + «Расшифровка AI · Скрыть» · «Расшифровываем…» · кнопка «Расшифровать». Расшифровка доступна и для своих голосовых. Композер в режиме записи: корзина · красный таймер · живая волна · пауза · Отправить (Esc — отмена, Enter — отправить). -- **S1** — пустой список: «Диалоги появятся, когда клиенты напишут вашему агенту», без кнопки (сотрудник). -- **S2** — ширина 1024: сайдбар сворачивается в рейку 60px, контекст-панель — выезжающая, открывается кнопкой «Контакт» в шапке ленты. -- **M1 / M2** — мобильный: список ↔ диалог. Контакт — по кнопке в шапке. - -## Зафиксированные решения - -1. **Сайдбар 220px с деревом «Диалоги».** У сотрудника нет разделов (§3), поэтому сайдбар — это фильтр списка: Все диалоги · Группы (с цветной точкой и счётчиком) · Агенты. Внизу — профиль. Верхней панели нет. На 1024 сворачивается в рейку. -2. **Одно действие взятия.** «Взять диалог» одинаково для очереди и AI — атомарный перехват (ADR-0003). Композер активен всегда: первое отправленное сообщение выполняет тот же перехват; над полем — одна строка-предупреждение. -3. **Фильтры.** Статусы — `UnderlineTabs` над списком (Все · Мои · Ждут оператора; закрытые попадают в Все/Мои); Группа и Агент — пункты дерева в сайдбаре (v1 чипы под поиском убраны). Заголовок списка = выбранный пункт дерева. -4. **Строка диалога** (ориентир — Chatwoot): иконка канала (Telegram/MAX — фирменные глифы из `shared/icons.tsx`, Email/Web — линейные) + имя агента в одну строку серым (`--c-n4`) · индикатор приоритета справа → имя · время → превью (↩ если последнее сообщение наше) · непрочитанные → бейджи: таймер ожидания (иконка + время, без слова «ждёт»), группа, метки. Активная строка — фон `--c-n9`, без акцентной полосы. -4a. **Сообщения**: у клиента (слева) аватара нет — он в шапке; фон входящих `--c-msg-in` (светлее/темнее подложки по теме), без обводки. Исходящие справа: аватар AI/сотрудника (в диалоге по очереди могут отвечать AI и разные люди), фон — акцент 22% на подложке ленты, AI — `--c-ai` 16%. -5. **Карточка контакта**: аватар 64 · канал · имя + карандаш · описание · поля с иконками и «копировать» (мессенджер/почта, телефон, компания, город). Кнопки «Позвонить» · «Видеозвонок» — в карточке контакта под полями (не в шапке ленты). Блок «Диалог»: Ответственный, Группа, Приоритет (Высокий/Средний/Низкий/Не задан, индикатор-столбики) — полноширинные селекты, Агент и Режим — read-only в две колонки, Метки — чипы с «+ Добавить», Начат. Заметка — жёлтая карточка. -6. **Метки и Приоритет диалога** — по образцу конкурентов; в SPEC-HUB-0031 этих полей нет. Требует решения: добавить в модель или убрать из макета. -6a. Агент везде подписан иконкой бота в цвете агента (Консультант — фиолетовый, Поддержка сайта — бирюзовый). Скроллбары скрыты. -7. **Ссылка «Открыть профиль контакта»** сотруднику не показывается — раздела «Контакты» у него нет. -8. Подсказки `Hint` сотруднику не показываются (§6). -9. Композер: поле сверху, строка инструментов снизу (эмодзи · вложение · голосовое · Шаблоны «/») и кнопка отправки с ⏎. - -## Технические примечания - -- Инлайн-стили поверх переменных темы `--c-*`, задаваемых на корне из Tweaks: **Тема** (light/dark) и **Акцент** (синий · индиго · бирюзовый · оранжевый). Полутона (фон акцента, предупреждений, AI) считаются через `color-mix`, поэтому одинаково работают в обеих темах. Данные — нейтральные примеры («Ателье Норд»). -- Статусные цвета режимов: AI `--ai`, ждёт `--warning`, человек `--primary`, закрыт `--n-5` — как в текущем `conversations/data.ts`. diff --git a/design/baseline/Chatballs Baseline v2/_ds/styles.css b/design/baseline/Chatballs Baseline v2/_ds/styles.css deleted file mode 100644 index 0cd17b1..0000000 --- a/design/baseline/Chatballs Baseline v2/_ds/styles.css +++ /dev/null @@ -1,4 +0,0 @@ -@import "./tokens/colors.css"; -@import "./tokens/typography.css"; -@import "./tokens/spacing.css"; -@import "./tokens/effects.css"; diff --git a/design/baseline/Chatballs Baseline v2/_ds/tokens/colors.css b/design/baseline/Chatballs Baseline v2/_ds/tokens/colors.css deleted file mode 100644 index 9044d34..0000000 --- a/design/baseline/Chatballs Baseline v2/_ds/tokens/colors.css +++ /dev/null @@ -1,88 +0,0 @@ -/* Chatballs — color tokens - Source: packages/ui/src/theme.ts (Ant Design ThemeConfig), apps/internal-ui/src/styles/base.css, - and design/baseline/*.dc.html (approved HTML baseline). Ant Design's default light palette, - customized with a single accent (#1677ff) per ADR-HUB-0013. */ - -:root { - /* ---- base neutrals (Ant Design neutral scale as used in the baseline) ---- */ - --n-1: #1f1f1f; /* page/heading titles */ - --n-2: #262626; /* primary body text */ - --n-3: #595959; /* secondary text */ - --n-4: #8c8c8c; /* tertiary text / captions */ - --n-5: #bfbfbf; /* disabled / placeholder / faint */ - --n-6: #d9d9d9; /* strong border, control border */ - --n-7: #e8e8e8; /* input border */ - --n-8: #f0f0f0; /* hairline border */ - --n-9: #f5f5f5; /* hover surface */ - --n-10: #fafafa; /* table header surface */ - --n-11: #f0f2f5; /* app/page background */ - --n-white: #ffffff; - - /* ---- primary (blue-6 / blue-7) ---- */ - --primary: #1677ff; - --primary-hover: #0958d9; - --primary-bg: #e6f4ff; - --primary-border: #91caff; - --primary-shadow: rgba(22, 119, 255, 0.3); - - /* ---- semantic: success / warning / error / info ---- */ - --success: #52c41a; - --success-text: #389e0d; - --success-bg: #f6ffed; - --success-bg-strong: #f1f9ec; - --success-border: #b7eb8f; - - --warning: #faad14; - --warning-text: #d48806; - --warning-bg: #fffbe6; - --warning-bg-strong: #fff7e6; - --warning-border: #ffe58f; - - --error: #ff4d4f; - --error-text: #cf1322; - --error-bg: #fff2f0; - --error-border: #ffccc7; - - --info: #1677ff; - - /* ---- role accents: AI vs human ---- */ - --ai: #722ed1; - --ai-light: #9254de; - --ai-lighter: #b37feb; - --ai-bg: #f9f0ff; - --human: #1677ff; - - /* ---- channel accents (MAX / Telegram / Web Chat) ---- */ - --channel-max: #6b5be0; - --channel-max-bg: #f2f0ff; - --channel-telegram: #2f8fd0; - --channel-telegram-bg: #eaf6fd; - --channel-webchat: #0f9b8e; - --channel-webchat-bg: #e8f7f4; - - /* ---- product accents (as tagged in tables/lists) ---- */ - --product-firepage: #0958d9; - --product-firepage-bg: #e6f4ff; - --product-foxray: #722ed1; - --product-foxray-bg: #f9f0ff; - - /* ---- surfaces ---- */ - --surface-page: var(--n-11); - --surface-card: var(--n-white); - --surface-sunken: #fafafa; - --surface-chip: #f7f8fa; - - /* ---- text aliases ---- */ - --text-heading: var(--n-1); - --text-body: var(--n-2); - --text-secondary: var(--n-3); - --text-tertiary: var(--n-4); - --text-disabled: var(--n-5); - --text-inverse: var(--n-white); - --text-link: var(--primary); - - /* ---- borders ---- */ - --border-default: var(--n-8); - --border-input: var(--n-7); - --border-strong: var(--n-6); -} diff --git a/design/baseline/Chatballs Baseline v2/_ds/tokens/effects.css b/design/baseline/Chatballs Baseline v2/_ds/tokens/effects.css deleted file mode 100644 index cd157ec..0000000 --- a/design/baseline/Chatballs Baseline v2/_ds/tokens/effects.css +++ /dev/null @@ -1,40 +0,0 @@ -/* Chatballs — radii, shadows, motion. "Премиальный минимализм": no gradients, - no glassmorphism/blur, no neon — shadows stay soft and near-black at low opacity. */ - -:root { - --radius-xs: 5px; - --radius-sm: 7px; - --radius-md: 8px; - --radius-lg: 9px; - --radius-xl: 10px; - --radius-2xl: 12px; - --radius-3xl: 13px; - --radius-4xl: 14px; - --radius-pill: 999px; - --radius-circle: 50%; - - --shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.03); - --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.04); - --shadow-md: 0 2px 12px rgba(0, 0, 0, 0.05); - --shadow-lg: 0 6px 20px rgba(0, 0, 0, 0.12); - --shadow-sticky: 0 -4px 16px rgba(0, 0, 0, 0.04); - --shadow-primary: 0 1px 2px rgba(22, 119, 255, 0.3); - - --ease-standard: cubic-bezier(0.4, 0, 0.2, 1); /* @kind other */ - --duration-fast: 0.15s; /* @kind other */ - --duration-base: 0.2s; /* @kind other */ -} - -@keyframes hub-typing { - 0%, 80%, 100% { opacity: 0.25; transform: translateY(0); } - 40% { opacity: 1; transform: translateY(-2px); } -} - -@keyframes hub-spin { - to { transform: rotate(360deg); } -} - -@keyframes hub-pulse { - 0%, 100% { opacity: 1; } - 50% { opacity: 0.5; } -} diff --git a/design/baseline/Chatballs Baseline v2/_ds/tokens/spacing.css b/design/baseline/Chatballs Baseline v2/_ds/tokens/spacing.css deleted file mode 100644 index ba5ca9f..0000000 --- a/design/baseline/Chatballs Baseline v2/_ds/tokens/spacing.css +++ /dev/null @@ -1,36 +0,0 @@ -/* Chatballs — spacing tokens. Compact-enterprise density (ADR-HUB-0013): - "большие пустые поля и oversized-карточки запрещены". Values are the exact - paddings/gaps observed in the baseline, not snapped to a rounder grid. */ - -:root { - --space-1: 2px; - --space-2: 4px; - --space-3: 6px; - --space-4: 8px; - --space-5: 9px; - --space-6: 10px; - --space-7: 11px; - --space-8: 12px; - --space-9: 14px; - --space-10: 16px; - --space-11: 18px; - --space-12: 20px; - --space-13: 22px; - --space-14: 24px; - --space-15: 28px; - --space-16: 32px; - --space-17: 36px; - - /* control heights */ - --h-control-sm: 30px; - --h-control-md: 34px; - --h-control: 36px; - --h-control-lg: 42px; - --h-control-xl: 44px; - --h-control-2xl: 48px; - --h-topbar: 56px; - - /* layout */ - --sidebar-width: 248px; - --content-max: 1340px; -} diff --git a/design/baseline/Chatballs Baseline v2/_ds/tokens/typography.css b/design/baseline/Chatballs Baseline v2/_ds/tokens/typography.css deleted file mode 100644 index 119e53e..0000000 --- a/design/baseline/Chatballs Baseline v2/_ds/tokens/typography.css +++ /dev/null @@ -1,42 +0,0 @@ -/* Chatballs — typography tokens - The internal Hub uses the Ant Design default system font stack — no bespoke webfont - is loaded (see ADR-HUB-0013: "используется системный font stack Ant Design без - обязательной внешней зависимости от загружаемого шрифта"). Technical identifiers - (order/release/event IDs, emails) use a monospace stack. */ - -:root { - --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; - --font-mono: "SF Mono", ui-monospace, Menlo, Consolas, monospace; - - /* type scale, taken verbatim from the baseline (px, not snapped to a 4/8 grid) */ - --text-2xl: 26px; /* page hero title (Command Center) */ - --text-xl: 24px; /* page title */ - --text-lg: 20px; /* auth title */ - --text-md: 18px; /* card title */ - --text-base-lg: 16px; - --text-base: 14.5px; /* buttons, primary body */ - --text-sm-lg: 13.5px; - --text-sm: 13px; /* table body */ - --text-sm-tight: 12.5px; - --text-xs: 12px; /* meta */ - --text-xs-tight: 11.5px; - --text-2xs: 11px; /* table header / eyebrow */ - --text-2xs-tight: 10.5px; - - --weight-regular: 400; - --weight-medium: 500; - --weight-semibold: 600; - --weight-bold: 700; - --weight-black: 800; - - --tracking-tight: -0.02em; /* large headings */ - --tracking-tight-sm: -0.01em; - --tracking-wide: 0.03em; /* table header eyebrows */ - --tracking-wider: 0.06em; /* nav section labels */ - - --leading-tight: 1.15; - --leading-snug: 1.2; - --leading-normal: 1.4; - --leading-relaxed: 1.5; - --leading-loose: 1.55; -} diff --git a/design/baseline/Chatballs Baseline v2/assets/av-1.png b/design/baseline/Chatballs Baseline v2/assets/av-1.png deleted file mode 100644 index 716ee3cb041dc85a440a54ea79fbfe177218acce..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 58566 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