synabun 2.1.0 → 2.2.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/CHANGELOG.md +139 -0
- package/README.md +1 -1
- package/docs/whatsapp.md +11 -11
- package/lib/client-config-repair.js +13 -9
- package/lib/external-tools.js +6 -5
- package/lib/update-snapshot.js +77 -3
- package/lib/virustotal.mjs +12 -1
- package/mcp-server/dist/index.js +1 -1
- package/mcp-server/dist/index.js.map +1 -1
- package/mcp-server/dist/services/budget-judgments.d.ts +1 -1
- package/mcp-server/dist/services/budget-judgments.d.ts.map +1 -1
- package/mcp-server/dist/services/budget-judgments.js +5 -3
- package/mcp-server/dist/services/budget-judgments.js.map +1 -1
- package/mcp-server/dist/services/identity.d.ts +2 -2
- package/mcp-server/dist/services/identity.js +2 -2
- package/mcp-server/dist/services/memory-sessions.d.ts +1 -1
- package/mcp-server/dist/services/memory-sessions.d.ts.map +1 -1
- package/mcp-server/dist/services/memory-sessions.js +28 -2
- package/mcp-server/dist/services/memory-sessions.js.map +1 -1
- package/mcp-server/dist/services/neural-interface.d.ts +2 -1
- package/mcp-server/dist/services/neural-interface.d.ts.map +1 -1
- package/mcp-server/dist/services/neural-interface.js.map +1 -1
- package/mcp-server/dist/services/server-instructions.d.ts.map +1 -1
- package/mcp-server/dist/services/server-instructions.js +2 -1
- package/mcp-server/dist/services/server-instructions.js.map +1 -1
- package/mcp-server/dist/services/tool-catalog.js +1 -1
- package/mcp-server/dist/services/tool-catalog.js.map +1 -1
- package/mcp-server/dist/services/tool-list.d.ts +1 -1
- package/mcp-server/dist/services/tool-list.d.ts.map +1 -1
- package/mcp-server/dist/services/tool-runtime.d.ts +2 -2
- package/mcp-server/dist/services/tool-runtime.d.ts.map +1 -1
- package/mcp-server/dist/services/tool-runtime.js +1 -1
- package/mcp-server/dist/services/tool-runtime.js.map +1 -1
- package/mcp-server/dist/services/typesafe-config.js +1 -1
- package/mcp-server/dist/services/typesafe-config.js.map +1 -1
- package/mcp-server/dist/tools/agents.d.ts +8 -8
- package/mcp-server/dist/tools/agents.d.ts.map +1 -1
- package/mcp-server/dist/tools/agents.js +10 -9
- package/mcp-server/dist/tools/agents.js.map +1 -1
- package/mcp-server/dist/tools/choice.d.ts +6 -0
- package/mcp-server/dist/tools/choice.d.ts.map +1 -1
- package/mcp-server/dist/tools/choice.js +18 -13
- package/mcp-server/dist/tools/choice.js.map +1 -1
- package/mcp-server/dist/tools/git-tools.js +2 -2
- package/mcp-server/dist/tools/git-tools.js.map +1 -1
- package/mcp-server/dist/tools/morelogin.d.ts.map +1 -1
- package/mcp-server/dist/tools/morelogin.js +9 -6
- package/mcp-server/dist/tools/morelogin.js.map +1 -1
- package/mcp-server/dist/tools/skill-tools.d.ts +8 -8
- package/mcp-server/dist/tools/skill-tools.d.ts.map +1 -1
- package/mcp-server/dist/tools/skill-tools.js +11 -8
- package/mcp-server/dist/tools/skill-tools.js.map +1 -1
- package/mcp-server/package-lock.json +2 -2
- package/mcp-server/package.json +1 -1
- package/mcp-server/src/index.ts +1 -1
- package/mcp-server/src/services/budget-judgments.ts +5 -3
- package/mcp-server/src/services/identity.ts +2 -2
- package/mcp-server/src/services/memory-sessions.ts +24 -3
- package/mcp-server/src/services/neural-interface.ts +2 -1
- package/mcp-server/src/services/server-instructions.ts +2 -1
- package/mcp-server/src/services/tool-catalog.ts +1 -1
- package/mcp-server/src/services/tool-list.ts +1 -1
- package/mcp-server/src/services/tool-runtime.ts +2 -2
- package/mcp-server/src/services/typesafe-config.ts +1 -1
- package/mcp-server/src/tools/agents.ts +10 -9
- package/mcp-server/src/tools/choice.ts +17 -12
- package/mcp-server/src/tools/git-tools.ts +2 -2
- package/mcp-server/src/tools/morelogin.ts +6 -4
- package/mcp-server/src/tools/skill-tools.ts +11 -8
- package/neural-interface/i18n/en.json +504 -39
- package/neural-interface/i18n/pt-BR.json +504 -39
- package/neural-interface/lib/agent-sandbox-elevation.js +6 -0
- package/neural-interface/lib/antigravity/acp-client-host.js +554 -0
- package/neural-interface/lib/antigravity/acp-connection.js +299 -0
- package/neural-interface/lib/antigravity/bypass.js +100 -0
- package/neural-interface/lib/antigravity/catalog.js +273 -0
- package/neural-interface/lib/antigravity/home.js +260 -0
- package/neural-interface/lib/antigravity/migrate-gemini.js +139 -0
- package/neural-interface/lib/antigravity/panel-bridge.js +1058 -0
- package/neural-interface/lib/antigravity/policy-hook.mjs +78 -0
- package/neural-interface/lib/antigravity/policy.js +74 -0
- package/neural-interface/lib/antigravity/tool-call.js +178 -0
- package/neural-interface/lib/antigravity/transcripts.js +252 -0
- package/neural-interface/lib/antigravity/usage.js +43 -0
- package/neural-interface/lib/assistant-api.js +1 -0
- package/neural-interface/lib/assistant-brains/antigravity.js +423 -0
- package/neural-interface/lib/assistant-brains/codex.js +6 -1
- package/neural-interface/lib/assistant-budget-planner.js +7 -3
- package/neural-interface/lib/assistant-budget-split.js +42 -15
- package/neural-interface/lib/assistant-catalog.js +76 -9
- package/neural-interface/lib/assistant-config.js +6 -3
- package/neural-interface/lib/assistant-dispatch.js +211 -50
- package/neural-interface/lib/assistant-envelope.js +153 -1
- package/neural-interface/lib/assistant-evidence.js +80 -0
- package/neural-interface/lib/assistant-hidden-models.js +7 -5
- package/neural-interface/lib/assistant-persona.js +16 -11
- package/neural-interface/lib/assistant-plan-permissions.js +15 -0
- package/neural-interface/lib/assistant-router.js +237 -35
- package/neural-interface/lib/assistant-runtime.js +158 -12
- package/neural-interface/lib/assistant-task-prompt.js +10 -8
- package/neural-interface/lib/assistant-tools.js +2 -2
- package/neural-interface/lib/assistant-usage.js +29 -0
- package/neural-interface/lib/backup-service.js +6 -1
- package/neural-interface/lib/browser-guard-cdp.js +5 -4
- package/neural-interface/lib/browser-network-guard.js +274 -82
- package/neural-interface/lib/browser-tool-policy.js +12 -7
- package/neural-interface/lib/claude-agent-bridge.js +7 -0
- package/neural-interface/lib/claude-history-cache.js +210 -0
- package/neural-interface/lib/claude-model-catalog.js +5 -1
- package/neural-interface/lib/cli-install-api.js +1 -1
- package/neural-interface/lib/cli-install.js +160 -31
- package/neural-interface/lib/cli-resolver.js +9 -2
- package/neural-interface/lib/cli-update-planner.js +25 -15
- package/neural-interface/lib/codex-rollout-reader.js +52 -0
- package/neural-interface/lib/codex-worker-app-server.js +25 -6
- package/neural-interface/lib/editor-git-api.js +3 -20
- package/neural-interface/lib/exec-loop-command.js +9 -2
- package/neural-interface/lib/file-explorer-api.js +74 -40
- package/neural-interface/lib/file-explorer-watch.js +9 -3
- package/neural-interface/lib/git-bash/install.js +52 -6
- package/neural-interface/lib/git-bash/manager.js +59 -17
- package/neural-interface/lib/git-bash/pins.js +12 -0
- package/neural-interface/lib/git-read.js +2 -2
- package/neural-interface/lib/git-status-runner.js +390 -0
- package/neural-interface/lib/git-status-watchdog.js +40 -0
- package/neural-interface/lib/greeting/budgets.js +1 -0
- package/neural-interface/lib/greeting/engine.js +2 -2
- package/neural-interface/lib/greeting/sources.js +10 -12
- package/neural-interface/lib/greeting/writer.js +1 -1
- package/neural-interface/lib/mcp-client-config.js +37 -6
- package/neural-interface/lib/mcp-external-servers.js +40 -7
- package/neural-interface/lib/mcp-installer.js +2 -1
- package/neural-interface/lib/memory-session-capture.js +17 -3
- package/neural-interface/lib/morelogin.js +147 -25
- package/neural-interface/lib/native-loop-providers.js +423 -23
- package/neural-interface/lib/native-loop-runtime.js +23 -2
- package/neural-interface/lib/opencode-native-boundary.js +165 -22
- package/neural-interface/lib/owner-authority.js +1 -1
- package/neural-interface/lib/path-confine.js +12 -9
- package/neural-interface/lib/peer-identity.js +80 -6
- package/neural-interface/lib/pty-host/core.js +28 -1
- package/neural-interface/lib/pty-host/lifetime.js +12 -0
- package/neural-interface/lib/pty-host/manager.js +12 -1
- package/neural-interface/lib/remote-policy.js +1 -1
- package/neural-interface/lib/request-gate.js +3 -0
- package/neural-interface/lib/rulesets/api.js +6 -5
- package/neural-interface/lib/rulesets/fs-safe.js +1 -1
- package/neural-interface/lib/rulesets/installer.js +89 -10
- package/neural-interface/lib/rulesets/render.js +1 -1
- package/neural-interface/lib/session-cache-builder.js +64 -1
- package/neural-interface/lib/skills/catalog.js +5 -1
- package/neural-interface/lib/skills/creation-brief.js +1 -1
- package/neural-interface/lib/skills/creation-sources.js +19 -1
- package/neural-interface/lib/skills/deploy.js +9 -4
- package/neural-interface/lib/skills/draft-confinement.js +3 -1
- package/neural-interface/lib/skills/eval-confinement.js +3 -1
- package/neural-interface/lib/skills/eval-trace.js +16 -0
- package/neural-interface/lib/skills/export.js +5 -2
- package/neural-interface/lib/skills/known-bridge-digests.json +58 -4
- package/neural-interface/lib/skills/ownership.js +1 -1
- package/neural-interface/lib/skills/roots.js +10 -3
- package/neural-interface/lib/skills/runtime-checks.js +6 -3
- package/neural-interface/lib/skills/service.js +15 -7
- package/neural-interface/lib/skills/telemetry.js +6 -0
- package/neural-interface/lib/synabun-version.js +1 -1
- package/neural-interface/lib/system-activity.js +1 -1
- package/neural-interface/lib/whatsapp/brain.js +7 -3
- package/neural-interface/lib/whatsapp/cards.js +22 -9
- package/neural-interface/lib/whatsapp/config.js +1 -1
- package/neural-interface/lib/worker-confinement.js +77 -1
- package/neural-interface/lib/worker-question.js +74 -0
- package/neural-interface/lib/worker-resume.js +13 -4
- package/neural-interface/lib/worker-sandbox.js +46 -16
- package/neural-interface/package-lock.json +18 -2
- package/neural-interface/package.json +2 -1
- package/neural-interface/public/onboarding.html +1 -1
- package/neural-interface/public/shared/agent-runtime-options.js +89 -11
- package/neural-interface/public/shared/agy/agy-bypass.js +47 -0
- package/neural-interface/public/shared/agy/agy-client.js +266 -0
- package/neural-interface/public/shared/agy/agy-commands.js +120 -0
- package/neural-interface/public/shared/agy/agy-copy.js +326 -0
- package/neural-interface/public/shared/agy/agy-diff.js +161 -0
- package/neural-interface/public/shared/agy/agy-events.js +469 -0
- package/neural-interface/public/shared/agy/agy-greeting.js +26 -0
- package/neural-interface/public/shared/agy/agy-options.js +232 -0
- package/neural-interface/public/shared/agy/agy-panel.js +2543 -0
- package/neural-interface/public/shared/agy/agy-render.js +392 -0
- package/neural-interface/public/shared/agy/agy-setup.js +149 -0
- package/neural-interface/public/shared/agy/agy-styles.js +604 -0
- package/neural-interface/public/shared/agy/agy-tabs-logic.js +181 -0
- package/neural-interface/public/shared/api.js +4 -4
- package/neural-interface/public/shared/assistant/asst-agents-dock.js +3 -4
- package/neural-interface/public/shared/assistant/asst-api.js +15 -3
- package/neural-interface/public/shared/assistant/asst-brain-picker.js +56 -9
- package/neural-interface/public/shared/assistant/asst-budget.js +5 -4
- package/neural-interface/public/shared/assistant/asst-computer.js +26 -2
- package/neural-interface/public/shared/assistant/asst-control.js +48 -0
- package/neural-interface/public/shared/assistant/asst-hosts.js +16 -7
- package/neural-interface/public/shared/assistant/asst-limit-dock.js +6 -1
- package/neural-interface/public/shared/assistant/asst-limit-wait.js +38 -5
- package/neural-interface/public/shared/assistant/asst-motion-gate.js +80 -0
- package/neural-interface/public/shared/assistant/asst-panel.js +46 -9
- package/neural-interface/public/shared/assistant/asst-render.js +112 -18
- package/neural-interface/public/shared/assistant/asst-route.js +237 -57
- package/neural-interface/public/shared/assistant/asst-routes-editor.js +9 -8
- package/neural-interface/public/shared/assistant/asst-scene-styles.js +55 -23
- package/neural-interface/public/shared/assistant/asst-scenes.js +279 -80
- package/neural-interface/public/shared/assistant/asst-sidepanel-styles.js +4 -4
- package/neural-interface/public/shared/assistant/asst-sidepanel.js +51 -7
- package/neural-interface/public/shared/assistant/asst-slash.js +8 -2
- package/neural-interface/public/shared/assistant/asst-state.js +76 -9
- package/neural-interface/public/shared/assistant/asst-style-cache.js +44 -0
- package/neural-interface/public/shared/assistant/asst-styles.js +58 -4
- package/neural-interface/public/shared/assistant/asst-tool-kinds.js +34 -7
- package/neural-interface/public/shared/assistant/asst-usage.js +2 -1
- package/neural-interface/public/shared/cdx/cdx-output.js +26 -3
- package/neural-interface/public/shared/cdx/cdx-panel.js +14 -2
- package/neural-interface/public/shared/cdx/cdx-preview.js +12 -0
- package/neural-interface/public/shared/cdx/cdx-protocol.js +11 -10
- package/neural-interface/public/shared/cdx/cdx-render.js +10 -9
- package/neural-interface/public/shared/cdx/cdx-requests.js +11 -1
- package/neural-interface/public/shared/cdx/cdx-styles.js +4 -0
- package/neural-interface/public/shared/cdx/cdx-tabs.js +75 -5
- package/neural-interface/public/shared/cli-install-controller.js +218 -0
- package/neural-interface/public/shared/cli-install-view.js +7 -4
- package/neural-interface/public/shared/cli-status.js +9 -8
- package/neural-interface/public/shared/constants.js +3 -2
- package/neural-interface/public/shared/context-menu-model.js +1 -0
- package/neural-interface/public/shared/cp/cp-permissions.js +12 -0
- package/neural-interface/public/shared/cp/cp-restore.js +162 -0
- package/neural-interface/public/shared/cp/cp-tool-cards.js +10 -3
- package/neural-interface/public/shared/desktop-bridge.js +17 -0
- package/neural-interface/public/shared/desktop-titlebar-view.js +12 -0
- package/neural-interface/public/shared/desktop-titlebar.js +13 -4
- package/neural-interface/public/shared/desktop-update-view.js +1 -0
- package/neural-interface/public/shared/explorer/fx-api.js +4 -4
- package/neural-interface/public/shared/explorer/fx-links.js +3 -3
- package/neural-interface/public/shared/explorer/fx-logic.js +1 -1
- package/neural-interface/public/shared/explorer/fx-main.js +48 -16
- package/neural-interface/public/shared/explorer/fx-tree.js +1 -0
- package/neural-interface/public/shared/greeting/greeting-card.js +2 -2
- package/neural-interface/public/shared/greeting/greeting-view.js +1 -0
- package/neural-interface/public/shared/html-shell.js +21 -4
- package/neural-interface/public/shared/loading-teardown.js +34 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +6 -1
- package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +28 -7
- package/neural-interface/public/shared/ocp-v2/ocp-v2-rehydrate.js +13 -4
- package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +17 -1
- package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +6 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +101 -68
- package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +46 -3
- package/neural-interface/public/shared/provider-icons.js +4 -2
- package/neural-interface/public/shared/settings/settings-rules.js +2 -2
- package/neural-interface/public/shared/settings/settings-skill-folders-pane.js +1 -1
- package/neural-interface/public/shared/settings/settings-skill-folders.js +2 -2
- package/neural-interface/public/shared/showcase-privacy.js +6 -1
- package/neural-interface/public/shared/sidepanel-session-status.js +3 -3
- package/neural-interface/public/shared/skills/ss-creation.js +6 -3
- package/neural-interface/public/shared/skills/ss-dialog.js +5 -2
- package/neural-interface/public/shared/skills/ss-lint.js +9 -1
- package/neural-interface/public/shared/skills/ss-profiles.js +44 -6
- package/neural-interface/public/shared/skills/ss-review-view.js +2 -2
- package/neural-interface/public/shared/skills/studio/sx-actions.js +1 -1
- package/neural-interface/public/shared/skills/studio/sx-create-ai-view.js +1 -1
- package/neural-interface/public/shared/skills/studio/sx-create-ai.js +2 -1
- package/neural-interface/public/shared/skills/studio/sx-dom.js +2 -1
- package/neural-interface/public/shared/skills/studio/sx-fixes-view.js +359 -0
- package/neural-interface/public/shared/skills/studio/sx-fixes.js +327 -0
- package/neural-interface/public/shared/skills/studio/sx-inspector.js +1 -1
- package/neural-interface/public/shared/skills/studio/sx-install-flow.js +9 -6
- package/neural-interface/public/shared/skills/studio/sx-install.js +20 -3
- package/neural-interface/public/shared/skills/studio/sx-library-view.js +144 -35
- package/neural-interface/public/shared/skills/studio/sx-library.js +6 -2
- package/neural-interface/public/shared/skills/studio/sx-main.js +11 -2
- package/neural-interface/public/shared/skills/studio/sx-review-view.js +1 -1
- package/neural-interface/public/shared/skills/studio/sx-subject.js +7 -6
- package/neural-interface/public/shared/skills/studio/sx-workspace-view.js +6 -5
- package/neural-interface/public/shared/state.js +1 -1
- package/neural-interface/public/shared/storage-retry.js +47 -0
- package/neural-interface/public/shared/storage.js +25 -4
- package/neural-interface/public/shared/streaming-markdown.js +58 -0
- package/neural-interface/public/shared/styles.css +362 -35
- package/neural-interface/public/shared/synabun-mascot.js +27 -8
- package/neural-interface/public/shared/synabun-ticker.js +147 -12
- package/neural-interface/public/shared/text-cache.js +29 -0
- package/neural-interface/public/shared/tool-list-picker.js +2 -2
- package/neural-interface/public/shared/tool-list-view.js +2 -2
- package/neural-interface/public/shared/topright-overflow-logic.js +54 -0
- package/neural-interface/public/shared/ui-antigravity-panel.js +19 -0
- package/neural-interface/public/shared/ui-assistant.js +2 -0
- package/neural-interface/public/shared/ui-automation-studio.js +20 -12
- package/neural-interface/public/shared/ui-claude-panel.js +374 -134
- package/neural-interface/public/shared/ui-cli-banner.js +23 -167
- package/neural-interface/public/shared/ui-file-explorer.js +48 -6
- package/neural-interface/public/shared/ui-file-links.js +2 -2
- package/neural-interface/public/shared/ui-games.js +2 -2
- package/neural-interface/public/shared/ui-judgments.js +1 -1
- package/neural-interface/public/shared/ui-keybinds.js +8 -2
- package/neural-interface/public/shared/ui-link.js +3 -3
- package/neural-interface/public/shared/ui-loading.js +5 -4
- package/neural-interface/public/shared/ui-menubar.js +2 -2
- package/neural-interface/public/shared/ui-native-loop-router.js +4 -0
- package/neural-interface/public/shared/ui-navbar.js +45 -0
- package/neural-interface/public/shared/ui-notifications.js +1 -0
- package/neural-interface/public/shared/ui-resume.js +9 -7
- package/neural-interface/public/shared/ui-schedules-studio.js +41 -10
- package/neural-interface/public/shared/ui-scroll-follow.js +16 -5
- package/neural-interface/public/shared/ui-sessions.js +22 -12
- package/neural-interface/public/shared/ui-settings.js +90 -65
- package/neural-interface/public/shared/ui-sidepanel-session-entry.js +9 -2
- package/neural-interface/public/shared/ui-sidepanel-sessions.js +3 -3
- package/neural-interface/public/shared/ui-sidepanel-tray.js +48 -7
- package/neural-interface/public/shared/ui-sidepanel-windows.js +3 -3
- package/neural-interface/public/shared/ui-terminal.js +146 -10
- package/neural-interface/public/shared/ui-tool-updates.js +3 -3
- package/neural-interface/public/shared/ui-tooltip.js +71 -46
- package/neural-interface/public/shared/ui-topright-overflow.js +233 -0
- package/neural-interface/public/shared/ui-whiteboard.js +1 -1
- package/neural-interface/public/shared/ui-worker-end.js +1 -1
- package/neural-interface/public/shared/whatsapp/wa-view.js +2 -2
- package/neural-interface/public/shared/worker-questions.js +34 -0
- package/neural-interface/public/sidepanel-session.html +1 -1
- package/neural-interface/server.js +731 -466
- package/neural-interface/templates/rulesets/README.md +1 -1
- package/neural-interface/templates/rulesets/legacy-hashes.json +5 -5
- package/neural-interface/templates/rulesets/manifest.json +1 -1
- package/npm-shrinkwrap.json +18 -2
- package/package.json +2 -1
- package/skills/skill-forge/SKILL.md +4 -4
- package/skills/synabun/SKILL.md +1 -1
- package/skills/synabun/modules/schedule.md +3 -3
- /package/neural-interface/templates/rulesets/{gemini.md → antigravity.md} +0 -0
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════
|
|
2
|
+
// SynaBun Neural Interface — Skills Studio 2.0: the fix panel and "Fix safe issues"
|
|
3
|
+
// ═══════════════════════════════════════════
|
|
4
|
+
//
|
|
5
|
+
// A health pill of the Library (or of a workspace's head) opens the fix panel of its row: every problem in plain
|
|
6
|
+
// words, what each fix does, and its buttons (sx-fixes.js says which). The Library's attention notice offers
|
|
7
|
+
// "Fix safe issues…": every one-press repair is read first and listed (what will be added, which record archived),
|
|
8
|
+
// nothing is written until the owner applies that list, and what was done or not is said item by item.
|
|
9
|
+
// Every write is a Studio API call (X-SynaBun-UI): a library skill's text goes to its draft (PATCH /v2/drafts), an
|
|
10
|
+
// agent's file is saved over the revision it was read at, a second library record is archived (Restore brings it
|
|
11
|
+
// back). Install locations change only through Publish and the existing remove flows.
|
|
12
|
+
|
|
13
|
+
import { studioErrorText } from './sx-errors.js';
|
|
14
|
+
import { subjectOf } from './sx-subject.js';
|
|
15
|
+
import { issuesOf, planTextFix, safeFixCandidates, issueCounts, fingerprintOf } from './sx-fixes.js';
|
|
16
|
+
import { healthWords } from './sx-library-view.js';
|
|
17
|
+
import { HEAD } from './sx-docs.js';
|
|
18
|
+
import { createFrontmatter } from '../ss-frontmatter.js';
|
|
19
|
+
import { lintArtifact } from '../ss-lint.js';
|
|
20
|
+
import { html, setHTML, ICONS, healthIcon } from './sx-dom.js';
|
|
21
|
+
|
|
22
|
+
const YAML_MODULE = '/vendor/yaml/yaml-2.9.1.min.js';
|
|
23
|
+
let _fm = null;
|
|
24
|
+
const frontmatter = async () => (_fm ||= createFrontmatter(await import(YAML_MODULE)));
|
|
25
|
+
|
|
26
|
+
/** The most text repairs "Fix safe issues" reads in one go (each is one or two reads). */
|
|
27
|
+
export const MAX_TEXT_READS = 40;
|
|
28
|
+
|
|
29
|
+
/** What a row allows the fix panel to do. */
|
|
30
|
+
export function canOf(ctx, deleteKind) {
|
|
31
|
+
return (row) => {
|
|
32
|
+
const s = subjectOf(row, { guest: ctx.guest, caps: ctx.caps });
|
|
33
|
+
const library = s.mode === 'library' && !!row.skill?.skillId;
|
|
34
|
+
return {
|
|
35
|
+
edit: !ctx.guest && !row.binary && (library || (s.mode === 'inplace' && !!s.v1Id && row.kind === 'agent')),
|
|
36
|
+
open: true,
|
|
37
|
+
delete: !!deleteKind?.(ctx, row, s),
|
|
38
|
+
archive: !ctx.guest && (!ctx.capsKnown || ctx.caps.has('library-delete')),
|
|
39
|
+
dismiss: !ctx.guest,
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Read a row's head file and the safe repair of it, without writing anything.
|
|
46
|
+
* @returns {Promise<{ changes: object[], apply: () => Promise<void> } | null>} null: nothing safe to do
|
|
47
|
+
*/
|
|
48
|
+
export async function readTextFix(ctx, row) {
|
|
49
|
+
const { api } = ctx;
|
|
50
|
+
const s = subjectOf(row, { guest: ctx.guest, caps: ctx.caps });
|
|
51
|
+
if (s.mode === 'inplace' && s.v1Id && row.kind === 'agent') {
|
|
52
|
+
const got = await api.fetchSkillsArtifact(s.v1Id);
|
|
53
|
+
if (got.binary || got.readOnly) return null;
|
|
54
|
+
const plan = planTextFix({ raw: got.rawContent, diagnostics: got.diagnostics, type: 'agent', folderName: row.folderName });
|
|
55
|
+
if (!plan) return null;
|
|
56
|
+
return { changes: plan.changes, apply: () => api.saveSkillsArtifact(s.v1Id, plan.text, { expectedRevision: got.revision }) };
|
|
57
|
+
}
|
|
58
|
+
if (s.mode !== 'library' || !row.skill?.skillId) return null;
|
|
59
|
+
const skill = (await api.fetchSkill(row.skill.skillId)).skill;
|
|
60
|
+
let draft = null;
|
|
61
|
+
if (skill.pointers?.draft) {
|
|
62
|
+
try { draft = (await api.fetchSkillsDraft(skill.pointers.draft)).draft; } catch (err) { if (err?.status !== 404) throw err; }
|
|
63
|
+
}
|
|
64
|
+
const latest = skill.pointers?.latest || null;
|
|
65
|
+
const file = draft ? await api.fetchSkillsDraftFile(draft.draftId, HEAD) : latest ? await api.fetchSkillsRevisionFile(latest, HEAD) : null;
|
|
66
|
+
if (!file || typeof file.content !== 'string') return null;
|
|
67
|
+
const fm = await frontmatter();
|
|
68
|
+
const folderName = String(skill.name || row.folderName || '');
|
|
69
|
+
const lint = lintArtifact({ type: 'skill', parsed: fm.parse(file.content), folderName });
|
|
70
|
+
const plan = planTextFix({ raw: file.content, diagnostics: lint.all, type: 'skill', folderName });
|
|
71
|
+
if (!plan) return null;
|
|
72
|
+
return {
|
|
73
|
+
changes: plan.changes,
|
|
74
|
+
apply: async () => {
|
|
75
|
+
const target = draft || (await api.createSkillsDraft({ skillId: skill.skillId, baseRevisionId: latest }, { idempotencyKey: ctx.key('fix-draft') })).draft;
|
|
76
|
+
await api.patchSkillsDraft(target.draftId, { expectedEtag: target.etag, ops: [{ op: 'put', path: HEAD, content: plan.text }] });
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Archive the second library record of a skill (Restore in the Archived view brings it back). */
|
|
82
|
+
const archiveRecord = (ctx, row) => ctx.api.deleteLibrarySkill(row.skill.skillId, row.skill.etag ? { expectedEtag: row.skill.etag } : {});
|
|
83
|
+
|
|
84
|
+
/** The words of a step of the preview. */
|
|
85
|
+
function stepWords(ctx, step) {
|
|
86
|
+
const { t } = ctx;
|
|
87
|
+
if (step.kind === 'archive') return t('skillsStudio.fix.step.archive', { name: step.row.label, original: step.issue.health.name || '' });
|
|
88
|
+
return t('skillsStudio.fix.step.text', { name: step.row.label, added: step.changes.map((c) => c.added).join(' · ') });
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Say what was done (a notice kept above the list until OK, a toast, the live region). */
|
|
92
|
+
function report(ctx, done, failed) {
|
|
93
|
+
const { t, tp } = ctx;
|
|
94
|
+
const line = failed.length ? t('skillsStudio.fix.resultPartial', { done: done.length, failed: failed.length, first: failed[0] }) : tp('skillsStudio.fix.resultDone', done.length);
|
|
95
|
+
ctx.lib.fixNotice = { kind: failed.length ? 'warn' : 'info', line, lines: [...done, ...failed] };
|
|
96
|
+
ctx.toast(line, failed.length ? 'error' : 'info');
|
|
97
|
+
ctx.announce(line);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* "Fix safe issues…": read every candidate, list exactly what would change, apply only that list on the owner's word.
|
|
102
|
+
* @param {object} ctx @param {object[]} rows the Library's rows @param {Function} deleteKind
|
|
103
|
+
*/
|
|
104
|
+
export async function fixAllSafe(ctx, rows, deleteKind) {
|
|
105
|
+
const { t, tp } = ctx;
|
|
106
|
+
const { steps } = safeFixCandidates(rows, canOf(ctx, deleteKind));
|
|
107
|
+
const gen = ctx.gen();
|
|
108
|
+
ctx.toast(t('skillsStudio.fix.reading'));
|
|
109
|
+
const ready = []; const unreadable = [];
|
|
110
|
+
let reads = 0;
|
|
111
|
+
for (const step of steps) {
|
|
112
|
+
if (step.kind !== 'text') { ready.push(step); continue; }
|
|
113
|
+
if (reads++ >= MAX_TEXT_READS) break;
|
|
114
|
+
try {
|
|
115
|
+
const fix = await readTextFix(ctx, step.row);
|
|
116
|
+
if (fix) ready.push({ ...step, changes: fix.changes, apply: fix.apply });
|
|
117
|
+
} catch (err) { unreadable.push(`${step.row.label}: ${studioErrorText(t, err)}`); }
|
|
118
|
+
if (!ctx.alive(gen)) return;
|
|
119
|
+
}
|
|
120
|
+
if (!ctx.alive(gen)) return;
|
|
121
|
+
const list = document.createElement('ul');
|
|
122
|
+
list.className = 'sx-fix-preview';
|
|
123
|
+
for (const step of ready) {
|
|
124
|
+
const li = document.createElement('li');
|
|
125
|
+
li.className = 'sx-fix-preview-row';
|
|
126
|
+
li.textContent = stepWords(ctx, step);
|
|
127
|
+
list.append(li);
|
|
128
|
+
}
|
|
129
|
+
if (!ready.length) {
|
|
130
|
+
await ctx.ask({ title: t('skillsStudio.fix.previewTitle'), lines: [t('skillsStudio.fix.previewNothing'), ...unreadable], buttons: [{ id: 'ok', label: t('skillsStudio.dialog.close'), kind: 'primary' }], cancelId: 'ok' });
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
const answer = await ctx.ask({
|
|
134
|
+
title: t('skillsStudio.fix.previewTitle'),
|
|
135
|
+
lines: [t('skillsStudio.fix.previewIntro'), ...unreadable],
|
|
136
|
+
extra: list,
|
|
137
|
+
wide: true,
|
|
138
|
+
buttons: [{ id: 'cancel', label: t('skillsStudio.dialog.cancel') }, { id: 'apply', label: tp('skillsStudio.fix.apply', ready.length), kind: 'primary' }],
|
|
139
|
+
focus: 'apply',
|
|
140
|
+
});
|
|
141
|
+
if (answer !== 'apply' || !ctx.alive(gen)) return;
|
|
142
|
+
const done = []; const failed = [];
|
|
143
|
+
for (const step of ready) {
|
|
144
|
+
try {
|
|
145
|
+
if (step.kind === 'archive') await archiveRecord(ctx, step.row); else await step.apply();
|
|
146
|
+
done.push(stepWords(ctx, step));
|
|
147
|
+
} catch (err) { failed.push(`${step.row.label}: ${studioErrorText(t, err)}`); }
|
|
148
|
+
}
|
|
149
|
+
if (!ctx.alive(gen)) return;
|
|
150
|
+
report(ctx, done, failed);
|
|
151
|
+
await ctx.reload();
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/** One issue as a card of the panel: its word, what it means, how it is fixed, and its buttons. */
|
|
155
|
+
function issueCard(ctx, row, issue, index) {
|
|
156
|
+
const { t } = ctx;
|
|
157
|
+
const card = document.createElement('section');
|
|
158
|
+
card.className = `sx-fix-card sx-fix-card--${issue.sev}`;
|
|
159
|
+
card.setAttribute('aria-labelledby', `sx-fix-${index}-title`);
|
|
160
|
+
const head = document.createElement('div');
|
|
161
|
+
head.className = 'sx-fix-card-head';
|
|
162
|
+
setHTML(head, html`${healthIcon(issue.code)}<strong id="sx-fix-${index}-title">${healthWords(issue.health, t)}</strong><span class="sx-fix-mode sx-fix-mode--${issue.mode}">${issue.mode === 'auto' ? t('skillsStudio.fix.mode.auto') : t('skillsStudio.fix.mode.guided')}</span>`);
|
|
163
|
+
const explain = document.createElement('p');
|
|
164
|
+
explain.className = 'sx-fix-explain';
|
|
165
|
+
explain.textContent = explainOf(ctx, row, issue);
|
|
166
|
+
card.append(head, explain);
|
|
167
|
+
const bar = document.createElement('div');
|
|
168
|
+
bar.className = 'sx-fix-actions';
|
|
169
|
+
for (const action of issue.actions) {
|
|
170
|
+
const b = document.createElement('button');
|
|
171
|
+
b.type = 'button';
|
|
172
|
+
b.className = `sx-btn sx-btn--sm${action === 'dismiss' ? ' sx-btn--ghost' : action === 'delete' ? ' sx-btn--danger' : (action === 'auto-text' || action === 'archive-duplicate') ? ' sx-btn--primary' : ''}`;
|
|
173
|
+
b.dataset.fix = `${index}:${action}`;
|
|
174
|
+
b.textContent = t(`skillsStudio.fix.action.${action}`);
|
|
175
|
+
bar.append(b);
|
|
176
|
+
}
|
|
177
|
+
if (!issue.actions.length) {
|
|
178
|
+
const p = document.createElement('p');
|
|
179
|
+
p.className = 'sx-dim';
|
|
180
|
+
p.textContent = t('skillsStudio.fix.nothingHere');
|
|
181
|
+
bar.append(p);
|
|
182
|
+
}
|
|
183
|
+
card.append(bar);
|
|
184
|
+
return card;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/** What a problem means for this row, in plain words. */
|
|
188
|
+
export function explainOf(ctx, row, issue) {
|
|
189
|
+
const { t } = ctx;
|
|
190
|
+
const h = issue.health;
|
|
191
|
+
const runtime = h.runtime ? t(`skillsStudio.runtime.${h.runtime}`) : '';
|
|
192
|
+
switch (issue.code) {
|
|
193
|
+
case 'shadowed': return t('skillsStudio.fix.explain.shadowed', { runtime: runtime || '—' });
|
|
194
|
+
case 'twice': return t('skillsStudio.fix.explain.twice', { runtime: runtime || '—' });
|
|
195
|
+
case 'same-description': return t('skillsStudio.fix.explain.same-description', { names: (h.names || []).join(', ') });
|
|
196
|
+
case 'duplicate-record': return `${t('skillsStudio.fix.explain.duplicate-record', { original: h.name || '' })}${h.why ? ` ${t(`skillsStudio.fix.duplicateWhy.${h.why}`)}` : ''}`;
|
|
197
|
+
default: return t(`skillsStudio.fix.explain.${issue.code}`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* The fix panel of one row. Resolves when the owner is done (a fix applied, a place opened, or closed).
|
|
203
|
+
* @param {object} ctx @param {object} row @param {{ deleteKind: Function, bulkDelete: Function }} hooks
|
|
204
|
+
*/
|
|
205
|
+
export async function openFixPanel(ctx, row, { deleteKind, bulkDelete } = {}) {
|
|
206
|
+
const { t } = ctx;
|
|
207
|
+
const issues = issuesOf(row, canOf(ctx, deleteKind)(row));
|
|
208
|
+
const box = document.createElement('div');
|
|
209
|
+
box.className = 'sx-fix-panel';
|
|
210
|
+
if (!issues.length) {
|
|
211
|
+
const p = document.createElement('p');
|
|
212
|
+
p.textContent = t('skillsStudio.fix.none');
|
|
213
|
+
box.append(p);
|
|
214
|
+
}
|
|
215
|
+
issues.forEach((issue, i) => box.append(issueCard(ctx, row, issue, i)));
|
|
216
|
+
const answer = await ctx.ask({
|
|
217
|
+
title: t('skillsStudio.fix.title', { name: row.label }),
|
|
218
|
+
lines: issues.length ? [t('skillsStudio.fix.intro')] : [],
|
|
219
|
+
extra: box,
|
|
220
|
+
wide: true,
|
|
221
|
+
buttons: [{ id: 'close', label: t('skillsStudio.dialog.close') }],
|
|
222
|
+
cancelId: 'close',
|
|
223
|
+
onOpen: (ui) => box.addEventListener('click', (e) => { const b = e.target.closest?.('[data-fix]'); if (b) ui.done(b.dataset.fix); }),
|
|
224
|
+
});
|
|
225
|
+
if (!answer || answer === 'close') return;
|
|
226
|
+
const [index, action] = [Number(answer.split(':')[0]), answer.split(':').slice(1).join(':')];
|
|
227
|
+
const issue = issues[index];
|
|
228
|
+
if (!issue) return;
|
|
229
|
+
await runAction(ctx, row, issue, action, { bulkDelete });
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
async function runAction(ctx, row, issue, action, { bulkDelete }) {
|
|
233
|
+
const { t, tp } = ctx;
|
|
234
|
+
const gen = ctx.gen();
|
|
235
|
+
try {
|
|
236
|
+
switch (action) {
|
|
237
|
+
case 'dismiss':
|
|
238
|
+
ctx.dismissIssues([issue.fingerprint]);
|
|
239
|
+
ctx.toast(t('skillsStudio.fix.dismissed'));
|
|
240
|
+
return;
|
|
241
|
+
case 'open-edit': ctx.openRow(row.key, { tab: 'edit' }); return;
|
|
242
|
+
case 'open-publish': ctx.openRow(row.key, { tab: 'publish' }); return;
|
|
243
|
+
case 'open-update': ctx.openRow(row.key); return;
|
|
244
|
+
case 'keep-mine':
|
|
245
|
+
ctx.keepUpdate(row.folderName, row.update?.upstreamRevisionId);
|
|
246
|
+
ctx.toast(t('skillsStudio.toast.keptMine', { name: row.folderName }));
|
|
247
|
+
ctx.render();
|
|
248
|
+
return;
|
|
249
|
+
case 'move-bridges': await ctx.openSkillFolders(); return;
|
|
250
|
+
case 'delete':
|
|
251
|
+
ctx.lib.selected = new Set([row.key]);
|
|
252
|
+
await bulkDelete?.(ctx);
|
|
253
|
+
return;
|
|
254
|
+
case 'auto-text': {
|
|
255
|
+
const fix = await readTextFix(ctx, row);
|
|
256
|
+
if (!ctx.alive(gen)) return;
|
|
257
|
+
if (!fix) {
|
|
258
|
+
const go = await ctx.ask({ title: t('skillsStudio.fix.title', { name: row.label }), lines: [t('skillsStudio.fix.textNothing')], buttons: [{ id: 'cancel', label: t('skillsStudio.dialog.close') }, { id: 'open', label: t('skillsStudio.fix.action.open-edit'), kind: 'primary' }], focus: 'open' });
|
|
259
|
+
if (go === 'open') ctx.openRow(row.key, { tab: 'edit' });
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
const step = { kind: 'text', row, issue, changes: fix.changes };
|
|
263
|
+
const ok = await ctx.ask({ title: t('skillsStudio.fix.previewTitle'), lines: [t('skillsStudio.fix.previewIntro'), stepWords(ctx, step), row.skill ? t('skillsStudio.fix.textToDraft') : t('skillsStudio.fix.textInPlace')], buttons: [{ id: 'cancel', label: t('skillsStudio.dialog.cancel') }, { id: 'apply', label: tp('skillsStudio.fix.apply', 1), kind: 'primary' }], focus: 'apply' });
|
|
264
|
+
if (ok !== 'apply' || !ctx.alive(gen)) return;
|
|
265
|
+
await fix.apply();
|
|
266
|
+
report(ctx, [stepWords(ctx, step)], []);
|
|
267
|
+
await ctx.reload();
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
case 'archive-duplicate': {
|
|
271
|
+
const step = { kind: 'archive', row, issue };
|
|
272
|
+
const ok = await ctx.ask({ title: t('skillsStudio.fix.previewTitle'), lines: [stepWords(ctx, step)], buttons: [{ id: 'cancel', label: t('skillsStudio.dialog.cancel') }, { id: 'apply', label: tp('skillsStudio.fix.apply', 1), kind: 'primary' }], focus: 'apply' });
|
|
273
|
+
if (ok !== 'apply' || !ctx.alive(gen)) return;
|
|
274
|
+
await archiveRecord(ctx, row);
|
|
275
|
+
report(ctx, [stepWords(ctx, step)], []);
|
|
276
|
+
await ctx.reload();
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
case 'choose-copy': await chooseCopy(ctx, row, issue); return;
|
|
280
|
+
default: return;
|
|
281
|
+
}
|
|
282
|
+
} catch (err) {
|
|
283
|
+
if (ctx.alive(gen)) ctx.toast(t('skillsStudio.fix.failed', { error: studioErrorText(t, err) }), 'error');
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/** Two records of one skill that differ: the owner picks the one that keeps the name; the other is archived. */
|
|
288
|
+
async function chooseCopy(ctx, row, issue) {
|
|
289
|
+
const { t, tp } = ctx;
|
|
290
|
+
const original = ctx.rows.find((r) => r.key === issue.health.of);
|
|
291
|
+
const pair = [original, row].filter(Boolean);
|
|
292
|
+
const archivable = (r) => !(r.skill?.installations || []).length && !r.review;
|
|
293
|
+
const box = document.createElement('div');
|
|
294
|
+
box.className = 'sx-fix-choose';
|
|
295
|
+
box.setAttribute('role', 'radiogroup');
|
|
296
|
+
box.setAttribute('aria-label', t('skillsStudio.fix.chooseTitle'));
|
|
297
|
+
let picked = null;
|
|
298
|
+
for (const r of pair) {
|
|
299
|
+
const label = document.createElement('label');
|
|
300
|
+
label.className = 'sx-fix-choice';
|
|
301
|
+
const input = document.createElement('input');
|
|
302
|
+
input.type = 'radio';
|
|
303
|
+
input.name = 'sx-fix-keep';
|
|
304
|
+
input.value = r.key;
|
|
305
|
+
// Keeping one archives the other: only a record that can be archived (not installed, not in review) may go.
|
|
306
|
+
const other = pair.find((x) => x !== r);
|
|
307
|
+
input.disabled = !other || !archivable(other);
|
|
308
|
+
input.addEventListener('change', () => { picked = r.key; });
|
|
309
|
+
const text = document.createElement('span');
|
|
310
|
+
const versions = Number(r.skill?.revisions) || 0;
|
|
311
|
+
const installs = (r.skill?.installations || []).length;
|
|
312
|
+
text.textContent = t('skillsStudio.fix.keepThis', { name: r.label, versions: tp('skillsStudio.fix.versions', versions), installs: tp('skillsStudio.fix.installs', installs) });
|
|
313
|
+
label.append(input, text);
|
|
314
|
+
box.append(label);
|
|
315
|
+
}
|
|
316
|
+
if (!pair.some((r) => archivable(r))) {
|
|
317
|
+
await ctx.ask({ title: t('skillsStudio.fix.chooseTitle'), lines: [t('skillsStudio.fix.chooseNone')], buttons: [{ id: 'ok', label: t('skillsStudio.dialog.close'), kind: 'primary' }], cancelId: 'ok' });
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
const answer = await ctx.ask({
|
|
321
|
+
title: t('skillsStudio.fix.chooseTitle'),
|
|
322
|
+
lines: [t('skillsStudio.fix.chooseIntro')],
|
|
323
|
+
extra: box,
|
|
324
|
+
buttons: [{ id: 'cancel', label: t('skillsStudio.dialog.cancel') }, { id: 'keep', label: t('skillsStudio.fix.keepConfirm'), kind: 'primary' }],
|
|
325
|
+
accept: () => !!picked,
|
|
326
|
+
});
|
|
327
|
+
if (answer !== 'keep' || !picked) return;
|
|
328
|
+
const gone = pair.find((r) => r.key !== picked);
|
|
329
|
+
await archiveRecord(ctx, gone);
|
|
330
|
+
report(ctx, [t('skillsStudio.fix.step.archive', { name: gone.label, original: pair.find((r) => r.key === picked)?.label || '' })], []);
|
|
331
|
+
await ctx.reload();
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/** The Library's attention notice: how many problems, and "Fix safe issues…" / "Show them". */
|
|
335
|
+
export function fixNoticeHTML(ctx, deleteKind) {
|
|
336
|
+
if (ctx.guest || ctx.lib.view === 'archived' || ctx.loading) return '';
|
|
337
|
+
const { t, tp } = ctx;
|
|
338
|
+
const out = [];
|
|
339
|
+
const n = ctx.lib.fixNotice;
|
|
340
|
+
if (n) {
|
|
341
|
+
out.push(html`<div class="sx-notice sx-notice--${n.kind === 'info' ? 'info' : 'warn'}" role="status" data-notice-box="fix">
|
|
342
|
+
${n.kind === 'info' ? ICONS.ok : ICONS.warn}<span class="sx-notice-text">${n.line}${n.lines.slice(0, 8).map((l) => html`<span class="sx-block">${l}</span>`)}</span>
|
|
343
|
+
<button type="button" class="sx-btn sx-btn--ghost" data-lib="fix-notice-ok">${t('skillsStudio.notice.ok')}</button>
|
|
344
|
+
</div>`);
|
|
345
|
+
}
|
|
346
|
+
const counts = issueCounts(ctx.rows, canOf(ctx, deleteKind));
|
|
347
|
+
if (counts.total) {
|
|
348
|
+
const { steps } = safeFixCandidates(ctx.rows, canOf(ctx, deleteKind));
|
|
349
|
+
out.push(html`<div class="sx-notice sx-notice--warn sx-fix-notice" role="note" data-notice-box="attention">
|
|
350
|
+
${ICONS.warn}<span class="sx-notice-text">${tp('skillsStudio.fix.notice', counts.total)}${steps.length ? ` ${tp('skillsStudio.fix.noticeSafe', steps.length)}` : ''}</span>
|
|
351
|
+
${steps.length ? html`<button type="button" class="sx-btn sx-btn--primary" data-lib="fix-all">${t('skillsStudio.fix.fixAll')}</button>` : ''}
|
|
352
|
+
${ctx.lib.health === 'attention' ? '' : html`<button type="button" class="sx-btn" data-lib="fix-show">${t('skillsStudio.fix.showIssues')}</button>`}
|
|
353
|
+
</div>`);
|
|
354
|
+
}
|
|
355
|
+
return html`${out}`;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/** The unreadable folders of the catalog, said with what can be done: read again, or leave them (dismissed). */
|
|
359
|
+
export const unreadableFingerprint = (count) => fingerprintOf('library', { code: 'unreadable', count });
|