DeepSeek Harness Plugin Hub

发布与管理完整 Harness Profiles,发现适合你的插件。

探索

插件目录环境预设文档中心动态

社区

发布插件联系我们报告问题

相关链接

Plugin Hub GitHubDeepSeek Harness 官方项目系统状态隐私说明
© 2026 DeepSeek Harness Plugin HubPowered byPaxTech

独立、非官方社区项目,与 DeepSeek 官方无隶属、授权或背书关系。

DeepSeek Harness Plugin Hub
ProfilesPlugins分类动态文档登录管理 Profiles
ProfilesPlugins分类动态文档登录
← Plugins
D

@local/dsh-design-canvas

Design Canvas

适用于 DeepSeek Harness 的可视化 HTML/CSS 布局编辑器:为每个项目提供类似 Figma 的画布、组件库、设计令牌和用于构建及导出应用设计的代理工具。

插件会安装到这里;不确定时保持 web。

npx -y @deepseek-ai/dsh plugin --profile web add github:kukuruzovo/dsh-design-canvas#43ffe28cec0c6f94272bd18dc37fb993cdb15216
README兼容性版本
ЭкранКомпоненты

兼容性与来源证明

Design Canvas 以 @local/dsh-design-canvas 发布,当前版本为 0.1.0。Plugin Hub 会校验它的 manifest,并保存精确安装来源,便于复现安装结果。

DSH 兼容范围
*
运行环境
web
发布来源
github
Registry 更新时间
2026/10/23

版本

0.1.0stable
2026/10/23

相关插件

正在加载相关插件…

最新版
0.1.0
DSH
*
HMR
重启进程
Tree shaking
未声明可安全裁剪
解包体积
未提供
文件数
未提供
Surface
web
许可证
未声明
发布源
github
GitHub
★ 0
周下载
0
最近提交
2026/10/23
查看源码 ↗
README Badge

点击下方 Badge 复制 Markdown,粘贴到 README 即可。

这是你的 Plugin?认领权益 · 优先安全扫描

验证 package.json 声明的 GitHub 仓库,即可管理这个公开页面。认领后,Hub 会优先安排当前版本的安全扫描,并在通过后公开展示结果。

认领这个 Plugin →
报告问题

相关插件

继续浏览 ui-customization 分类下经过校验的插件。

Plugin Wallpaper Enginedsh-plugin-wallpaper-engineDSH web GUI 的 Wallpaper Engine 背景:将本地 Wallpaper Engine 壁纸渲染到聊天背景中——通过内置的 WebWallGL 引擎实时渲染 Scene 和 Web,直接播放 Video——配备 iOS 风格的液态玻璃效果,可调节模糊/遮罩/bClaude Designdsh-claude-designDeepSeek Harness 插件:来自 baoyu-design 的 claude-design 技能。智能体将画板画布写入文件,并在本地画布编辑器中打开它们。Claude Slidesdsh-claude-slidesDeepSeek Harness 插件:来自 baoyu-design 的 claude-slides 技能。代理会将可编辑的幻灯片演示文稿写入文件,并在本地幻灯片编辑器中打开。Genui@changfenhuang/dsh-genuiDeepSeek Harness 的 GenUI:通过 ```dsh-ui``` 代码围栏在助手回复中内嵌渲染交互式 UI 组件——布局、图表、绘图、表单、测验、mermaid、3D 场景,以及回传给模型的操作事件循环。随附用于教授代码围栏的主机插件

README

Design canvas — визуальные макеты для DeepSeek Harness

Плагин даёт две половины одной системы:

  • инструменты агента design_* — агент проектирует интерфейс как код: создаёт холсты, собирает экраны батчами операций, объявляет компоненты, ведёт токены, проверяет и экспортирует в HTML/CSS;
  • панель «Макеты» в веб-интерфейсе — Figma-подобный редактор тех же самых файлов: дерево слоёв, перетаскивание, ресайз, инспектор, библиотека компонентов, редактор токенов, экспорт.

Человек и агент работают над одним документом на диске, поэтому дизайн не расходится между «тем, что нарисовал агент» и «тем, что поправил человек».

