@yadsh/dsh-qa-surface 0.7.0 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -16572,8 +16572,19 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16572
16572
  }
16573
16573
  //#endregion
16574
16574
  //#region src/client/components/QaChangelog.tsx
16575
- const QA_VERSION = "0.7.0";
16575
+ const QA_VERSION = "0.7.1";
16576
16576
  const QA_CHANGELOG = [
16577
+ {
16578
+ version: "0.7.1",
16579
+ date: "2026-09-16",
16580
+ sections: [{
16581
+ title: "Исправления",
16582
+ items: ["В консоли администратора открываются все разделы, а не только «Обзор»: «Пользователи», «Саброли», «Общие возможности», «Навыки», «Разговоры», «Очередь разбора», «Обратная связь», «Аналитика» и «Аудит». Раньше переход в любой раздел закрывал консоль и возвращал в чат, а ссылка на конкретный разговор или сообщение вместо разбора открывала обычный чат. Каждый такой переход ещё и оставлял в стенде пустой чат, попадавший в счётчики разговоров; переходы внутри консоли чатов больше не создают."]
16583
+ }, {
16584
+ title: "Прочее",
16585
+ items: ["Внутренняя доработка для плагинов стенда: карточка плагина в настройках DSH теперь может узнать, вошёл ли пользователь в QA, и работать с его подключениями от его имени — на этом подключения к рабочим сервисам показываются и в списке плагинов. Интерфейс QA это не меняет: вход, чаты и настройки работают как раньше, а без входа карточка подключений ничего не показывает."]
16586
+ }]
16587
+ },
16577
16588
  {
16578
16589
  version: "0.7.0",
16579
16590
  date: "2026-09-16",
@@ -16779,7 +16790,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16779
16790
  className: "dsh-qa-changelog__date",
16780
16791
  children: entry.date
16781
16792
  }),
16782
- entry.version === "0.7.0" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
16793
+ entry.version === "0.7.1" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
16783
16794
  className: "dsh-qa-changelog__current",
16784
16795
  children: "текущая"
16785
16796
  }) : null
@@ -19967,9 +19978,25 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19967
19978
  }
19968
19979
  //#endregion
19969
19980
  //#region src/client/admin/routes.ts
19981
+ /** The console's own base path under the surface route. */
19982
+ function adminBasePath(routePath) {
19983
+ return `${routePath === "/" ? "" : routePath}/admin`;
19984
+ }
19985
+ /**
19986
+ * Whether a pathname addresses the console at all. Sections and entities each
19987
+ * own a path under the base ({@link adminPath}), and the surface hosting the
19988
+ * console must stay the console while any of them is open, so the test is by
19989
+ * prefix. An equality test against the base alone unmounts the console on the
19990
+ * first navigation inside it, and every deep link lands in the chat instead.
19991
+ */
19992
+ function isAdminPath(pathname, routePath) {
19993
+ const base = adminBasePath(routePath);
19994
+ const trimmed = pathname.replace(/\/+$/u, "");
19995
+ return trimmed === base || trimmed.startsWith(`${base}/`);
19996
+ }
19970
19997
  /** Path segments after `/qa/admin`, without the leading slash. */
19971
19998
  function adminSegments(pathname, routePath) {
19972
- const base = `${routePath === "/" ? "" : routePath}/admin`;
19999
+ const base = adminBasePath(routePath);
19973
20000
  return (pathname.startsWith(base) ? pathname.slice(base.length) : "").split("/").map((segment) => segment.trim()).filter((segment) => segment !== "");
19974
20001
  }
19975
20002
  /**
@@ -20009,7 +20036,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
20009
20036
  }
20010
20037
  /** The pathname for one console route; the inverse of {@link parseAdminRoute}. */
