12 KiB
12 KiB
BQ-System — правила для Claude
Поиск по коду
ast-index — дефолт. ВСЕГДА первым для «найти символ по имени / usages / callers / outline». Grep/Read — только если ast-index вернул пустой результат.
vex — для поиска по смыслу, AST-паттернов, дубликатов, компактного тела символа:
vex search "..." --semantic, vex similar, vex pattern, vex duplicates, vex show.
Что и когда — подробно в .claude/rules/search-tools.md. (usages/callers по JS — только ast-index.)
# Найти класс/функцию/символ
ast-index class "ClassName"
ast-index symbol "functionName"
# Найти использования
ast-index usages "symbolName"
# Поиск по содержимому файла
ast-index search "keyword" --in-file "filename"
# Структура файла
ast-index outline "path/to/file.js"
# Универсальный поиск
ast-index search "query"
Grep использовать только для:
- Поиска строковых литералов (
"some text") - Regex-паттернов
- Если ast-index вернул пустой результат
Git — обновление репо
После любых изменений — коммит и push:
git add <изменённые файлы>
git commit -m "тип: описание"
git push origin master
- Коммитить только изменённые файлы (не
git add -A) - Сообщение коммита:
feat:/fix:/refactor:/style:+ краткое описание на русском или английском - Push выполнять сразу после коммита
Стек
- Node.js/Express backend, SQLite (better-sqlite3, sync)
- Frontend: vanilla JS, без бандлера
- ast-index проиндексирован:
ast-index rebuildпри добавлении новых файлов
Feature: Конструктор симуляций (SimForge)
Движок авторинга интерактивных 2D-симуляций из JSON-спеки (данные, НЕ код). План: plans/sim-builder/.
Phase 0 — Learnings
- Спека = данные. Любое числовое свойство объекта = число ИЛИ строка-выражение. Выражения шарятся между людьми → движок безопасный, ⛔ без
eval/new Function. window.SimExpr(frontend/js/labs/_sim_expr.js): токенайзер → AST → evaluate.compile(src)->{ast,fn,error};fn(env)НИКОГДА не бросает (NaN/∞/деление на 0 → 0). Whitelist:+ - * / ^ %, унарный- + !, сравнения< <= > >= == !=, логика&& ||, тернарник?:, функцииsin cos tan tg ctg cot asin..arctg sqrt abs exp ln log log2 log10 floor ceil round sign min max mod atan2 pow hypot, константыpi e tau. Идентификаторы (вкл. точечныеobj.x) — только изenv. Парсер — расширениеy=f(x)изgraph.js;-2^2 == 4(парити). ТакжеevalSafe,compileValue,parse,tokenize,FUNCTIONS,CONSTANTS.window.SimEngine.mount(host, spec)(_sim_engine.js) →{ play, pause, reset, setParam, getParam, isRunning, destroy, el }. Canvas (мир→экран, равные оси, Y вверх) + KaTeX-оверлей подписей (katex.renderToString, как graph.js) + слайдеры изparams[]. Выражения компилируются 1 раз в mount; в rAF — только evaluate.env = { t, <params>, w, h, xmin..ymax, <objId>.x, <objId>.y }. Объекты:point segment vector circle rect polyline path label. Формат спеки v1 — в шапке_sim_engine.js.window.registerSpecSim(spec)(_sim_adapter.js): спека → манифест LabRegistry (ленивый хост#sim-spec-host-<id>в#lab-sim;stopпрячет,destroyуничтожает). Так спек-сим открывается тем же путём, что рукописные ~40 (черезopenSim→ реестр).- Демо
customdemo—_sim_demo.js, за флагом?simdemo=1/?sim=customdemo/LAB_SHOW_SPEC_DEMO/ localStoragelab-spec-demo=1(ученикам не светится). - Подключение: 3 каркасных
<script>eager после_graph_panel.jsвlab.html, демо — после_register-all.js._sim_deps.jsне трогать (каркас грузится до диспетчера).
Phase 1 — Learnings
- Новые типы объектов (в
_sim_engine.js, формат — в шапке файла):plot— графикf(var)на canvas движка в мир-координатах (НЕ черезGraphPanelUI— тот stacked time-series в фикс. оверлее, неy=f(x)). Поля:expr,var(деф.x),range:[a,b](числа/выражения, деф. xmin..xmax),samples(клампится 2..2000, деф.200),trace(точка var=t пишется в trail; при trace без range статич. кривая не рисуется),color/width. Свободная переменная подставляется во временную копию env-ключа (восстанавливается после).readout— живой бейдж на DOM-оверлее (_labelLayer, как label). Поля:expr,label,unit,precision(0..8, деф.2),x/y(мир-коорд.; без них — авто-столбик верх-право, счётчик_readoutSlotсбрасывается на кадр). Ошибка — мягко черезSimExpr.evalSafe(AST компилируется 1 раз в prepare), показывает «—».vector— новая формаorigin:[ox,oy]+dx/dy(конец = origin + (dx,dy)); стараяx1/y1/x2/y2сохранена; стрелка из Ф0.
- Drag (
point/circleсdrag:{param,axis,min,max,paramY}): pointer events на canvas (мышь+тач,touchAction:none); хит-тест в экранных px (допуск 16px, ближайшая ручка), приоритет ручек.axis:'xy'требуетparamY. Курсор → мир через_toWorld(инверсия_toPx) →_setParamClamped(clamp поdrag.min/maxИ по диапазону параметра из_paramRange— не полагаться на DOM-clamp слайдера). Слушатели снимаются вdestroy. Drag только point/circle (вершины polyline/конец вектора — не реализовано). - Тестировать движок headless:
vm.createContext+ ручной DOM/canvas-стаб (canvas-ctx черезProxyс noop)._renderFrameрано выходит при_cw/_ch==0— выставить вручную.setParam/drag используютnew Event('input')(браузерно безопасно, в стабе нуженEvent). - ⛔
lab.html/lab-glue.js— зона параллельной сессии (Ф2 измерит. инструменты); Ф1 их НЕ трогала, работала только в_sim_engine.js/_sim_demo.js.
Phase 2 — Learnings
- Физический режим (всё в
_sim_engine.js, формат — в шапке файла): блокphysics:{ enabled, gravity:{x,y}, friction?, restitution?, dt?, walls?:[...], springs?:[...] }+body:{ mass, vx, vy, fixed }на point/circle. gravity/friction/restitution/k/length/damping/mass/нач.позиция/vx/vy — число ИЛИ выражение от params (вычисляются на reset, не каждый кадр — для стабильности). window.SimPhysics— экспортированный интегратор (step(state,dtFrame),integrate,resolveCollisions). Полу-неявный (симплектический) Эйлерv+=a·dt; x+=v·dt— та же математика, что_fx_motion.spring, обобщённая на N связанных тел. Фикс-шаг с накопителем (кламп dt 1/2000..1/30, кап подшагов 8, кламп скорости 1e4, вязкое трениеexp(-friction·dt)) → энергия не «взрывается». Упругие столкновения круг-круг (импульс по нормали + позиционная коррекция по обратным массам) и круг-стена. Чистая функция над state, без DOM/eval — переиспользуемо headless. Отдельного файла_sim_physics.jsНЕТ (нельзя подключить без правки lab.html — зона параллельной сессии); код внутри_sim_engine.js._fx_motionAPI не подходит для спек-движка напрямую:tween/springFactory— rAF-замыкания, тянущие ОДНО значение к цели, не связанные тела с силами. Переиспользована только их интеграционная математика (формула спринга), а не сами функции.- env-поля тел:
<id>.x/.y/.vx/.vyберутся из СОСТОЯНИЯ интегратора и кладутся в_buildEnvПЕРВЫМИ (до формульных центров) — это снимает forward-ref проблему однопроходного env для тел: формульный объект, ссылающийся на тело (segment x2:'ball.x'), видит актуальную позицию в том же кадре. point/circle сbodyрисуются из env-полей тела, а не из выражения x/y. - Drag тела: тело (point/circle с
body, не fixed) перетаскиваемо по умолчанию (безdrag-конфига). Тащишь —body.x/y = курсор, тело временноfixedв_stepPhysics; отпускаешь —body.vx/vyиз сглаженной оценки скорости курсора (кламп 40 м/с). Хит-тест тела — max(16px, экранный радиус). drag-ручки Ф1 и физ-тела сосуществуют. - Гочи: (1) имя param
eзарезервировано — это число Эйлера в SimExpr (parser проверяет CONSTANTS до env), выражениеeдаст 2.718, не значение param. Братьel/elast. (2) Радиус тела для коллизий: circle — мировойr; point — экранные px → мир через_scale, поэтому физика собирается вreset()ПОСЛЕ первого_fit(). (3) Слайдеры во время play меняют только env (readout/формулы), но НЕ силы/тела до reset (намеренно). На паузе приt==0— пересборка для предпросмотра старта. - Headless-тест физики: виртуальные часы (
vclock) синхронны сperformance.now()и таймстампом rAF (иначе первый кадр получит огромный/отрицательный dt и ничего не сдвинется).