Тот же редактор открывается и самостоятельно — отдельным окном на своём порту, без харнесса: node standalone.js (см. Автономный редактор).

Содержание

  • Требования
  • Быстрый старт
  • Где лежат макеты
  • Модель документа
  • Инструменты агента
  • Экспорт
  • Панель «Макеты»
  • Автономный редактор
  • HTTP-маршрут
  • Архитектура
  • Демо
  • Проверки
  • Разработка
  • Установка и обновление
  • Лицензия

Требования

  • Node.js ≥ 20 (ESM, node:fs/promises, fetch). Зависимостей у плагина нет: React для автономного окна лежит в vendor/, Playwright нужен только для браузерных проверок и берётся через npx.
  • DeepSeek Harness — только для панели «Макеты» и инструментов агента. Автономному редактору харнесс не нужен.

Быстрый старт

git clone https://github.com/kukuruzovo/dsh-design-canvas.git
cd dsh-design-canvas
node standalone.js            # редактор на http://127.0.0.1:5178/

В окне: «Новый холст» → выберите каталог проекта → холст появится в <проект>/.design/. Дальше его можно править руками, агентом или экспортировать.

Чтобы подключить плагин к DSH (инструменты design_* + панель «Макеты»):

dsh plugin --profile web add link:/абсолютный/путь/к/dsh-design-canvas

и перезапустить dsh web. Подробности — в разделе Установка и обновление.

Где лежат макеты

Холсты живут в репозитории проекта, а не в домашнем каталоге DSH:

<корень проекта>/.design/
  project.json            — метаданные проекта (появляется при первой записи холста)
  home.canvas.json        — документ холста
  settings-mobile.canvas.json
  exports/                — сгенерированные HTML/CSS
    home.html
    home.css
    home.components.html

Следствия: дизайн версионируется вместе с кодом, едет в git, переносится между машинами и лежит рядом с приложением, для которого сделан.

Реестр корней — ~/.dsh/design-canvas.json. Встроенного списка проектов нет: каталог попадает в реестр, когда вы инициализируете его как проект (design_projects init, первый design_create или standalone.js --project), а каталог с уже закоммиченной папкой .design/ плагин находит и сам. Найденный проект потом адресуется по id — сценарий «клонировали репозиторий с закоммиченными макетами». Корзина удалённых холстов — ~/.dsh/design-trash/ (восстановимо).

Модель документа

Документ — обычный JSON. Координатная модель Figma-подобная: у каждого узла абсолютные x/y относительно родителя и свои w/h; у фрейма есть режим layout.mode (none / vertical / horizontal), который включает поток и пересчёт позиций детей. Файл холста <id>.canvas.json выглядит так:

{
  "format": "dsh-design-canvas",
  "version": 1,
  "id": "home",
  "name": "Home",
  "device": "mobile",
  "rev": 7,                       // ревизия: поле файла, а не модели документа
  "tokens": { "color": { "bg": "#0b0d10" }, "text": { "h1": { "size": 32, "weight": 700 } } },
  "components": {
    "button-primary": {
      "key": "button-primary",
      "name": "Button / Primary",
      "node": { /* шаблон узла без координат */ },
      "props": {
        "label": { "type": "text", "default": "Кнопка", "target": "text", "property": "text.value" }
      },
      "variants": { "ghost": { "name": "Ghost", "node": { /* свой шаблон */ }, "props": { "label": { "default": "Позже" } } } }
    }
  },
  "guides": { "x": [120, 340], "y": [64] },   // направляющие редактора; нет разметки — нет поля
  "root": { "id": "root", "type": "frame", "children": [ /* фреймы-экраны */ ] }
}

Типы узлов: frame, group, rect, ellipse, line, text, image, instance.

rev живёт только в файле: design_read mode=full и GET /canvas возвращают документ без него, а сама ревизия приходит соседним полем ответа.

Пределы: вложенность — 64 уровня (глубже дети обрезаются), узлов — 20 000, вложенные инстансы разворачиваются до 32 уровней, холст больше 8 МБ не читается и не записывается (запись отклоняется, чтобы файл не стал нечитаемым).