20011
20038
  function adminPath(routePath, route, search = "") {
20012
- return `${`${routePath === "/" ? "" : routePath}/admin`}${(() => {
20039
+ return `${adminBasePath(routePath)}${(() => {
20013
20040
  switch (route.page) {
20014
20041
  case "overview": return "";
20015
20042
  case "users": return "/users";
@@ -22799,7 +22826,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
22799
22826
  const accounts = props.accounts;
22800
22827
  const accountsSnapshot = (0, react.useSyncExternalStore)(accounts?.subscribe ?? noopSubscribe$1, accounts?.getSnapshot ?? noopAccountsSnapshot, accounts?.getSnapshot ?? noopAccountsSnapshot);
22801
22828
  const accountsStage = accountsSnapshot.stage;
22802
- const adminRoute = route.pathname.replace(/\/+$/u, "") === `${config.route.path === "/" ? "" : config.route.path}/admin`;
22829
+ const adminRoute = isAdminPath(route.pathname, config.route.path);
22803
22830
  const [access, setAccess] = (0, react.useState)();
22804
22831
  const [selectedSubrole, setSelectedSubrole] = (0, react.useState)(null);
22805
22832
  const [adminPreview, setAdminPreview] = (0, react.useState)(false);
@@ -26590,6 +26617,64 @@ textarea.qa-card-control{height:auto;padding:8px 12px;line-height:1.5;resize:ver
26590
26617
  }
26591
26618
  };
26592
26619
  //#endregion
26620
+ //#region src/client/settings-extensions/user-session.ts
26621
+ const CHECKING = Object.freeze({
26622
+ stage: "checking",
26623
+ token: null
26624
+ });
26625
+ const ANONYMOUS = Object.freeze({
26626
+ stage: "anonymous",
26627
+ token: null
26628
+ });
26629
+ /**
26630
+ * Publishes the account state of the controller the QA overlay mounts. The
26631
+ * service lives for the whole host page while the controller is rebuilt
26632
+ * whenever the remote wiring re-injects, so {@link attach} swaps the source and
26633
+ * republishes; an unattached mirror stays in `checking`, which is what a card
26634
+ * that mounts before the accounts subsystem sees.
26635
+ */
26636
+ var QaUserSessionMirror = class {
26637
+ controller;
26638
+ unsubscribeSource;
26639
+ snapshot = CHECKING;
26640
+ listeners = /* @__PURE__ */ new Set();
26641
+ /** Bind the controller whose account state this view publishes. */
26642
+ attach(controller) {
26643
+ this.unsubscribeSource?.();
26644
+ this.unsubscribeSource = void 0;
26645
+ this.controller = controller;
26646
+ this.unsubscribeSource = controller?.subscribe(() => {
26647
+ this.publish();
26648
+ });
26649
+ this.publish();
26650
+ }
26651
+ dispose() {
26652
+ this.attach(void 0);
26653
+ this.listeners.clear();
26654
+ }
26655
+ getSnapshot = () => this.snapshot;
26656
+ subscribe = (listener) => {
26657
+ this.listeners.add(listener);
26658
+ return () => this.listeners.delete(listener);
26659
+ };
26660
+ publish() {
26661
+ const next = this.derive();
26662
+ if (next.stage === this.snapshot.stage && next.token === this.snapshot.token) return;
26663
+ this.snapshot = next;
26664
+ for (const listener of [...this.listeners]) listener();
26665
+ }
26666
+ derive() {
26667
+ const state = this.controller?.getSnapshot();
26668
+ const token = this.controller?.token() ?? null;
26669
+ if (state?.stage === "authed" && token !== null) return Object.freeze({
26670
+ stage: "authed",
26671
+ token
26672
+ });
26673
+ if (state === void 0 || state.stage === "checking") return CHECKING;
26674
+ return ANONYMOUS;
26675
+ }
26676
+ };
26677
+ //#endregion
26593
26678
  //#region src/client/index.tsx
26594
26679
  const QA_WELCOME_SLOT_ID = "welcome-notice";
26595
26680
  function qaWelcomeStorageKey(config) {
@@ -26607,6 +26692,7 @@ textarea.qa-card-control{height:auto;padding:8px 12px;line-height:1.5;resize:ver
26607
26692
  async function apply(ctx) {
26608
26693
  const panels = new QaSurfacePanelRegistry();
26609
26694
  const settingsSections = new QaUserSettingsSectionRegistry();
26695
+ const userSession = new QaUserSessionMirror();
26610
26696
  ctx.effect(() => {
26611
26697
  const removeService = ctx.provide("qaSurfacePanels", panels);
26612
26698
  return () => {
@@ -26621,6 +26707,13 @@ textarea.qa-card-control{height:auto;padding:8px 12px;line-height:1.5;resize:ver
26621
26707
  removeService();
26622
26708
  };
26623
26709
  }, "dsh-qa-surface: user settings extension service");
26710
+ ctx.effect(() => {
26711
+ const removeService = ctx.provide("qaUserSession", userSession);
26712
+ return () => {
26713
+ userSession.dispose();
26714
+ removeService();
26715
+ };
26716
+ }, "dsh-qa-surface: user session service");
26624
26717
  const disposeRemote = await ctx.remote.$mount(TYPERT_REMOTE);
26625
26718
  await ctx.inject([
26626
26719
  "remote.qaSurface",
@@ -26700,6 +26793,7 @@ textarea.qa-card-control{height:auto;padding:8px 12px;line-height:1.5;resize:ver
26700
26793
  new QaChatIndex(window.localStorage, qaStorageNamespace(snapshot)).forgetChat(sessionId);
26701
26794
  }
26702
26795
  });
26796
+ userSession.attach(accounts);
26703
26797
  const route = new QaRouteController();
26704
26798
  const settingsScope = ctx.settingsScope.bind({ namespace: QA_SURFACE_SETTINGS_NAMESPACE });
26705
26799
  const config = new QaConfigController(settingsScope, async () => {