Токены

Значение поля может быть литералом (#0b0d10) или ссылкой на токен (token:color.bg). Ссылка разрешается при отрисовке, экспорте и проверке, а в CSS превращается в переменную: var(--color-bg, #0b0d10). Смена токена перекрашивает весь макет — и в редакторе, и в собранном HTML.

Единицы вывода берутся из пути токена: radius.*, space.*, размеры и межбуквенные интервалы типографики, а также смещения теней получают px; веса и множители строки остаются безразмерными.

Исключение — поле text.token: это голый путь токена типографики (text.h1, без префикса token:), потому что токен составной и описывает сразу размер, вес, высоту строки и трекинг. Явные text.size, text.weight, text.lineHeight, text.letterSpacing переопределяют значения токена для конкретного узла.

Раскладка

layout.mode = 'none' — свободное позиционирование: дети сохраняют авторские x/y. vertical / horizontal включает поток: позиции детей выводятся из порядка, gap, padding, align и justify, а размер фрейма подгоняется по содержимому (отключить можно полями узла autoW / autoH со значением false — они сохраняются в canvas.json и переживают запись).

justify (start / center / end / space-between) распределяет свободное место по главной оси, align: 'stretch' растягивает детей по поперечной. Свободное место появляется только у фрейма с авторским размером по этой оси: с включённым autoW/autoH фрейм сжимается по содержимому и распределять нечего. Поэтому justify: center работает вместе с autoW: false (для горизонтали) и autoH: false (для вертикали), а align: 'stretch' — с заданным размером по поперечной оси. Ту же арифметику повторяет панель, поэтому сохранённый файл совпадает с картинкой.

Направляющие

guides — разметка редактора: x задаёт вертикальные линии, y — горизонтальные, в координатах холста. Поле необязательное: холст без разметки его не содержит, и нормализация не добавляет пустые массивы в старые canvas.json. Значения сортируются, дубликаты отбрасываются, на ось хранится не больше сотни линий. В экспорт направляющие не попадают: это разметка рабочего места, а не часть макета. Агент ставит их операцией set-guides.

Компоненты

Инстанс — живая ссылка, а не копия. Своего содержимого у инстанса нет: ключ компонента, координаты и overrides; содержимое разворачивается из шаблона при отрисовке и экспорте, поэтому правка компонента обновляет все инстансы сразу. Авторские дети инстанса не отрисовываются и не экспортируются, а design_validate сообщает о них замечанием instance-children. design_components action=detach (или op: detach) превращает инстанс в обычный узел.

Правка компонента. update-component меняет имя, описание, параметры, вариации и шаблон существующего компонента, не пересоздавая его: props сливаются по имени параметра, а сам параметр — по полям ({ label: { default: 'ОК' } } меняет только значение по умолчанию и не теряет target), значение null убирает параметр, node заменяет шаблон целиком.

Вариации. variants[name] — тот же компонент с другим шаблоном (node) и/или другими полями параметров (props). Параметры и переопределения инстанса общие для всех вариаций, поэтому переключение вариации не теряет правки инстанса: инстанс хранит поле variant, а разворачивание берёт шаблон вариации (componentTemplate) и действующие параметры (componentProps). Ссылка на несуществующую вариацию попадает в design_validate замечанием missing-variant, а лист библиотеки в экспорте печатает базовый шаблон и каждую вариацию отдельной ячейкой. define-component умеет объявить компонент не только из узла холста (from), но и из готового шаблона (node) — так панель клонирует компонент, не оставляя мусорного узла в макете.

Переопределения инстанса бывают двух видов:

  • объявленный параметр компонента — значение ложится в property-путь узла-цели target (root, #Имя узла, text или путь внутри шаблона — children[0].label);
  • произвольный путь узла (style.fill, text.value, w) — точечная правка без объявления параметра.

При объявлении компонента параметр label выводится автоматически из первого текстового узла.

Инструменты агента

ИнструментНазначение
design_projectsреестр проектов и холстов; init — инициализировать каталог как проект
design_createсоздать холст (устройство, шаблон, сразу батч операций)
design_readпроекции холста: summary, outline, node, tokens, components, full
design_applyосновной инструмент: батч операций, транзакционно
design_tokensчтение и правка палитры, типографики, радиусов, отступов, теней
design_componentslist / get / define / update / instantiate / detach / delete
design_exportHTML + CSS + страница библиотеки компонентов в .design/exports/
design_validateдубли id, битые инстансы, авторские дети инстанса, отсутствующие токены, обрезка, контраст (порог по размеру текста, токены и rgba() считаются)
design_deleteкорзина: list / delete / restore

Операции design_apply

ОперацияПоля
add{ parent, node | type, index?, after? } — занятый id заменяется новым, ответ возвращает фактический
update{ id | ids, patch } — x, y, w, h, name, visible, locked, style.*, text.*, layout.*
move{ id, parent?, index?, after? } — after: id соседа, за которым вставить; без parent узел остаётся у того же родителя и меняет порядок
delete{ id | ids }
duplicate{ id | ids, count?, offsetX?, offsetY?, parent? }
group / ungroup{ ids, name?, as? } / { id } — as: frame оборачивает выделение во фрейм по габаритам (по умолчанию группа); неизвестное as — ошибка
reorder{ id, index | direction }, direction: front / back / up / down
set-tokens{ tokens, remove?, replace? } — remove удаляет пути (color.bg, text.h1.size)
define-component{ key, from?, node?, name?, description?, props?, convert? } — from берёт узел холста, node задаёт готовый шаблон
update-component{ key, name?, description?, props?, variants?, node? } — props сливаются по имени (значение null убирает параметр), variants — по имени вариации (значение null убирает вариацию), node заменяет шаблон
instantiate{ component, parent, x?, y?, w?, h?, name?, variant?, overrides?, index?, after? } — variant выбирает вариацию

Батч транзакционный: ошибка в любой операции откатывает весь батч, документ остаётся прежним. Ревизия растёт на 1; expectRev защищает от затирания правок, сделанных в панели (несовпадение → отказ 409).

Фрейм, добавленный операцией add, по умолчанию обрезает детей (clip: true), как фрейм из шаблона устройства; clip: false делает контейнер прозрачным.

Пример: экран одним вызовом

// design_create { "project": "my-app", "canvas": "home", "device": "mobile" }
// design_apply
{
  "project": "my-app",
  "canvas": "home",
  "ops": [
    { "op": "add", "parent": "page", "node": {
        "id": "header", "type": "frame", "name": "Header",
        "x": 0, "y": 0, "w": 390, "h": 88,
        "style": { "fill": "token:color.surface" } } },
    { "op": "add", "parent": "header", "node": {
        "id": "title", "type": "text", "name": "Title",
        "x": 20, "y": 32, "w": 300, "h": 32,
        "text": { "value": "Викторины", "token": "text.h1", "color": "token:color.fg" } } },
    { "op": "add", "parent": "page", "node": {
        "id": "cta", "type": "frame", "name": "CTA",
        "x": 20, "y": 120, "w": 350, "h": 52,
        "style": { "fill": "token:color.accent", "radius": "token:radius.pill" },
        "layout": { "mode": "horizontal", "align": "center", "justify": "center" } } },
    { "op": "add", "parent": "cta", "node": {
        "type": "text", "name": "CTALabel", "x": 0, "y": 0, "w": 200, "h": 24,
        "text": { "value": "Начать", "color": "token:color.accentFg", "align": "center" } } },
    { "op": "define-component", "key": "button-primary", "from": "cta", "name": "Button / Primary" },
    { "op": "instantiate", "component": "button-primary", "parent": "page",
      "x": 20, "y": 190, "overrides": { "label": "Мои результаты" } }
  ]
}

Экспорт

design_export пишет в <проект>/.design/exports/:

  • <canvas>.html — страница со всеми фреймами;
  • <canvas>.css — токены как :root { --color-bg: … } плюс базовые классы;
  • <canvas>.components.html — библиотека: каждый компонент один раз, а его вариации — отдельными ячейками.

Верхние фреймы холста — экраны: на собранной странице они всегда встают потоком доски (.dc-board), поэтому два экрана, поставленные в одну точку (0,0), не накладываются друг на друга. Режим решает, как позиционируются узлы внутри:

  • fidelity — абсолютное позиционирование узлов внутри фреймов, пиксельно точно (превью, handoff); верхние фреймы и здесь встают потоком доски, поэтому их координаты в собранной странице не сохраняются;
  • clean — фреймы с layout.mode становятся flex-контейнерами, остальное абсолютное (ближе к вёрстке приложения).

У каждого элемента есть data-node="<id узла>" — DOM можно связать с макетом.

Панель «Макеты»

Иконка в левой колонке веб-интерфейса. Внутри: тулбар инструментов (select, frame, rect, ellipse, text), панели «Холсты / Слои / Компоненты / Токены», вьюпорт с pan/zoom, инспектор, автосохранение с разрешением конфликтов ревизий, undo/redo, экспорт и проверка. Узлы line и image из тулбара не создаются — их ставит агент операцией add.

Правка узлов

  • Рамка выделения — протяжка левой кнопкой по пустому месту или по фону контейнера: протяжка по фону фрейма выделяет его детей, а клик по тому же фону — сам фрейм. Верхние фреймы выделяются рамкой из серого поля вокруг макета, дети — рамкой внутри фрейма. Панорама холста: средняя кнопка мыши или пробел с протяжкой; зум — Ctrl+колесо или кнопки тулбара.
  • Мультивыделение — Shift+клик или Ctrl/Cmd+клик на холсте и в дереве слоёв добавляет узел к выделению и снимает его повторным кликом; Ctrl+A выделяет всё в текущей области (дети «вошедшего» контейнера, иначе верхние фреймы). Выделенные узлы двигаются, ресайзятся, копируются и группируются вместе; если в выделении есть предок и его потомок, потомок едет вместе с предком.
  • Вход в контейнер — двойной клик по фрейму или группе: протяжка по его фону делает рамку по его детям, а не двигает контейнер. Выход — Esc.
  • Стрелки двигают выделение на 1px, с Shift — на 10px. При перетаскивании Shift ведёт узел строго по одной оси — по той, куда курсор ушёл сильнее, поэтому объект скользит вдоль направляющей и не сносит в сторону. Внутри фрейма с авто-раскладкой координаты детей считает сама раскладка (layout.mode), поэтому перетаскивание ребёнка там ничего не меняет: правьте порядок слоёв, gap и padding.
  • Копирование — Ctrl+C / Ctrl+V, дублирование — Ctrl+D, вырезать — Ctrl+X. Копия встаёт рядом с оригиналом (в его родителя), каждая следующая вставка сдвигается на шаг.
  • Группировка — Ctrl+G и Ctrl+Shift+G; объединить выделение в новый фрейм — Ctrl+Alt+G или пункт контекстного меню «Объединить в фрейм» (фрейм встаёт по габаритам выделения, без авто-раскладки). Порядок слоёв — Ctrl+] / Ctrl+[ (с Shift — сразу на передний или задний план).
  • Выравнивание — секция в инспекторе при выделении двух и более узлов: шесть режимов и распределение по горизонтали и вертикали (нужно минимум три узла).
  • Контекстное меню по правой кнопке на узле (на холсте или в дереве слоёв): дублирование, копирование, вставка, группировка, «объединить в фрейм», порядок, замок, видимость, отсоединение инстанса, «сделать компонентом», удаление. Правый клик по узлу вне выделения сначала выделяет его.

Компоненты в панели

  • Библиотека — карточки с настоящими превью: миниатюра рисуется тем же NodeView, что и холст, поэтому не расходится с макетом. Есть поиск по имени и ключу, счётчики инстансов и параметров, наведение на карточку подсвечивает все инстансы компонента на холсте.
  • Карточка компонента — превью крупнее, имя и описание, редактор параметров (имя, тип, значение по умолчанию, цель и свойство; цель подставляется кликом по имени узла шаблона), вариации (превью каждой, добавление вариации из выделенного узла, удаление), кнопки «поставить инстанс», «найти инстансы» и «дублировать компонент».
  • Инстанс — в инспекторе видно, какие параметры переопределены на этом инстансе, и есть сброс одного переопределения, сброс всех, «вынести правки в компонент» (шаблон становится тем, что нарисовано на инстансе), замена компонента у инстанса с переносом совпадающих переопределений и переключатель вариации, когда они у компонента объявлены.

Разметка рабочего места

  • Наведение подсвечивает узел под курсором, его размер и всю цепочку контейнеров до верхнего фрейма — структура читается без клика по дереву слоёв. Та же цепочка подсвечивается и в дереве слоёв, а наведение в дереве подсвечивает узел на холсте.
  • Линейки сверху и слева показывают координаты холста; шаг штрихов зависит от масштаба, подписи — каждые десять штрихов. Кнопка «Линейки» или Shift+R скрывает их.
  • Направляющие: потяните из линейки, чтобы поставить линию. Линия переносится перетаскиванием, удаляется броском обратно на линейку, двойным кликом или кнопкой «Убрать все направляющие». Направляющие видны по кнопке «Линии» и хранятся в canvas.json.
  • Привязка работает при перемещении, ресайзе и создании узла: объект липнет к краям и серединам родителя, соседей и соседних фреймов, к направляющим и к пиксельной сетке (при ресайзе и создании Shift — шаг 10px). Совпавшие линии подсвечиваются. Привязка к объектам важнее привязки к сетке, а Alt отключает обе; кнопка «Привязка» выключает привязку целиком. При перемещении притягиваются только те оси, по которым объект реально сдвинулся, — на старте жеста узел не уводит в сторону.
  • Замеры показывают расстояния: у выделенного узла — до краёв родителя и до ближайших соседей, при перетаскивании — живые значения, при наведении — по удержанию Alt. Padding контейнера выделяется подложками вокруг его содержимого, поэтому видно и отступы контейнера, и зазоры между объектами.

Автономный редактор

Тот же редактор открывается отдельным окном на своём порту и работает без харнесса — нужен только Node:

node standalone.js                     # http://127.0.0.1:5178/
npm run editor                         # то же самое
node standalone.js --port 4180 --open  # другой порт и сразу открыть браузер
node standalone.js --project ~/app/foo # зарегистрировать корень проекта в реестре
node standalone.js --tailscale         # окно для телефона и других устройств tailnet
node standalone.js --host 0.0.0.0 --token   # наружу — только с токеном

Редактор с телефона: --tailscale

node standalone.js --tailscale        # или: npm run editor:tailscale

Сервер слушает адрес этого узла в tailnet (100.x.y.z, узнаётся у tailscale ip -4, а без CLI — по интерфейсу tailscale0), поэтому окно открывается с телефона, ноутбука или другого узла сети, а в локальную сеть 192.168.* не выставляется вовсе. В выводе печатаются два адреса: по IP и по имени MagicDNS (<узел>.<tailnet>.ts.net) — второй удобнее, но требует включённого MagicDNS.

Доступ при этом закрыт токеном: он сохраняется в $DSH_HOME/design-canvas-token (права 0600), поэтому адрес окна не меняется от запуска к запуску — его можно положить в закладку на телефоне. Свой токен задаётся --token <значение>, сменить сохранённый — удалить файл.

HTTPS через tailnet — флагом --tailscale-serve: сервер сам позовёт tailscale serve --bg <адрес> и напечатает https://<узел>.<tailnet>.ts.net/. Эта команда требует root, поэтому без него сервер не падает, а печатает готовую строку для sudo и напоминание sudo tailscale serve reset, когда прокси больше не нужен.

Это не вторая реализация: standalone.js отдаёт ту же страницу редактора (client.js) и монтирует тот же обработчик /api/dsh-design-canvas, что и плагин внутри DSH. Обе половины ходят через один service.js и правят одни файлы <проект>/.design/*.canvas.json, поэтому холст, открытый в отдельном окне, виден агенту и панели «Макеты» — и наоборот.

Что внутри автономного окна:

  • standalone/index.html + standalone/shell.js — оболочка: она ставит шим window.__ModuleLoader__ и контекст с сервисами slots/locale, которых клиентская половина плагина ждёт от харнесса, и переключатель светлой/тёмной темы;
  • standalone/theme.css — переменные --dsw-* для светлой и тёмной темы (значения сняты с темы харнесса, чтобы панель выглядела одинаково там и здесь);
  • vendor/react.production.min.js, vendor/react-dom.production.min.js — React 18 локально (MIT), поэтому окно не зависит ни от сети, ни от установки DSH.

Порт по умолчанию — 5178, адрес — только этот компьютер: маршрут умеет писать файлы в .design/ проектов. Как только окно становится видно не только отсюда (--tailscale, --tailscale-serve или не-петлевой --host), доступ закрывается токеном: свой из --token (8–128 символов из латиницы, цифр и . _ ~ -) или сохранённый автоматически. Страница с ?token= выдаёт cookie, а скрипты могут ходить с заголовком x-dsh-token. Реестр проектов и корзина — те же (~/.dsh/design-canvas.json, ~/.dsh/design-trash/), так что окно и харнесс видят один список проектов.

HTTP-маршрут

/api/dsh-design-canvas — тот же service.js, что и у инструментов агента. Основное:

GET  /projects?canvases=1        GET  /canvases?project=
GET  /display?project=&id=       GET  /canvas?project=&id=&mode=full
GET  /outline|tokens|components|validate|export|trash|config
POST /create | /init | /apply | /canvas | /tokens | /components | /export | /delete | /restore

GET /display отдаёт готовое дерево для отрисовки: все инстансы развёрнуты сервером, дети инстанса неймспейснуты его id (<instanceId>/<childId>) — так правила разворачивания компонентов живут в одном месте и не дублируются на клиенте.

Дефолты адаптеров различаются: design_read читает summary, GET /canvas — full; GET /export всегда отдаёт превью со встроенным CSS и файлов не пишет.

Архитектура

ФайлСлой
schema.jsмодель: нормализация, обход дерева, токены, компоненты, направляющие, валидация
store.jsфайлы: реестр проектов, .design/, ревизии, корзина
ops.jsоперации над документом (транзакционный движок)
export.jsгенерация HTML/CSS
service.jsсценарии: одна логика для агента и панели
tools.js9 инструментов агента (тонкие адаптеры)
route.jsHTTP-маршрут панели (тонкий адаптер)
index.jsточка входа плагина: инструменты + маршрут
client.jsвизуальный редактор (React без JSX)
standalone.jsавтономный хост: статика оболочки, тот же маршрут на своём порту, токен, tailnet
standalone/оболочка окна (index.html, shell.js) и тема --dsw-*
vendor/React 18 UMD локально (MIT) — окно не зависит от харнесса и сети

Правило: логика живёт в schema/store/ops/export/service, а tools.js, route.js и standalone.js только разбирают аргументы и рендерят результат.

Демо

node demo.mjs собирает холст demo/.design/quiz-home.canvas.json — экран приложения с токенами, компонентом topic-card (три инстанса с разными переопределениями) и потоковой нижней навигацией. Он же служит фикстурой для проверки панели.

ЭкранБиблиотека компонентов
ЭкранКомпоненты

Превью снимаются с того же экспорта: npm run demo && npm run preview. npm run demo пересобирает только demo/.design/, поэтому готовые превью уцелевают; npm run preview нужен, когда изменился сам демо-холст или экспорт. Снимок самой панели «Макеты» делает живой прогон — DSH_WEB_TOKEN=… node smoke-gui.mjs … .gui-shot.png.

Проверки

npm run check             # синтаксис всех .js/.mjs
npm test                  # ядро + инструменты и маршрут + контракт клиента + автономный сервер
npm run test:editor       # автономное окно редактора в Playwright
npm run test:editor -- --tailscale   # то же окно по адресу tailnet (если Tailscale есть)
DSH_WEB_TOKEN=<токен из адресной строки> \
  node smoke-gui.mjs http://127.0.0.1:3080 .gui-shot.png   # панель в живом DSH
  • smoke.mjs — схема, хранилище, операции, транзакционность, экспорт, защита путей;
  • smoke-tools.mjs — регистрация 9 инструментов и полный цикл через HTTP-маршрут;
  • smoke-client.mjs — контракт браузерного модуля: слоты, словари, маршруты API, отсутствие JSX;
  • smoke-standalone.mjs — автономный сервер: статика оболочки, вендорный React, тема, правило доступа (токен, как только окно видно не только с этого компьютера), сохранение токена, защита от обхода пути, полный цикл правки через /api/dsh-design-canvas и аргументы CLI; если на машине есть Tailscale, отдельно проверяется --tailscale, иначе шаг помечается пропущенным;
  • smoke-editor.mjs — поднимает автономное окно на свободном порту с временным DSH_HOME, повторяет сценарий редактора в браузере и снимает .editor-shot.png; харнесс не нужен, Playwright берётся из установки или кеша npx. С флагом --tailscale то же окно открывается по адресу tailnet — так проверяется сценарий «с другого устройства»;
  • smoke-gui.mjs — открывает панель в живом DSH и снимает скриншот, собирая ошибки консоли. Ему нужны запущенный DSH, токен и собранная демо-фикстура (npm run demo).

Разработка

  • AGENTS.md — правила проекта для агента: слои, инварианты, команды, критерии сдачи;
  • docs/ARCHITECTURE.md — внутреннее устройство: слои, модель, рецепты расширения, коды ошибок;
  • docs/BACKLOG.md — известные дефекты и долги с уровнем доказанности;
  • docs/PLAN-editor-ux.md — план улучшения редактора и статусы фаз;
  • .dsh/skills/ — скилы проекта, которые DSH подхватывает автоматически;
  • agency/ — эксперты и команда The Agency для разработки плагина (npm run agency:check, затем node tools/agency-sync.mjs).

Установка и обновление

Плагин — обычный npm-пакет: Host-половина (index.js + dsh.bundle.patch) и клиентская (client.js + dsh.client). Зависимостей нет, поэтому установка сводится к тому, чтобы добавить каталог в профиль DSH.

git clone https://github.com/kukuruzovo/dsh-design-canvas.git
dsh plugin --profile web add link:/абсолютный/путь/к/dsh-design-canvas
dsh web            # перезапуск: Host-половина подхватывается при старте

dsh plugin — это обёртка над pnpm в каталоге профиля, поэтому link: ставит пакет без копирования и без публикации в реестр npm. В веб-интерфейсе то же делает plugin_manager (install_bundle с абсолютным путём к каталогу).

После установки:

  • правки Host-половины подхватываются перезапуском dsh web;
  • правки client.js — перезагрузкой страницы (на лету — только при запущенном pnpm run dev:web в чекауте харнесса);
  • обновление — git pull в каталоге плагина плюс перезапуск dsh web.

Автономному окну (node standalone.js) установка в DSH не нужна вовсе: это обычный Node-процесс в каталоге плагина, зависимостей у него нет — React лежит рядом в vendor/. Правки client.js и оболочки видны в нём после перезагрузки страницы, правки standalone.js — после перезапуска процесса.

Лицензия

MIT. Вендорный React 18 в vendor/ распространяется по MIT (тексты лицензий лежат рядом).

delete-component{ key, detach? }
detach{ id }
set-canvas{ name?, device? } — mobile / tablet / desktop / component; неизвестное значение — ошибка
align{ ids, mode, axis? } — mode: left / center / right / top / middle / bottom; axis задаёт ось (x / y) вместо выведенной из mode, противоречие axis и mode — ошибка
distribute{ ids, axis? } — x / y (по умолчанию x), минимум три узла
set-guides{ x?, y?, clear? } — направляющие холста: x — вертикальные на этой координате, y — горизонтальные; ось заменяется целиком, clear убирает разметку