Compare commits
18 Commits
e2ab676ec2
...
c05759901a
Author | SHA1 | Date | |
---|---|---|---|
![]() |
c05759901a | ||
![]() |
ac50a4fb05 | ||
![]() |
2b7acfbd27 | ||
![]() |
313d5de706 | ||
![]() |
c166a4478a | ||
![]() |
5dcaae4f51 | ||
![]() |
f0f41a4dcb | ||
![]() |
2b65b19d08 | ||
![]() |
53d8dfc3d6 | ||
![]() |
ece60e565d | ||
![]() |
841a72af4e | ||
![]() |
8452bf4d55 | ||
![]() |
32eee7ca0b | ||
![]() |
addcd52cb6 | ||
![]() |
c442ca8094 | ||
![]() |
a26dd4bc7b | ||
![]() |
16a74bf4f8 | ||
![]() |
5f2239dc26 |
8
.vscode/settings.json
vendored
8
.vscode/settings.json
vendored
|
@ -58,14 +58,6 @@
|
|||
"depth": 2
|
||||
}
|
||||
],
|
||||
"colorize.include": [".tsx", ".jsx", ".ts", ".js"],
|
||||
"colorize.languages": [
|
||||
"typescript",
|
||||
"javascript",
|
||||
"css",
|
||||
"typescriptreact",
|
||||
"javascriptreact"
|
||||
],
|
||||
"cSpell.words": [
|
||||
"ablt",
|
||||
"acconcept",
|
||||
|
|
|
@ -28,8 +28,8 @@ RUN apt-get update -qq && \
|
|||
FROM python-base AS builder
|
||||
|
||||
# Set env variables
|
||||
ENV PYTHONDONTWRITEBYTECODE 1
|
||||
ENV PYTHONUNBUFFERED 1
|
||||
ENV PYTHONDONTWRITEBYTECODE=1
|
||||
ENV PYTHONUNBUFFERED=1
|
||||
|
||||
COPY ./requirements.txt ./
|
||||
RUN python3.12 -m pip wheel \
|
||||
|
|
|
@ -7410,13 +7410,12 @@
|
|||
"pk": 1,
|
||||
"fields": {
|
||||
"oss": 41,
|
||||
"parent": null,
|
||||
"operation_type": "input",
|
||||
"result": 38,
|
||||
"alias": "КС Вещества",
|
||||
"title": "Вещества и смеси",
|
||||
"description": "",
|
||||
"position_x": 530.0,
|
||||
"position_y": 370.0
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -7424,13 +7423,12 @@
|
|||
"pk": 2,
|
||||
"fields": {
|
||||
"oss": 41,
|
||||
"parent": null,
|
||||
"operation_type": "input",
|
||||
"result": 39,
|
||||
"alias": "КС ООО",
|
||||
"title": "Объект-объектные отношения",
|
||||
"description": "",
|
||||
"position_x": 710.0,
|
||||
"position_y": 370.0
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -7438,13 +7436,12 @@
|
|||
"pk": 4,
|
||||
"fields": {
|
||||
"oss": 41,
|
||||
"parent": null,
|
||||
"operation_type": "input",
|
||||
"result": 40,
|
||||
"alias": "КС Процессы",
|
||||
"title": "Процессы",
|
||||
"description": "",
|
||||
"position_x": 890.0,
|
||||
"position_y": 370.0
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -7452,13 +7449,12 @@
|
|||
"pk": 9,
|
||||
"fields": {
|
||||
"oss": 41,
|
||||
"parent": null,
|
||||
"operation_type": "synthesis",
|
||||
"result": 43,
|
||||
"alias": "КС Объект-сред",
|
||||
"title": "Объектная среда",
|
||||
"description": "",
|
||||
"position_x": 620.0,
|
||||
"position_y": 470.0
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -7466,13 +7462,49 @@
|
|||
"pk": 10,
|
||||
"fields": {
|
||||
"oss": 41,
|
||||
"parent": null,
|
||||
"operation_type": "synthesis",
|
||||
"result": 44,
|
||||
"alias": "КС Проц-сред",
|
||||
"title": "Процессные среды",
|
||||
"description": "",
|
||||
"position_x": 760.0,
|
||||
"position_y": 570.0
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "oss.layout",
|
||||
"pk": 1,
|
||||
"fields": {
|
||||
"oss": 41,
|
||||
"data": {
|
||||
"blocks": [],
|
||||
"operations": [
|
||||
{
|
||||
"x": 530.0,
|
||||
"y": 370.0,
|
||||
"id": 1
|
||||
},
|
||||
{
|
||||
"x": 710.0,
|
||||
"y": 370.0,
|
||||
"id": 2
|
||||
},
|
||||
{
|
||||
"x": 890.0,
|
||||
"y": 370.0,
|
||||
"id": 4
|
||||
},
|
||||
{
|
||||
"x": 620.0,
|
||||
"y": 470.0,
|
||||
"id": 9
|
||||
},
|
||||
{
|
||||
"x": 760.0,
|
||||
"y": 570.0,
|
||||
"id": 10
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
tzdata==2025.2
|
||||
Django==5.1.7
|
||||
djangorestframework==3.15.2
|
||||
Django==5.2.1
|
||||
djangorestframework==3.16.0
|
||||
django-cors-headers==4.7.0
|
||||
django-filter==25.1
|
||||
drf-spectacular==0.28.0
|
||||
drf-spectacular-sidecar==2025.3.1
|
||||
drf-spectacular-sidecar==2025.5.1
|
||||
coreapi==2.3.3
|
||||
django-rest-passwordreset==1.5.0
|
||||
cctext==0.1.4
|
||||
|
@ -13,9 +13,9 @@ pyconcept==0.1.12
|
|||
psycopg2-binary==2.9.10
|
||||
gunicorn==23.0.0
|
||||
|
||||
djangorestframework-stubs==3.15.3
|
||||
django-extensions==3.2.3
|
||||
django-stubs==5.1.3
|
||||
djangorestframework-stubs==3.16.0
|
||||
django-extensions==4.1
|
||||
django-stubs==5.2.0
|
||||
mypy==1.15.0
|
||||
pylint==3.3.6
|
||||
coverage==7.7.1
|
||||
pylint==3.3.7
|
||||
coverage==7.8.2
|
|
@ -1,10 +1,10 @@
|
|||
tzdata==2025.2
|
||||
Django==5.1.7
|
||||
djangorestframework==3.15.2
|
||||
Django==5.2.1
|
||||
djangorestframework==3.16.0
|
||||
django-cors-headers==4.7.0
|
||||
django-filter==25.1
|
||||
drf-spectacular==0.28.0
|
||||
drf-spectacular-sidecar==2025.3.1
|
||||
drf-spectacular-sidecar==2025.5.1
|
||||
coreapi==2.3.3
|
||||
django-rest-passwordreset==1.5.0
|
||||
cctext==0.1.4
|
||||
|
|
3035
rsconcept/frontend/package-lock.json
generated
3035
rsconcept/frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
|
@ -4,7 +4,7 @@
|
|||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"generate": "lezer-generator src/components/rs-input/rslang/rslang-fast.grammar -o src/components/rs-input/rslang/parser.ts && lezer-generator src/components/rs-input/rslang/rslang-ast.grammar -o src/components/rs-input/rslang/parserAST.ts && lezer-generator src/components/refs-input/parse/refs-text.grammar -o src/components/refs-input/parse/parser.ts",
|
||||
"generate": "lezer-generator src/features/rsform/components/rs-input/rslang/rslang-fast.grammar -o src/features/rsform/components/rs-input/rslang/parser.ts && lezer-generator src/features/rsform/components/rs-input/rslang/rslang-ast.grammar -o src/features/rsform/components/rs-input/rslang/parser-ast.ts && lezer-generator src/features/rsform/components/refs-input/parse/refs-text.grammar -o src/features/rsform/components/refs-input/parse/parser.ts",
|
||||
"test": "jest",
|
||||
"test:e2e": "playwright test",
|
||||
"dev": "vite --host",
|
||||
|
@ -17,10 +17,10 @@
|
|||
"@dagrejs/dagre": "^1.1.4",
|
||||
"@hookform/resolvers": "^5.0.1",
|
||||
"@lezer/lr": "^1.4.2",
|
||||
"@radix-ui/react-popover": "^1.1.13",
|
||||
"@radix-ui/react-select": "^2.2.4",
|
||||
"@tanstack/react-query": "^5.76.0",
|
||||
"@tanstack/react-query-devtools": "^5.76.0",
|
||||
"@radix-ui/react-popover": "^1.1.14",
|
||||
"@radix-ui/react-select": "^2.2.5",
|
||||
"@tanstack/react-query": "^5.80.5",
|
||||
"@tanstack/react-query-devtools": "^5.80.5",
|
||||
"@tanstack/react-table": "^8.21.3",
|
||||
"@uiw/codemirror-themes": "^4.23.12",
|
||||
"@uiw/react-codemirror": "^4.23.12",
|
||||
|
@ -30,56 +30,56 @@
|
|||
"cmdk": "^1.1.1",
|
||||
"global": "^4.4.0",
|
||||
"js-file-download": "^0.4.12",
|
||||
"lucide-react": "^0.510.0",
|
||||
"lucide-react": "^0.511.0",
|
||||
"qrcode.react": "^4.2.0",
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0",
|
||||
"react-error-boundary": "^6.0.0",
|
||||
"react-hook-form": "^7.56.3",
|
||||
"react-hook-form": "^7.57.0",
|
||||
"react-icons": "^5.5.0",
|
||||
"react-intl": "^7.1.11",
|
||||
"react-router": "^7.6.0",
|
||||
"react-scan": "^0.3.3",
|
||||
"react-router": "^7.6.2",
|
||||
"react-scan": "^0.3.4",
|
||||
"react-tabs": "^6.1.0",
|
||||
"react-toastify": "^11.0.5",
|
||||
"react-tooltip": "^5.28.1",
|
||||
"react-zoom-pan-pinch": "^3.7.0",
|
||||
"reactflow": "^11.11.4",
|
||||
"tailwind-merge": "^3.3.0",
|
||||
"tw-animate-css": "^1.2.9",
|
||||
"tw-animate-css": "^1.3.4",
|
||||
"use-debounce": "^10.0.4",
|
||||
"zod": "^3.24.4",
|
||||
"zustand": "^5.0.4"
|
||||
"zod": "^3.25.51",
|
||||
"zustand": "^5.0.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@lezer/generator": "^1.7.3",
|
||||
"@playwright/test": "^1.52.0",
|
||||
"@tailwindcss/vite": "^4.1.6",
|
||||
"@tailwindcss/vite": "^4.1.8",
|
||||
"@types/jest": "^29.5.14",
|
||||
"@types/node": "^22.15.17",
|
||||
"@types/react": "^19.1.4",
|
||||
"@types/react-dom": "^19.1.5",
|
||||
"@types/node": "^22.15.29",
|
||||
"@types/react": "^19.1.6",
|
||||
"@types/react-dom": "^19.1.6",
|
||||
"@typescript-eslint/eslint-plugin": "^8.0.1",
|
||||
"@typescript-eslint/parser": "^8.0.1",
|
||||
"@vitejs/plugin-react": "^4.4.1",
|
||||
"@vitejs/plugin-react": "^4.5.1",
|
||||
"babel-plugin-react-compiler": "^19.1.0-rc.1",
|
||||
"eslint": "^9.26.0",
|
||||
"eslint": "^9.28.0",
|
||||
"eslint-plugin-import": "^2.31.0",
|
||||
"eslint-plugin-playwright": "^2.2.0",
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
"eslint-plugin-react-compiler": "^19.1.0-rc.1",
|
||||
"eslint-plugin-react-hooks": "^5.2.0",
|
||||
"eslint-plugin-simple-import-sort": "^12.1.1",
|
||||
"globals": "^16.1.0",
|
||||
"globals": "^16.2.0",
|
||||
"jest": "^29.7.0",
|
||||
"stylelint": "^16.19.1",
|
||||
"stylelint": "^16.20.0",
|
||||
"stylelint-config-recommended": "^16.0.0",
|
||||
"stylelint-config-standard": "^38.0.0",
|
||||
"stylelint-config-tailwindcss": "^1.0.0",
|
||||
"tailwindcss": "^4.0.7",
|
||||
"ts-jest": "^29.3.2",
|
||||
"ts-jest": "^29.3.4",
|
||||
"typescript": "^5.8.3",
|
||||
"typescript-eslint": "^8.32.1",
|
||||
"typescript-eslint": "^8.33.1",
|
||||
"vite": "^6.3.5"
|
||||
},
|
||||
"jest": {
|
||||
|
|
|
@ -47,6 +47,7 @@ export function TextArea({
|
|||
<textarea
|
||||
id={id}
|
||||
className={cn(
|
||||
'min-h-0',
|
||||
'px-3 py-2',
|
||||
'leading-tight',
|
||||
'overflow-x-hidden overflow-y-auto',
|
||||
|
|
|
@ -19,8 +19,8 @@ function useTokenValidation(token: string, isPending: boolean) {
|
|||
|
||||
const validate = async () => {
|
||||
if (!isTokenValidating && !isPending) {
|
||||
await validateToken({ token });
|
||||
setIsTokenValidating(true);
|
||||
await validateToken({ token });
|
||||
}
|
||||
};
|
||||
return { isTokenValidating, validate };
|
||||
|
@ -44,10 +44,7 @@ export function Component() {
|
|||
void resetPassword({
|
||||
password: newPassword,
|
||||
token: token
|
||||
}).then(() => {
|
||||
router.replace({ path: urls.home });
|
||||
router.push({ path: urls.login });
|
||||
});
|
||||
}).then(() => router.replace({ path: urls.login }));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -83,6 +80,9 @@ export function Component() {
|
|||
setNewPasswordRepeat(event.target.value);
|
||||
}}
|
||||
/>
|
||||
{newPasswordRepeat && newPassword !== newPasswordRepeat ? (
|
||||
<div className='text-sm text-destructive'>Пароли не совпадают</div>
|
||||
) : null}
|
||||
|
||||
<SubmitButton
|
||||
text='Установить пароль'
|
||||
|
|
|
@ -35,6 +35,7 @@ export function Component() {
|
|||
<form className='cc-column w-96 mx-auto px-6 mt-3' onSubmit={handleSubmit} onChange={clearServerError}>
|
||||
<TextInput
|
||||
id='email'
|
||||
type='email'
|
||||
autoComplete='email'
|
||||
required
|
||||
allowEnter
|
||||
|
|
|
@ -20,24 +20,26 @@ export function HelpConceptSynthesis() {
|
|||
<p>
|
||||
Расширение выразительной способности достигается несколькими способами в зависимости от соотношения
|
||||
синтезируемых точек зрения:
|
||||
<li>
|
||||
<b>аспектный синтез</b> характеризуется отождествлением общих понятий в случае, когда часть неопределяемых
|
||||
понятий является общей для двух точек зрения;
|
||||
</li>
|
||||
<li>
|
||||
<b>конкретизирующий синтез</b> заменяет неопределяемое слабо ограниченное понятия из одной схемы на более
|
||||
ограниченное, конкретное базовое или производное понятие из другой схемы;
|
||||
</li>
|
||||
<li>
|
||||
<b>синтез через новое новое отношение</b> помимо исходных схем использует абстрактную (не имеющую предметной
|
||||
интерпретации) схему для соединения понятий из двух операндов путем введения нового неопределяемого понятия,
|
||||
моделирующего отношения между синтезируемыми схемами.
|
||||
</li>
|
||||
<ul>
|
||||
<li>
|
||||
<b>аспектный синтез</b> характеризуется отождествлением общих понятий в случае, когда часть неопределяемых
|
||||
понятий является общей для двух точек зрения;
|
||||
</li>
|
||||
<li>
|
||||
<b>конкретизирующий синтез</b> заменяет неопределяемое слабо ограниченное понятия из одной схемы на более
|
||||
ограниченное, конкретное базовое или производное понятие из другой схемы;
|
||||
</li>
|
||||
<li>
|
||||
<b>синтез через новое новое отношение</b> помимо исходных схем использует абстрактную (не имеющую предметной
|
||||
интерпретации) схему для соединения понятий из двух операндов путем введения нового неопределяемого понятия,
|
||||
моделирующего отношения между синтезируемыми схемами.
|
||||
</li>
|
||||
</ul>
|
||||
</p>
|
||||
<p>
|
||||
Возможно использование комбинации описанных подходов в рамках одного синтеза. Более подробно про реализацию
|
||||
операций в родоструктурной форме можно прочитать в{' '}
|
||||
<LinkTopic text='разделе Операции' topic={HelpTopic.RSL_OPERATIONS} />
|
||||
Поддерживается использование комбинации описанных подходов в рамках одного синтеза. Более подробно про
|
||||
реализацию операций в родоструктурной форме можно прочитать в{' '}
|
||||
<LinkTopic text='разделе Операции' topic={HelpTopic.RSL_OPERATIONS} />.
|
||||
</p>
|
||||
<p>
|
||||
Для управления совокупностью синтезов используются{' '}
|
||||
|
|
|
@ -9,23 +9,25 @@ export function HelpAccess() {
|
|||
<p>
|
||||
Доступ к контенту на Портале может быть ограничен владельцем каждой схемы в рамках <b>политики доступа</b>.
|
||||
</p>
|
||||
<li>
|
||||
<IconPublic className='inline-icon icon-green' /> публичная политика не ограничивает чтение схемы
|
||||
</li>
|
||||
<li>
|
||||
<IconProtected className='inline-icon icon-blue' /> защитная политика запрещает доступ для всех кроме редакторов
|
||||
и владельца схемы
|
||||
</li>
|
||||
<li>
|
||||
<IconPrivate className='inline-icon icon-red' /> личная политика оставляет доступ к схеме только владельцу
|
||||
</li>
|
||||
<li>
|
||||
<IconHide className='inline-icon' /> режим скрытия схемы из списка в Библиотеке не ограничивает доступ к схеме
|
||||
по прямой ссылке
|
||||
</li>
|
||||
<li>
|
||||
<IconImmutable className='inline-icon' /> режим защиты от редактирования предохраняет от случайных изменений
|
||||
</li>
|
||||
<ul>
|
||||
<li>
|
||||
<IconPublic className='inline-icon icon-green' /> публичная политика не ограничивает чтение схемы
|
||||
</li>
|
||||
<li>
|
||||
<IconProtected className='inline-icon icon-blue' /> защитная политика запрещает доступ для всех кроме
|
||||
редакторов и владельца схемы
|
||||
</li>
|
||||
<li>
|
||||
<IconPrivate className='inline-icon icon-red' /> личная политика оставляет доступ к схеме только владельцу
|
||||
</li>
|
||||
<li>
|
||||
<IconHide className='inline-icon' /> режим скрытия схемы из списка в Библиотеке не ограничивает доступ к схеме
|
||||
по прямой ссылке
|
||||
</li>
|
||||
<li>
|
||||
<IconImmutable className='inline-icon' /> режим защиты от редактирования предохраняет от случайных изменений
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -24,17 +24,19 @@ export function HelpExteor() {
|
|||
</p>
|
||||
|
||||
<h2>Основные функции</h2>
|
||||
<li>Работа с РС-формой системы понятий</li>
|
||||
<li>Автоматическое определение типизации выражений</li>
|
||||
<li>Проверка корректности РС-формы</li>
|
||||
<li>Контекстный поиск с учетом словоформ терминов</li>
|
||||
<li>Терминологический контроль вхождений терминов</li>
|
||||
<li>Автоматическое выполнение операций синтеза РС-форм</li>
|
||||
<li>Синтез с помощью операционной схемы синтеза (ОСС)</li>
|
||||
<li>Автоматическое сквозное внесение изменений в ОСС</li>
|
||||
<li>Вычисление объектной интерпретации</li>
|
||||
<li>Выгрузка концептуальных схем в Word</li>
|
||||
<li>Импорт/экспорт интерпретаций через Excel</li>
|
||||
<ul>
|
||||
<li>Работа с РС-формой системы понятий</li>
|
||||
<li>Автоматическое определение типизации выражений</li>
|
||||
<li>Проверка корректности РС-формы</li>
|
||||
<li>Контекстный поиск с учетом словоформ терминов</li>
|
||||
<li>Терминологический контроль вхождений терминов</li>
|
||||
<li>Автоматическое выполнение операций синтеза РС-форм</li>
|
||||
<li>Синтез с помощью операционной схемы синтеза (ОСС)</li>
|
||||
<li>Автоматическое сквозное внесение изменений в ОСС</li>
|
||||
<li>Вычисление объектной интерпретации</li>
|
||||
<li>Выгрузка концептуальных схем в Word</li>
|
||||
<li>Импорт/экспорт интерпретаций через Excel</li>
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -31,32 +31,34 @@ export function HelpInterface() {
|
|||
</p>
|
||||
|
||||
<h2>Навигация и настройки</h2>
|
||||
<li>
|
||||
<kbd>Ctrl + клик</kbd> на объект навигации откроет новую вкладку
|
||||
</li>
|
||||
<li>
|
||||
<IconPin size='1.25rem' className='inline-icon' /> навигационную панель можно скрыть с помощью кнопки в правом
|
||||
верхнем углу
|
||||
</li>
|
||||
<li>
|
||||
<IconLightTheme className='inline-icon' />
|
||||
<IconDarkTheme className='inline-icon' /> переключатели темы
|
||||
</li>
|
||||
<li>
|
||||
<IconLogin size='1.25rem' className='inline-icon' /> вход в систему / регистрация нового пользователя
|
||||
</li>
|
||||
<li>
|
||||
<IconUser2 size='1.25rem' className='inline-icon' /> меню пользователя содержит ряд настроек и переход к профилю
|
||||
пользователя
|
||||
</li>
|
||||
<ul>
|
||||
<li>
|
||||
<kbd>Ctrl + клик</kbd> на объект навигации откроет новую вкладку
|
||||
</li>
|
||||
<li>
|
||||
<IconPin size='1.25rem' className='inline-icon' /> навигационную панель можно скрыть с помощью кнопки в правом
|
||||
верхнем углу
|
||||
</li>
|
||||
<li>
|
||||
<IconLightTheme className='inline-icon' />
|
||||
<IconDarkTheme className='inline-icon' /> переключатели темы
|
||||
</li>
|
||||
<li>
|
||||
<IconLogin size='1.25rem' className='inline-icon' /> вход в систему / регистрация нового пользователя
|
||||
</li>
|
||||
<li>
|
||||
<IconUser2 size='1.25rem' className='inline-icon' /> меню пользователя содержит ряд настроек и переход к
|
||||
профилю пользователя
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<IconHelp className='inline-icon' />
|
||||
<IconHelpOff className='inline-icon' /> отключение иконок контекстной справки
|
||||
</li>
|
||||
<li>
|
||||
<IconLogout className='inline-icon' /> выход из системы
|
||||
</li>
|
||||
<li>
|
||||
<IconHelp className='inline-icon' />
|
||||
<IconHelpOff className='inline-icon' /> отключение иконок контекстной справки
|
||||
</li>
|
||||
<li>
|
||||
<IconLogout className='inline-icon' /> выход из системы
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<Subtopics headTopic={HelpTopic.INTERFACE} />
|
||||
</div>
|
||||
|
|
|
@ -22,34 +22,38 @@ export function HelpMain() {
|
|||
|
||||
<details>
|
||||
<summary className='text-center font-semibold'>Разделы Справки</summary>
|
||||
{[
|
||||
HelpTopic.THESAURUS,
|
||||
HelpTopic.INTERFACE,
|
||||
HelpTopic.CONCEPTUAL,
|
||||
HelpTopic.RSLANG,
|
||||
HelpTopic.TERM_CONTROL,
|
||||
HelpTopic.ACCESS,
|
||||
HelpTopic.VERSIONS,
|
||||
HelpTopic.INFO,
|
||||
HelpTopic.EXTEOR
|
||||
].map(topic => (
|
||||
<TopicItem key={`${prefixes.topic_item}${topic}`} topic={topic} />
|
||||
))}
|
||||
<ul>
|
||||
{[
|
||||
HelpTopic.THESAURUS,
|
||||
HelpTopic.INTERFACE,
|
||||
HelpTopic.CONCEPTUAL,
|
||||
HelpTopic.RSLANG,
|
||||
HelpTopic.TERM_CONTROL,
|
||||
HelpTopic.ACCESS,
|
||||
HelpTopic.VERSIONS,
|
||||
HelpTopic.INFO,
|
||||
HelpTopic.EXTEOR
|
||||
].map(topic => (
|
||||
<TopicItem key={`${prefixes.topic_item}${topic}`} topic={topic} />
|
||||
))}
|
||||
</ul>
|
||||
</details>
|
||||
|
||||
<h2>Лицензирование и раскрытие информации</h2>
|
||||
<li>Пользователи Портала сохраняют авторские права на создаваемый ими контент</li>
|
||||
<li>
|
||||
Политика обработки данных доступна по <LinkTopic text='ссылке' topic={HelpTopic.INFO_PRIVACY} />
|
||||
</li>
|
||||
<li>
|
||||
Портал является проектом с открытым исходным кодом, доступным на{' '}
|
||||
<TextURL text='Github' href={external_urls.git_portal} />
|
||||
</li>
|
||||
<li>
|
||||
Данный сайт использует доменное имя и серверные мощности{' '}
|
||||
<TextURL text='Центра Концепт' href={external_urls.concept} />
|
||||
</li>
|
||||
<ul>
|
||||
<li>Пользователи Портала сохраняют авторские права на создаваемый ими контент</li>
|
||||
<li>
|
||||
Политика обработки данных доступна по <LinkTopic text='ссылке' topic={HelpTopic.INFO_PRIVACY} />
|
||||
</li>
|
||||
<li>
|
||||
Портал является проектом с открытым исходным кодом, доступным на{' '}
|
||||
<TextURL text='Github' href={external_urls.git_portal} />
|
||||
</li>
|
||||
<li>
|
||||
Данный сайт использует доменное имя и серверные мощности{' '}
|
||||
<TextURL text='Центра Концепт' href={external_urls.concept} />
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h2>Поддержка</h2>
|
||||
<p>
|
||||
|
|
|
@ -11,20 +11,22 @@ export function HelpVersions() {
|
|||
<p>После создания версии ее содержание изменить нельзя.</p>
|
||||
|
||||
<h2>Действия</h2>
|
||||
<li>
|
||||
<IconShare size='1.25rem' className='inline-icon' /> Поделиться включает версию в ссылку
|
||||
</li>
|
||||
<li>
|
||||
<IconUpload size='1.25rem' className='inline-icon icon-red' /> Загрузить версию в актуальную схему
|
||||
</li>
|
||||
<li>
|
||||
<IconNewVersion size='1.25rem' className='inline-icon icon-green' /> Создать версию можно только из актуальной
|
||||
схемы
|
||||
</li>
|
||||
<ul>
|
||||
<li>
|
||||
<IconShare size='1.25rem' className='inline-icon' /> Поделиться включает версию в ссылку
|
||||
</li>
|
||||
<li>
|
||||
<IconUpload size='1.25rem' className='inline-icon icon-red' /> Загрузить версию в актуальную схему
|
||||
</li>
|
||||
<li>
|
||||
<IconNewVersion size='1.25rem' className='inline-icon icon-green' /> Создать версию можно только из актуальной
|
||||
схемы
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<IconVersions size='1.25rem' className='inline-icon' /> Редактировать атрибуты версий
|
||||
</li>
|
||||
<li>
|
||||
<IconVersions size='1.25rem' className='inline-icon' /> Редактировать атрибуты версий
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -17,8 +17,8 @@ export function HelpContributors() {
|
|||
В списке указан год окончания работ над соответствующим результатом или год публикации соответствующей статьи.
|
||||
Курсивом выделены комментарии к значимости указанного результата.
|
||||
</p>
|
||||
<p>Любые добавления и поправки приветствуются.</p>
|
||||
<div className='flex flex-col gap-3'>
|
||||
<p>Добавления и корректировки приветствуются.</p>
|
||||
<ul className='flex flex-col gap-3'>
|
||||
<li>1973 Никаноров С.П., Персиц Д.Б. Формальное проектирование целостных СОУ.</li>
|
||||
<li>
|
||||
1975–1981 Никаноров С.П., Персиц Д.Б., Айзенштат А.В., Закс Б.А. Экспериментальная система пакетов прикладных
|
||||
|
@ -48,18 +48,18 @@ export function HelpContributors() {
|
|||
решений.
|
||||
</li>
|
||||
<li>
|
||||
1989 Кучкаров З.А., Остапов А.В. Методические вопросы концептуализации предметных областей,{' '}
|
||||
1989 Остапов А.В., Кучкаров З.А. Методические вопросы концептуализации предметных областей,{' '}
|
||||
<i>
|
||||
как пример одной из работ Остапова, значительно расширившего технику экспликации и практику применения
|
||||
"бескванторных" выражений.
|
||||
</i>
|
||||
</li>
|
||||
<li>
|
||||
1990 Никитина Н.К., Постников В.В. Синтаксический анализатор текста рода структуры для МАКС,{' '}
|
||||
1990 Постников В.В., Никитина Н.К. Синтаксический анализатор текста рода структуры для МАКС,{' '}
|
||||
<i>являющийся первой попыткой реализовать автоматизированную проверку синтаксиса родов структур.</i>
|
||||
</li>
|
||||
<li>
|
||||
1993 Костюк А.В., Никитина Н.К., Юдкин Ю.Ю. Программа визуализации М-графов, представляющих родовую структуру.
|
||||
1993 Юдкин Ю.Ю., Костюк А.В., Никитина Н.К. Программа визуализации М-графов, представляющих родовую структуру.
|
||||
</li>
|
||||
<li>1993 Никитина Н.К., Чувашов Е.В. Система проектирования баз данных по их концептуальной модели.</li>
|
||||
<li>
|
||||
|
@ -72,11 +72,11 @@ export function HelpContributors() {
|
|||
PROLOG-программ, формирующих предметные интерпретации родоструктурных экспликаций Инттеор.
|
||||
</li>
|
||||
<li>
|
||||
1994 Кучкаров З.А., Ким В.Л. Разработка родоструктурных конструктов для библиотеки моделей и исследование
|
||||
1994 Ким В.Л., Кучкаров З.А. Разработка родоструктурных конструктов для библиотеки моделей и исследование
|
||||
возможностей их развития.
|
||||
</li>
|
||||
<li>
|
||||
1994 Коваль А.Г., Воробей П.Н. Редактор Программного комплекса Экстеор 1.5,{' '}
|
||||
1994 Воробей П.Н., Коваль А.Г. Редактор Программного комплекса Экстеор 1.5,{' '}
|
||||
<i>упростивший механизм печати экспликаций и улучшивший синтаксический анализ формального выражения.</i>
|
||||
</li>
|
||||
<li>
|
||||
|
@ -84,16 +84,17 @@ export function HelpContributors() {
|
|||
родоструктурного синтеза операционализированных терминальных концептуальных моделей Экстеор 2,{' '}
|
||||
<i>ставшая первой версией реализации родоструктурного аппарата на C++ под Windows.</i>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
1996 Никаноров С.П., Никитина Н.К., Климишин В.В. Автоматизированная система "Библиотека концептуальных схем",{' '}
|
||||
1996 Климишин В.В., Никаноров С.П., Никитина Н.К. Автоматизированная система "Библиотека концептуальных схем",{' '}
|
||||
<i>впервые определившая паспорт концептуальной схемы.</i>
|
||||
</li>
|
||||
<li>
|
||||
1997 Никитина Н.К., Юрьев О.И. Система поддержки процессов концептуального анализа и проектирования ПРОКСИМА
|
||||
1997 Юрьев О.И., Никитина Н.К. Система поддержки процессов концептуального анализа и проектирования ПРОКСИМА
|
||||
1.
|
||||
</li>
|
||||
<li>
|
||||
1998 Никитина Н.К., Гараева Ю.Р. Синтаксический анализатор выражений на языке родоструктурной экспликации для
|
||||
1998 Гараева Ю.Р., Никитина Н.К. Синтаксический анализатор выражений на языке родоструктурной экспликации для
|
||||
ПРОКСИМА 1.
|
||||
</li>
|
||||
<li>
|
||||
|
@ -101,12 +102,12 @@ export function HelpContributors() {
|
|||
концептуального проектирования.
|
||||
</li>
|
||||
<li>
|
||||
1999 Кучкаров З.А., Кононенко А.А. Программа преобразования родоструктурного синтеза операционализированных
|
||||
1999 Кононенко А.А., Кучкаров З.А. Программа преобразования родоструктурного синтеза операционализированных
|
||||
терминальных концептуальных моделей Экстеор 3,{' '}
|
||||
<i>впервые включившая операционную схему синтеза (дерево синтеза).</i>
|
||||
</li>
|
||||
<li>
|
||||
1999 Никитина Н.К., Ландин Н.А. Разработка автоматизированной подсистемы, реализующей операции отслоения и
|
||||
1999 Ландин Н.А., Никитина Н.К. Разработка автоматизированной подсистемы, реализующей операции отслоения и
|
||||
рассечения над концептуальными схемами.
|
||||
</li>
|
||||
<li>
|
||||
|
@ -122,7 +123,7 @@ export function HelpContributors() {
|
|||
</i>
|
||||
</li>
|
||||
<li>
|
||||
2000 Кононенко А.А., Майоров В.А. Программа автоматизированной генерации структуры данных и их визуализации по
|
||||
2000 Майоров В.А., Кононенко А.А. Программа автоматизированной генерации структуры данных и их визуализации по
|
||||
концептуальной модели БДтеор,{' '}
|
||||
<i>
|
||||
определившая проблемы интерфейса наполнения концептуальной модели в сложных ступенях и предложившая
|
||||
|
@ -136,11 +137,11 @@ export function HelpContributors() {
|
|||
</li>
|
||||
<li>2000 Ключников А.В. Эквивалентность теорий родов структур.</li>
|
||||
<li>
|
||||
2001 Кучкаров З.А., Никитин А.В. Исследование и построение типологии изменений теоретико-множественных
|
||||
2001 Никитин А.В., Кучкаров З.А. Исследование и построение типологии изменений теоретико-множественных
|
||||
интерпретаций класса декартового произведения.
|
||||
</li>
|
||||
<li>
|
||||
2001 Кононенко А.А., Майоров В.А. Программа преобразования сети процедур из формата Оргтеор в формат BPWin
|
||||
2001 Майоров В.А., Кононенко А.А. Программа преобразования сети процедур из формата Оргтеор в формат BPWin
|
||||
(IDEF0).
|
||||
</li>
|
||||
<li>
|
||||
|
@ -158,6 +159,7 @@ export function HelpContributors() {
|
|||
2003 Юдкин Ю.Ю., Кудюкин Д.А. Разработка и испытание компьютерной программы, формирующей
|
||||
теоретико-множественную интерпретацию терма частной родоструктурной теории.
|
||||
</li>
|
||||
|
||||
<li>
|
||||
2004 Кононенко А.А. Генерация кода на языке программирования C++ по тексту концептуальной схемы,
|
||||
эксплицированной в родах структур.
|
||||
|
@ -182,25 +184,25 @@ export function HelpContributors() {
|
|||
2008 Пономарев И.Н. Об эквивалентной представимости рода структуры с помощью заданной типовой характеристики.
|
||||
</li>
|
||||
<li>
|
||||
2010 Кононенко А.А., Грязнов А.Д. Исследование и построение транслятора концептуальной схемы в концептуальную
|
||||
2010 Грязнов А.Д., Кононенко А.А. Исследование и построение транслятора концептуальной схемы в концептуальную
|
||||
модель.
|
||||
</li>
|
||||
<li>2010 Никаноров С.П. Введение в аппарат ступеней.</li>
|
||||
<li>
|
||||
2012 Кононенко А.А., Елисов Д.Н. Использование механизма XSD-схем для хранения и операционализации
|
||||
2012 Елисов Д.Н., Кононенко А.А. Использование механизма XSD-схем для хранения и операционализации
|
||||
концептуальных схем и концептуальных моделей с помощью XML.
|
||||
</li>
|
||||
<li>
|
||||
2013 Кононенко А.А., Борисов И.Р. Исследование, разработка и экспериментальная программная реализация операций
|
||||
2013 Борисов И.Р., Кононенко А.А. Исследование, разработка и экспериментальная программная реализация операций
|
||||
над концептуальными моделями,{' '}
|
||||
<i>
|
||||
впервые реализовавшая модуль прямого вычисления интерпретации формального выражения, встроенный в Экстеор
|
||||
3.5.
|
||||
</i>
|
||||
</li>
|
||||
<li>2013 Пономарев И.Н., Липатов А.А. Операции над родами структур и пример автоматизации их выполнения.</li>
|
||||
<li>2013 Липатов А.А., Пономарев И.Н. Операции над родами структур и пример автоматизации их выполнения.</li>
|
||||
<li>
|
||||
2014 Борисов И.Р., Баширов Р.М. Исследования и программная реализации оптимальной структуры данных для
|
||||
2014 Баширов Р.М., Борисов И.Р. Исследования и программная реализации оптимальной структуры данных для
|
||||
вычисления интерпретации концептуальных схем.
|
||||
</li>
|
||||
<li>
|
||||
|
@ -220,7 +222,7 @@ export function HelpContributors() {
|
|||
</li>
|
||||
<li>2015 Иванов А.Ю. Аппарат ступеней С.П. Никанорова и возможное развитие идей по его использованию.</li>
|
||||
<li>
|
||||
2016 Борисов И.Р., Баширов Р.М. Исследование области компьютерной лингвистики и разработка модулей
|
||||
2016 Баширов Р.М., Борисов И.Р. Исследование области компьютерной лингвистики и разработка модулей
|
||||
терминологического контроля в Экстеор 4 и Microsoft Office Word,{' '}
|
||||
<i>
|
||||
являющееся основой библиотеки <TextURL text='cctext' href={external_urls.git_cctext} />.
|
||||
|
@ -239,25 +241,25 @@ export function HelpContributors() {
|
|||
теории (на примере родственных отношений).
|
||||
</li>
|
||||
<li>
|
||||
2017 Борисов И.Р., Мурадов А.К. Организация операций над системами понятий посредством графических
|
||||
2017 Мурадов А.К., Борисов И.Р. Организация операций над системами понятий посредством графических
|
||||
интерфейсов, <i>заложивший основу для технологии Концепт.Блоки и блока графического синтеза.</i>
|
||||
</li>
|
||||
<li>
|
||||
2018 Борисов И.Р., Князев А.В. Изучение методов концептуальной расчистки, разметки текстов и разработка
|
||||
2018 Князев А.В., Борисов И.Р. Изучение методов концептуальной расчистки, разметки текстов и разработка
|
||||
программных средств их автоматизации,{' '}
|
||||
<i> — диплом, сформировавший основу для технологий Концепт.Разметка и Концепт.Майнинг.</i>
|
||||
</li>
|
||||
<li>
|
||||
2018 Никитин А.В., Болотин П.В. Исследование типологии изменения теоретико-множественной интерпретации класса
|
||||
2018 Болотин П.В., Никитин А.В. Исследование типологии изменения теоретико-множественной интерпретации класса
|
||||
множества подмножеств.
|
||||
</li>
|
||||
<li>
|
||||
2019 Борисов И.Р., Широкова Л.Р. Исследование возможностей применения методов машинного обучения для решения
|
||||
2019 Широкова Л.Р., Борисов И.Р. Исследование возможностей применения методов машинного обучения для решения
|
||||
задач расчистки текстов. Разработка прототипа программного модуля, —{' '}
|
||||
<i>первая попытка внедрения технологий ИИ в текстовый модуль.</i>
|
||||
</li>
|
||||
<li>
|
||||
2020 Борисов И.Р., Пакулина Т.А. Исследование применения методов машинного обучения для выделения именованных
|
||||
2020 Пакулина Т.А., Борисов И.Р. Исследование применения методов машинного обучения для выделения именованных
|
||||
сущностей в текстах интервью. Экспериментальная разработка программного модуля расчистки текстов,{' '}
|
||||
<i>ставшего расширением технологии Концепт.Расчистка.</i>
|
||||
</li>
|
||||
|
@ -266,12 +268,12 @@ export function HelpContributors() {
|
|||
структур (рекурсивные и императивные выражения, фильтры, ASCII синтаксис).
|
||||
</li>
|
||||
<li>
|
||||
2021 Борисов И.Р., Демешко А.Б. Исследование и разработка программного модуля формирования текстов функций на
|
||||
2021 Демешко А.Б., Борисов И.Р. Исследование и разработка программного модуля формирования текстов функций на
|
||||
основе концепта функциональная структура,{' '}
|
||||
<i>дополнившего текстовый модуль возможностью работы с глагольными формами.</i>
|
||||
</li>
|
||||
<li>
|
||||
2023 Борисов И.Р., Тулисов А.В. Разработка инструмента экспликации концептуальных схем в родоструктурной форме
|
||||
2023 Тулисов А.В., Борисов И.Р. Разработка инструмента экспликации концептуальных схем в родоструктурной форме
|
||||
через веб-интерфейс, — <i>разработка прототипа интерфейса КонцептПортал.</i>
|
||||
</li>
|
||||
<li>
|
||||
|
@ -283,11 +285,16 @@ export function HelpContributors() {
|
|||
</i>
|
||||
</li>
|
||||
<li>
|
||||
2024 Борисов И.Р., Хаданович Б.А. Исследование механизмов проведения сквозных изменений в операционной схеме
|
||||
2024 Хаданович Б.А., Борисов И.Р. Исследование механизмов проведения сквозных изменений в операционной схеме
|
||||
синтеза. Разработка прототипа веб-интерфейса синтеза концептуальных схем.
|
||||
<i> Прототип графического интерфейса для синтеза концептуальных схем.</i>
|
||||
</li>
|
||||
</div>
|
||||
<li>
|
||||
2024 Викентьев М.И., Борисов И.Р. Исследование использования современных web-интерфейсов для визуализации
|
||||
отношений для применения в рамках концептуального синтеза.{' '}
|
||||
<i> Визуализации смешанных представлений концептуальной схемы.</i>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -28,28 +28,30 @@ export function HelpRules() {
|
|||
</p>
|
||||
|
||||
<h2>Ожидаемое поведение</h2>
|
||||
<li>взаимное уважением, поддержка в отношениях с участниками Портала.</li>
|
||||
<li>
|
||||
пожелания по доработке, найденные ошибки и иные предложения следует направлять по адресу email:{' '}
|
||||
<TextURL href={external_urls.mail_portal} text='portal@acconcept.ru' />.
|
||||
</li>
|
||||
<ul>
|
||||
<li>взаимное уважением, поддержка в отношениях с участниками Портала.</li>
|
||||
<li>
|
||||
пожелания по доработке, найденные ошибки и иные предложения следует направлять по адресу email:{' '}
|
||||
<TextURL href={external_urls.mail_portal} text='portal@acconcept.ru' />.
|
||||
</li>
|
||||
|
||||
<h2>Неприемлемое поведение</h2>
|
||||
<li>оскорбления, угрозы, сексуальное домогательство, троллинг и преследование других участников.</li>
|
||||
<li>
|
||||
раскрытие персональных данных (доксинг) участников Портала. Не распространяется на персональные данные,
|
||||
раскрытые участниками для отображения на Портале. Эти данные можно изменить в{' '}
|
||||
<TextURL text='профиле' href={urls.profile} />.
|
||||
</li>
|
||||
<li>
|
||||
злоупотребление властью, привилегиями или влиянием, включая использование статусов и доступов, предоставленных
|
||||
Порталом в личных целях, не связанных с разработкой контента, развитием и продвижением Портала.
|
||||
</li>
|
||||
<li>
|
||||
вандализм, намеренное добавление неуместного контента, или препятствование, затруднение или другого рода
|
||||
осложнение создания (и/или поддержания) контента, созданного другими участниками.
|
||||
</li>
|
||||
<li>нарушение работоспособности Портала, в том числе путем использования уязвимостей и ошибок в коде.</li>
|
||||
<h2>Неприемлемое поведение</h2>
|
||||
<li>оскорбления, угрозы, сексуальное домогательство, троллинг и преследование других участников.</li>
|
||||
<li>
|
||||
раскрытие персональных данных (доксинг) участников Портала. Не распространяется на персональные данные,
|
||||
раскрытые участниками для отображения на Портале. Эти данные можно изменить в{' '}
|
||||
<TextURL text='профиле' href={urls.profile} />.
|
||||
</li>
|
||||
<li>
|
||||
злоупотребление властью, привилегиями или влиянием, включая использование статусов и доступов, предоставленных
|
||||
Порталом в личных целях, не связанных с разработкой контента, развитием и продвижением Портала.
|
||||
</li>
|
||||
<li>
|
||||
вандализм, намеренное добавление неуместного контента, или препятствование, затруднение или другого рода
|
||||
осложнение создания (и/или поддержания) контента, созданного другими участниками.
|
||||
</li>
|
||||
<li>нарушение работоспособности Портала, в том числе путем использования уязвимостей и ошибок в коде.</li>
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ export function HelpRSLangOperations() {
|
|||
<h2>
|
||||
<IconSortList size='1.25rem' className='inline-icon' /> Упорядочение
|
||||
</h2>
|
||||
<p>
|
||||
<ul>
|
||||
Упорядочение списка конституент по следующим правилам
|
||||
<li>базисные и константные множества объявляются первыми</li>
|
||||
<li>
|
||||
|
@ -32,7 +32,7 @@ export function HelpRSLangOperations() {
|
|||
<LinkTopic text='порожденные' topic={HelpTopic.CC_RELATIONS} /> конституенты следуют сразу за исходной
|
||||
</li>
|
||||
<li>максимальное сохранение исходного порядка при выполнении предыдущих правил</li>
|
||||
</p>
|
||||
</ul>
|
||||
|
||||
<h2>
|
||||
<IconGenerateNames size='1.25rem' className='inline-icon' /> Порядковые имена
|
||||
|
|
|
@ -10,17 +10,19 @@ export function HelpRSLangTemplates() {
|
|||
Источником шаблонов является <b>Банк выражений</b>, содержащий параметризованные понятия и утверждения,
|
||||
сгруппированные по разделам
|
||||
</p>
|
||||
<li>Сначала выбирается шаблон выражения (вкладка Шаблон)</li>
|
||||
<li>
|
||||
Далее для аргументов можно зафиксировать значения, выбрав из конституент текущей схемы или указав выражения
|
||||
(вкладка Аргументы)
|
||||
</li>
|
||||
<li>Значения аргументов будут подставлены в выражение, включая корректировку перечня аргументов</li>
|
||||
<li>Если значения указаны для всех аргументов, то тип создаваемой конституенты будет автоматически обновлён</li>
|
||||
<li>На вкладке Конституента можно скорректировать все атрибуты, создаваемой конституенты</li>
|
||||
<li>
|
||||
Кнопка <b>Создать</b> инициирует добавление выбранной конституенты в схему
|
||||
</li>
|
||||
<ul>
|
||||
<li>Сначала выбирается шаблон выражения (вкладка Шаблон)</li>
|
||||
<li>
|
||||
Далее для аргументов можно зафиксировать значения, выбрав из конституент текущей схемы или указав выражения
|
||||
(вкладка Аргументы)
|
||||
</li>
|
||||
<li>Значения аргументов будут подставлены в выражение, включая корректировку перечня аргументов</li>
|
||||
<li>Если значения указаны для всех аргументов, то тип создаваемой конституенты будет автоматически обновлён</li>
|
||||
<li>На вкладке Конституента можно скорректировать все атрибуты, создаваемой конституенты</li>
|
||||
<li>
|
||||
Кнопка <b>Создать</b> инициирует добавление выбранной конституенты в схему
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ export function HelpRSLangTypes() {
|
|||
return (
|
||||
<div>
|
||||
<h1>Типизация</h1>
|
||||
<p>
|
||||
<ul>
|
||||
Родоструктурное выражение <code>ξ</code> обладает типизацией (структурой), если выполнено ξ∈H,
|
||||
<br />
|
||||
где <code>H</code> – корректное выражение <b>ступени</b>, задаваемой следующими правилами:
|
||||
|
@ -18,7 +18,7 @@ export function HelpRSLangTypes() {
|
|||
<li>
|
||||
<code>ℬ(H)</code> – ступень, называемая <b>множеством</b>.
|
||||
</li>
|
||||
</p>
|
||||
</ul>
|
||||
<p>Пустое множество ∅ имеет типизацию ℬ(R0) – множество с произвольной структурой элемента</p>
|
||||
<p>
|
||||
Для обобщения понятия типизация на логические и параметризованные выражения вводится ряд дополнительных
|
||||
|
|
|
@ -4,29 +4,41 @@ export function HelpFormulaTree() {
|
|||
<h1>Дерево разбора выражения</h1>
|
||||
<p>Дерево получено путем семантических преобразований дерева синтаксического разбора.</p>
|
||||
<p>Оно отражает структуру грамматически корректного выражения языка родов структур.</p>
|
||||
<li>Порядок узлов в рамках одного уровня может отличаться от их порядка в выражении</li>
|
||||
<li>При наведении курсора на узел в тексте выделяется соответствующий ему фрагмент</li>
|
||||
<li>Текст в узле дерева соответствует элементу языка</li>
|
||||
|
||||
<ul>
|
||||
<li>Порядок узлов в рамках одного уровня может отличаться от их порядка в выражении</li>
|
||||
<li>При наведении курсора на узел в тексте выделяется соответствующий ему фрагмент</li>
|
||||
<li>Текст в узле дерева соответствует элементу языка</li>
|
||||
</ul>
|
||||
|
||||
<h2>Виды узлов</h2>
|
||||
<li>
|
||||
<span className='bg-(--acc-bg-green)'>объявление идентификатора</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className='bg-(--acc-bg-teal)'>глобальный идентификатор</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className='bg-(--acc-bg-orange)'>логическое выражение</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className='bg-(--acc-bg-blue)'>типизированное выражение</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className='bg-(--acc-bg-red)'>присвоение и итерация</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className='bg-secondary'>составные выражения</span>
|
||||
</li>
|
||||
<ul>
|
||||
<li>
|
||||
<span className='bg-(--acc-bg-green)'>объявление идентификатора</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className='bg-(--acc-bg-teal)'>глобальный идентификатор</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className='bg-(--acc-bg-orange)'>логическое выражение</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className='bg-(--acc-bg-blue)'>типизированное выражение</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className='bg-(--acc-bg-red)'>присвоение и итерация</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className='bg-secondary'>составные выражения</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h2>Команды</h2>
|
||||
<ul>
|
||||
<li>
|
||||
<kbd>Space</kbd> – перемещение экрана
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -31,70 +31,74 @@ export function HelpLibrary() {
|
|||
<LinkTopic text='операционные схемы синтеза' topic={HelpTopic.CC_OSS} /> (ОСС).
|
||||
</p>
|
||||
|
||||
<li>
|
||||
<span className='text-(--acc-fg-green)'>зеленым текстом</span> выделены ОСС
|
||||
</li>
|
||||
<li>
|
||||
<kbd>клик</kbd> по строке - переход к редактированию схемы
|
||||
</li>
|
||||
<li>
|
||||
<kbd>Ctrl + клик</kbd> по строке откроет схему в новой вкладке
|
||||
</li>
|
||||
<li>Фильтры атрибутов три позиции: да/нет/не применять</li>
|
||||
<li>
|
||||
<IconShow size='1rem' className='inline-icon' /> фильтры атрибутов применяются по клику
|
||||
</li>
|
||||
<li>
|
||||
<IconSortAsc size='1rem' className='inline-icon' />
|
||||
<IconSortDesc size='1rem' className='inline-icon' /> сортировка по клику на заголовок таблицы
|
||||
</li>
|
||||
<li>
|
||||
<IconUserSearch size='1rem' className='inline-icon' /> фильтр по пользователю
|
||||
</li>
|
||||
<li>
|
||||
<IconSearch size='1rem' className='inline-icon' /> фильтр по названию и шифру
|
||||
</li>
|
||||
<li>
|
||||
<IconFolderSearch size='1rem' className='inline-icon' /> фильтр по расположению
|
||||
</li>
|
||||
<li>
|
||||
<IconFilterReset size='1rem' className='inline-icon' /> сбросить фильтры
|
||||
</li>
|
||||
<li>
|
||||
<IconFolderTree size='1rem' className='inline-icon' /> переключение между Проводник и Поиск
|
||||
</li>
|
||||
<ul>
|
||||
<li>
|
||||
<span className='text-(--acc-fg-green)'>зеленым текстом</span> выделены ОСС
|
||||
</li>
|
||||
<li>
|
||||
<kbd>клик</kbd> по строке - переход к редактированию схемы
|
||||
</li>
|
||||
<li>
|
||||
<kbd>Ctrl + клик</kbd> по строке откроет схему в новой вкладке
|
||||
</li>
|
||||
<li>Фильтры атрибутов три позиции: да/нет/не применять</li>
|
||||
<li>
|
||||
<IconShow size='1rem' className='inline-icon' /> фильтры атрибутов применяются по клику
|
||||
</li>
|
||||
<li>
|
||||
<IconSortAsc size='1rem' className='inline-icon' />
|
||||
<IconSortDesc size='1rem' className='inline-icon' /> сортировка по клику на заголовок таблицы
|
||||
</li>
|
||||
<li>
|
||||
<IconUserSearch size='1rem' className='inline-icon' /> фильтр по пользователю
|
||||
</li>
|
||||
<li>
|
||||
<IconSearch size='1rem' className='inline-icon' /> фильтр по названию и шифру
|
||||
</li>
|
||||
<li>
|
||||
<IconFolderSearch size='1rem' className='inline-icon' /> фильтр по расположению
|
||||
</li>
|
||||
<li>
|
||||
<IconFilterReset size='1rem' className='inline-icon' /> сбросить фильтры
|
||||
</li>
|
||||
<li>
|
||||
<IconFolderTree size='1rem' className='inline-icon' /> переключение между Проводник и Поиск
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h2>Режим: Проводник</h2>
|
||||
<li>
|
||||
<IconFolderEdit size='1rem' className='inline-icon' /> переименовать выбранную
|
||||
</li>
|
||||
<li>
|
||||
<IconSubfolders size='1rem' className='inline-icon icon-green' /> схемы во вложенных папках
|
||||
</li>
|
||||
<li>
|
||||
<kbd>клик</kbd> по папке отображает справа схемы в ней
|
||||
</li>
|
||||
<li>
|
||||
<kbd>Ctrl + клик по папке копирует путь в буфер обмена</kbd>
|
||||
</li>
|
||||
<li>
|
||||
<kbd>клик</kbd> по иконке сворачивает/разворачивает вложенные
|
||||
</li>
|
||||
<li>
|
||||
<IconFolderEmpty size='1rem' className='inline-icon text-foreground' /> папка без схем
|
||||
</li>
|
||||
<li>
|
||||
<IconFolderEmpty size='1rem' className='inline-icon' /> папка с вложенными без схем
|
||||
</li>
|
||||
<li>
|
||||
<IconFolder size='1rem' className='inline-icon' /> папка без вложенных
|
||||
</li>
|
||||
<li>
|
||||
<IconFolderClosed size='1rem' className='inline-icon' /> папка с вложенными и схемами
|
||||
</li>
|
||||
<li>
|
||||
<IconFolderOpened size='1rem' className='inline-icon icon-green' /> развернутая папка
|
||||
</li>
|
||||
<ul>
|
||||
<li>
|
||||
<IconFolderEdit size='1rem' className='inline-icon' /> переименовать выбранную
|
||||
</li>
|
||||
<li>
|
||||
<IconSubfolders size='1rem' className='inline-icon icon-green' /> схемы во вложенных папках
|
||||
</li>
|
||||
<li>
|
||||
<kbd>клик</kbd> по папке отображает справа схемы в ней
|
||||
</li>
|
||||
<li>
|
||||
<kbd>Ctrl + клик по папке копирует путь в буфер обмена</kbd>
|
||||
</li>
|
||||
<li>
|
||||
<kbd>клик</kbd> по иконке сворачивает/разворачивает вложенные
|
||||
</li>
|
||||
<li>
|
||||
<IconFolderEmpty size='1rem' className='inline-icon text-foreground' /> папка без схем
|
||||
</li>
|
||||
<li>
|
||||
<IconFolderEmpty size='1rem' className='inline-icon' /> папка с вложенными без схем
|
||||
</li>
|
||||
<li>
|
||||
<IconFolder size='1rem' className='inline-icon' /> папка без вложенных
|
||||
</li>
|
||||
<li>
|
||||
<IconFolderClosed size='1rem' className='inline-icon' /> папка с вложенными и схемами
|
||||
</li>
|
||||
<li>
|
||||
<IconFolderOpened size='1rem' className='inline-icon icon-green' /> развернутая папка
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -32,109 +32,117 @@ export function HelpOssGraph() {
|
|||
<h1 className='sm:pr-24'>Граф синтеза</h1>
|
||||
<div className='flex flex-col sm:flex-row'>
|
||||
<div className='sm:w-56'>
|
||||
<h1>Настройка графа</h1>
|
||||
<li>
|
||||
<IconReset className='inline-icon' /> Сбросить изменения
|
||||
</li>
|
||||
<li>
|
||||
<IconFitImage className='inline-icon' /> Вписать в экран
|
||||
</li>
|
||||
<li>
|
||||
<IconFixLayout className='inline-icon' /> Исправить расположения
|
||||
</li>
|
||||
<li>
|
||||
<IconSettings className='inline-icon' /> Диалог настроек
|
||||
</li>
|
||||
<h2>Настройка графа</h2>
|
||||
<ul>
|
||||
<li>
|
||||
<IconReset className='inline-icon' /> Сбросить изменения
|
||||
</li>
|
||||
<li>
|
||||
<IconFitImage className='inline-icon' /> Вписать в экран
|
||||
</li>
|
||||
<li>
|
||||
<IconFixLayout className='inline-icon' /> Исправить расположения
|
||||
</li>
|
||||
<li>
|
||||
<IconSettings className='inline-icon' /> Диалог настроек
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<IconGrid className='inline-icon' /> Отображение сетки
|
||||
</li>
|
||||
<li>
|
||||
<IconLineWave className='inline-icon' />
|
||||
<IconLineStraight className='inline-icon' /> Тип линии
|
||||
</li>
|
||||
<li>
|
||||
<IconAnimation className='inline-icon' />
|
||||
<IconAnimationOff className='inline-icon' /> Анимация
|
||||
</li>
|
||||
<li>
|
||||
<IconCoordinates className='inline-icon' /> Отображение координат
|
||||
</li>
|
||||
<li>черта сверху - Загрузка</li>
|
||||
<li>
|
||||
черта слева - КС <LinkTopic text='внешняя' topic={HelpTopic.CC_OSS} />
|
||||
</li>
|
||||
<li>
|
||||
<IconGrid className='inline-icon' /> Отображение сетки
|
||||
</li>
|
||||
<li>
|
||||
<IconLineWave className='inline-icon' />
|
||||
<IconLineStraight className='inline-icon' /> Тип линии
|
||||
</li>
|
||||
<li>
|
||||
<IconAnimation className='inline-icon' />
|
||||
<IconAnimationOff className='inline-icon' /> Анимация
|
||||
</li>
|
||||
<li>
|
||||
<IconCoordinates className='inline-icon' /> Отображение координат
|
||||
</li>
|
||||
<li>черта сверху - Загрузка</li>
|
||||
<li>
|
||||
черта слева - КС <LinkTopic text='внешняя' topic={HelpTopic.CC_OSS} />
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<Divider vertical margins='mx-3 mt-3' className='hidden sm:block' />
|
||||
|
||||
<div className='sm:w-84'>
|
||||
<h1>Изменение узлов</h1>
|
||||
<li>
|
||||
<kbd>Клик</kbd> на операцию – выделение
|
||||
</li>
|
||||
<li>
|
||||
<kbd>Esc</kbd> – сбросить выделение
|
||||
</li>
|
||||
<li>
|
||||
<kbd>Двойной клик</kbd> – переход к связанной <LinkTopic text='КС' topic={HelpTopic.CC_SYSTEM} />
|
||||
</li>
|
||||
<li>
|
||||
<IconConceptBlock className='inline-icon icon-green' /> Новый блок
|
||||
</li>
|
||||
<li>
|
||||
<IconNewItem className='inline-icon icon-green' /> Новая операция
|
||||
</li>
|
||||
<li>
|
||||
<IconEdit2 className='inline-icon' /> Редактирование узла
|
||||
</li>
|
||||
<li>
|
||||
<IconDestroy className='inline-icon icon-red' /> <kbd>Delete</kbd> – удалить выбранные
|
||||
</li>
|
||||
<h2>Изменение узлов</h2>
|
||||
<ul>
|
||||
<li>
|
||||
<kbd>Клик</kbd> на операцию – выделение
|
||||
</li>
|
||||
<li>
|
||||
<kbd>Esc</kbd> – сбросить выделение
|
||||
</li>
|
||||
<li>
|
||||
<kbd>Двойной клик</kbd> – переход к связанной <LinkTopic text='КС' topic={HelpTopic.CC_SYSTEM} />
|
||||
</li>
|
||||
<li>
|
||||
<IconConceptBlock className='inline-icon icon-green' /> Новый блок
|
||||
</li>
|
||||
<li>
|
||||
<IconNewItem className='inline-icon icon-green' /> Новая операция
|
||||
</li>
|
||||
<li>
|
||||
<IconEdit2 className='inline-icon' /> Редактирование узла
|
||||
</li>
|
||||
<li>
|
||||
<IconDestroy className='inline-icon icon-red' /> <kbd>Delete</kbd> – удалить выбранные
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Divider margins='my-3' className='hidden sm:block' />
|
||||
<Divider margins='my-2' className='hidden sm:block' />
|
||||
|
||||
<div className='flex flex-col-reverse mb-3 sm:flex-row'>
|
||||
<div className='sm:w-56'>
|
||||
<h1>Общие</h1>
|
||||
<li>
|
||||
<IconSave className='inline-icon' /> Сохранить положения
|
||||
</li>
|
||||
<li>
|
||||
<kbd>Space</kbd> – перемещение экрана
|
||||
</li>
|
||||
<li>
|
||||
<kbd>Shift</kbd> – перемещение выделенных элементов в границах родителя
|
||||
</li>
|
||||
<h2>Общие</h2>
|
||||
<ul>
|
||||
<li>
|
||||
<IconSave className='inline-icon' /> Сохранить положения
|
||||
</li>
|
||||
<li>
|
||||
<kbd>Space</kbd> – перемещение экрана
|
||||
</li>
|
||||
<li>
|
||||
<kbd>Shift</kbd> – перемещение выделенных элементов в границах родителя
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<Divider vertical margins='mx-3' className='hidden sm:block' />
|
||||
|
||||
<div className='dense w-84'>
|
||||
<h1>Контекстное меню</h1>
|
||||
<li>
|
||||
<IconRSForm className='inline-icon icon-green' /> Статус связанной{' '}
|
||||
<LinkTopic text='КС' topic={HelpTopic.CC_SYSTEM} />
|
||||
</li>
|
||||
<li>
|
||||
<IconConsolidation className='inline-icon' />{' '}
|
||||
<LinkTopic text='Ромбовидный синтез' topic={HelpTopic.CC_OSS} />
|
||||
</li>
|
||||
<li>
|
||||
<IconNewRSForm className='inline-icon icon-green' /> Создать пустую КС для загрузки
|
||||
</li>
|
||||
<li>
|
||||
<IconConnect className='inline-icon' /> Выбрать КС для загрузки
|
||||
</li>
|
||||
<li>
|
||||
<IconChild className='inline-icon icon-green' />{' '}
|
||||
<LinkTopic text='Перенести конституенты' topic={HelpTopic.UI_RELOCATE_CST} />
|
||||
</li>
|
||||
<li>
|
||||
<IconExecute className='inline-icon icon-green' /> Активировать операцию
|
||||
</li>
|
||||
<h2>Контекстное меню</h2>
|
||||
<ul>
|
||||
<li>
|
||||
<IconRSForm className='inline-icon icon-green' /> Статус связанной{' '}
|
||||
<LinkTopic text='КС' topic={HelpTopic.CC_SYSTEM} />
|
||||
</li>
|
||||
<li>
|
||||
<IconConsolidation className='inline-icon' />{' '}
|
||||
<LinkTopic text='Ромбовидный синтез' topic={HelpTopic.CC_OSS} />
|
||||
</li>
|
||||
<li>
|
||||
<IconNewRSForm className='inline-icon icon-green' /> Создать пустую КС для загрузки
|
||||
</li>
|
||||
<li>
|
||||
<IconConnect className='inline-icon' /> Выбрать КС для загрузки
|
||||
</li>
|
||||
<li>
|
||||
<IconChild className='inline-icon icon-green' />{' '}
|
||||
<LinkTopic text='Перенести конституенты' topic={HelpTopic.UI_RELOCATE_CST} />
|
||||
</li>
|
||||
<li>
|
||||
<IconExecute className='inline-icon icon-green' /> Активировать операцию
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -12,23 +12,29 @@ export function HelpRelocateCst() {
|
|||
другую КС (целевую) в рамках одной <IconOSS size='1rem' className='inline-icon' />{' '}
|
||||
<LinkTopic text='операционной схемы синтеза' topic={HelpTopic.CC_OSS} />.
|
||||
</p>
|
||||
<li>
|
||||
только для <IconPredecessor size='1rem' className='inline-icon' /> собственных конституент источника
|
||||
</li>
|
||||
<li>
|
||||
<IconMoveUp size='1rem' className='inline-icon' />
|
||||
<IconMoveDown size='1rem' className='inline-icon' /> направление переноса - вверх или вниз по дереву синтеза
|
||||
</li>
|
||||
<ul>
|
||||
<li>
|
||||
только для <IconPredecessor size='1rem' className='inline-icon' /> собственных конституент источника
|
||||
</li>
|
||||
<li>
|
||||
<IconMoveUp size='1rem' className='inline-icon' />
|
||||
<IconMoveDown size='1rem' className='inline-icon' /> направление переноса - вверх или вниз по дереву синтеза
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h2>Перенос вверх</h2>
|
||||
<li>выбранные конституенты становятся наследованными, а их копии добавляются в целевую КС</li>
|
||||
<li>нельзя выбирать конституенты, зависящие от конституент других концептуальных схем</li>
|
||||
<ul>
|
||||
<li>выбранные конституенты становятся наследованными, а их копии добавляются в целевую КС</li>
|
||||
<li>нельзя выбирать конституенты, зависящие от конституент других концептуальных схем</li>
|
||||
</ul>
|
||||
|
||||
<h2>Перенос вниз</h2>
|
||||
<li>
|
||||
выбранные конституенты становятся собственными конституентами целевой КС, удаляются из исходной КС и ее
|
||||
наследников
|
||||
</li>
|
||||
<ul>
|
||||
<li>
|
||||
выбранные конституенты становятся собственными конституентами целевой КС, удаляются из исходной КС и ее
|
||||
наследников
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -30,33 +30,35 @@ export function HelpRSCard() {
|
|||
</p>
|
||||
|
||||
<h2>Управление</h2>
|
||||
<li>
|
||||
<IconOSS className='inline-icon' /> переход к связанной <LinkTopic text='ОСС' topic={HelpTopic.CC_OSS} />
|
||||
</li>
|
||||
<li>
|
||||
<IconSave className='inline-icon' /> сохранить изменения: <kbd>Ctrl + S</kbd>
|
||||
</li>
|
||||
<li>
|
||||
<IconEditor className='inline-icon' /> Редактор обладает правом редактирования
|
||||
</li>
|
||||
<li>
|
||||
<IconOwner className='inline-icon' /> Владелец обладает полным доступом к схеме
|
||||
</li>
|
||||
<li>
|
||||
<IconPublic className='inline-icon' /> Общедоступные схемы видны всем посетителям
|
||||
</li>
|
||||
<li>
|
||||
<IconImmutable className='inline-icon' /> Неизменные схемы
|
||||
</li>
|
||||
<li>
|
||||
<IconClone className='inline-icon icon-green' /> Клонировать – создать копию схемы
|
||||
</li>
|
||||
<li>
|
||||
<IconDownload className='inline-icon' /> Загрузить/Выгрузить – взаимодействие с Экстеор
|
||||
</li>
|
||||
<li>
|
||||
<IconDestroy className='inline-icon icon-red' /> Удалить – полностью удаляет схему из базы Портала
|
||||
</li>
|
||||
<ul>
|
||||
<li>
|
||||
<IconOSS className='inline-icon' /> переход к связанной <LinkTopic text='ОСС' topic={HelpTopic.CC_OSS} />
|
||||
</li>
|
||||
<li>
|
||||
<IconSave className='inline-icon' /> сохранить изменения: <kbd>Ctrl + S</kbd>
|
||||
</li>
|
||||
<li>
|
||||
<IconEditor className='inline-icon' /> Редактор обладает правом редактирования
|
||||
</li>
|
||||
<li>
|
||||
<IconOwner className='inline-icon' /> Владелец обладает полным доступом к схеме
|
||||
</li>
|
||||
<li>
|
||||
<IconPublic className='inline-icon' /> Общедоступные схемы видны всем посетителям
|
||||
</li>
|
||||
<li>
|
||||
<IconImmutable className='inline-icon' /> Неизменные схемы
|
||||
</li>
|
||||
<li>
|
||||
<IconClone className='inline-icon icon-green' /> Клонировать – создать копию схемы
|
||||
</li>
|
||||
<li>
|
||||
<IconDownload className='inline-icon' /> Загрузить/Выгрузить – взаимодействие с Экстеор
|
||||
</li>
|
||||
<li>
|
||||
<IconDestroy className='inline-icon icon-red' /> Удалить – полностью удаляет схему из базы Портала
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -27,89 +27,99 @@ export function HelpRSEditor() {
|
|||
<div className='dense'>
|
||||
<h1>Редактор конституенты</h1>
|
||||
<div className='flex flex-col sm:flex-row sm:gap-3'>
|
||||
<div className='flex flex-col'>
|
||||
<li>
|
||||
<IconOSS className='inline-icon' /> переход к <LinkTopic text='ОСС' topic={HelpTopic.CC_OSS} />
|
||||
</li>
|
||||
<li>
|
||||
<IconPredecessor className='inline-icon' /> переход к исходной
|
||||
</li>
|
||||
<li>
|
||||
<IconList className='inline-icon' /> список конституент
|
||||
</li>
|
||||
<li>
|
||||
<IconSave className='inline-icon' /> сохранить: <kbd>Ctrl + S</kbd>
|
||||
</li>
|
||||
<li>
|
||||
<IconReset className='inline-icon' /> сбросить изменения
|
||||
</li>
|
||||
<li>
|
||||
<IconClone className='inline-icon icon-green' /> клонировать: <kbd>Alt + V</kbd>
|
||||
</li>
|
||||
<li>
|
||||
<IconNewItem className='inline-icon icon-green' /> новая конституента
|
||||
</li>
|
||||
<li>
|
||||
<IconDestroy className='inline-icon icon-red' /> удалить
|
||||
</li>
|
||||
<div>
|
||||
<h2>Команды</h2>
|
||||
<ul>
|
||||
<li>
|
||||
<IconOSS className='inline-icon' /> переход к <LinkTopic text='ОСС' topic={HelpTopic.CC_OSS} />
|
||||
</li>
|
||||
<li>
|
||||
<IconPredecessor className='inline-icon' /> переход к исходной
|
||||
</li>
|
||||
<li>
|
||||
<IconList className='inline-icon' /> список конституент
|
||||
</li>
|
||||
<li>
|
||||
<IconSave className='inline-icon' /> сохранить: <kbd>Ctrl + S</kbd>
|
||||
</li>
|
||||
<li>
|
||||
<IconReset className='inline-icon' /> сбросить изменения
|
||||
</li>
|
||||
<li>
|
||||
<IconClone className='inline-icon icon-green' /> клонировать: <kbd>Alt + V</kbd>
|
||||
</li>
|
||||
<li>
|
||||
<IconNewItem className='inline-icon icon-green' /> новая конституента
|
||||
</li>
|
||||
<li>
|
||||
<IconDestroy className='inline-icon icon-red' /> удалить
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className='flex flex-col'>
|
||||
<div>
|
||||
<h2>Список конституент</h2>
|
||||
<li>
|
||||
<IconMoveDown className='inline-icon' />
|
||||
<IconMoveUp className='inline-icon' /> <kbd>Alt + вверх/вниз</kbd>
|
||||
</li>
|
||||
<li>
|
||||
<IconFilter className='inline-icon' />
|
||||
<IconSettings className='inline-icon' /> фильтрация по графу термов
|
||||
</li>
|
||||
<li>
|
||||
<IconChild className='inline-icon' /> отображение наследованных
|
||||
</li>
|
||||
<li>
|
||||
<span className='bg-selected'>текущая конституента</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className='bg-accent-green50'>
|
||||
<LinkTopic text='основа' topic={HelpTopic.CC_RELATIONS} /> текущей
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className='bg-accent-orange50'>
|
||||
<LinkTopic text='порожденные' topic={HelpTopic.CC_RELATIONS} /> текущей
|
||||
</span>
|
||||
</li>
|
||||
<ul>
|
||||
<li>
|
||||
<IconMoveDown className='inline-icon' />
|
||||
<IconMoveUp className='inline-icon' /> <kbd>Alt + вверх/вниз</kbd>
|
||||
</li>
|
||||
<li>
|
||||
<IconFilter className='inline-icon' />
|
||||
<IconSettings className='inline-icon' /> фильтрация по графу термов
|
||||
</li>
|
||||
<li>
|
||||
<IconChild className='inline-icon' /> отображение наследованных
|
||||
</li>
|
||||
<li>
|
||||
<span className='bg-selected'>текущая конституента</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className='bg-accent-green50'>
|
||||
<LinkTopic text='основа' topic={HelpTopic.CC_RELATIONS} /> текущей
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className='bg-accent-orange50'>
|
||||
<LinkTopic text='порожденные' topic={HelpTopic.CC_RELATIONS} /> текущей
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>Формальное определение</h2>
|
||||
<li>
|
||||
<IconStatusOK className='inline-icon' /> индикатор статуса определения сверху
|
||||
</li>
|
||||
<li>
|
||||
<IconControls className='inline-icon' /> специальная клавиатура и горячие клавиши
|
||||
</li>
|
||||
<li>
|
||||
<IconTypeGraph className='inline-icon' /> отображение{' '}
|
||||
<LinkTopic text='графа ступеней типизации' topic={HelpTopic.UI_TYPE_GRAPH} />
|
||||
</li>
|
||||
<li>
|
||||
<IconTree className='inline-icon' /> отображение{' '}
|
||||
<LinkTopic text='дерева разбора' topic={HelpTopic.UI_FORMULA_TREE} />
|
||||
</li>
|
||||
<li>
|
||||
<kbd>Ctrl + Пробел</kbd> вставка незанятого имени / замена проекции
|
||||
</li>
|
||||
<ul>
|
||||
<li>
|
||||
<IconStatusOK className='inline-icon' /> индикатор статуса определения сверху
|
||||
</li>
|
||||
<li>
|
||||
<IconControls className='inline-icon' /> специальная клавиатура и горячие клавиши
|
||||
</li>
|
||||
<li>
|
||||
<IconTypeGraph className='inline-icon' /> отображение{' '}
|
||||
<LinkTopic text='графа ступеней типизации' topic={HelpTopic.UI_TYPE_GRAPH} />
|
||||
</li>
|
||||
<li>
|
||||
<IconTree className='inline-icon' /> отображение{' '}
|
||||
<LinkTopic text='дерева разбора' topic={HelpTopic.UI_FORMULA_TREE} />
|
||||
</li>
|
||||
<li>
|
||||
<kbd>Ctrl + Пробел</kbd> вставка незанятого имени / замена проекции
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h2>Термин и Текстовое определение</h2>
|
||||
<li>
|
||||
<IconEdit className='inline-icon' /> редактирование <LinkTopic text='Имени' topic={HelpTopic.CC_CONSTITUENTA} />{' '}
|
||||
/ <LinkTopic text='Термина' topic={HelpTopic.CC_CONSTITUENTA} />
|
||||
</li>
|
||||
<li>
|
||||
<kbd>Ctrl + Пробел</kbd> открывает редактирование отсылок
|
||||
</li>
|
||||
<ul>
|
||||
<li>
|
||||
<IconEdit className='inline-icon' /> редактирование{' '}
|
||||
<LinkTopic text='Имени' topic={HelpTopic.CC_CONSTITUENTA} /> /{' '}
|
||||
<LinkTopic text='Термина' topic={HelpTopic.CC_CONSTITUENTA} />
|
||||
</li>
|
||||
<li>
|
||||
<kbd>Ctrl + Пробел</kbd> открывает редактирование отсылок
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -30,37 +30,41 @@ export function HelpRSGraphTerm() {
|
|||
<h1>Граф термов</h1>
|
||||
<div className='flex flex-col sm:flex-row'>
|
||||
<div className='sm:w-56'>
|
||||
<h1>Настройка графа</h1>
|
||||
<li>Цвет – покраска узлов</li>
|
||||
<li>
|
||||
<IconText className='inline-icon' /> Отображение текста
|
||||
</li>
|
||||
<li>
|
||||
<IconClustering className='inline-icon' /> Скрыть порожденные
|
||||
</li>
|
||||
<li>
|
||||
<IconRotate3D className='inline-icon' /> Вращение 3D
|
||||
</li>
|
||||
<h2>Настройка графа</h2>
|
||||
<ul>
|
||||
<li>Цвет – покраска узлов</li>
|
||||
<li>
|
||||
<IconText className='inline-icon' /> Отображение текста
|
||||
</li>
|
||||
<li>
|
||||
<IconClustering className='inline-icon' /> Скрыть порожденные
|
||||
</li>
|
||||
<li>
|
||||
<IconRotate3D className='inline-icon' /> Вращение 3D
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<Divider vertical margins='mx-3 mt-3' className='hidden sm:block' />
|
||||
|
||||
<div className='sm:w-84'>
|
||||
<h1>Изменение узлов</h1>
|
||||
<li>Клик на узел – выделение</li>
|
||||
<li>Левый клик – выбор фокус-конституенты</li>
|
||||
<li>
|
||||
<IconReset className='inline-icon' /> Esc – сбросить выделение
|
||||
</li>
|
||||
<li>
|
||||
<IconEdit className='inline-icon' /> Двойной клик – редактирование
|
||||
</li>
|
||||
<li>
|
||||
<IconDestroy className='inline-icon icon-red' /> Delete – удалить выбранные
|
||||
</li>
|
||||
<li>
|
||||
<IconNewItem className='inline-icon icon-green' /> Новая со ссылками на выделенные
|
||||
</li>
|
||||
<h2>Изменение узлов</h2>
|
||||
<ul>
|
||||
<li>Клик на узел – выделение</li>
|
||||
<li>Левый клик – выбор фокус-конституенты</li>
|
||||
<li>
|
||||
<IconReset className='inline-icon' /> Esc – сбросить выделение
|
||||
</li>
|
||||
<li>
|
||||
<IconEdit className='inline-icon' /> Двойной клик – редактирование
|
||||
</li>
|
||||
<li>
|
||||
<IconDestroy className='inline-icon icon-red' /> Delete – удалить выбранные
|
||||
</li>
|
||||
<li>
|
||||
<IconNewItem className='inline-icon icon-green' /> Новая со ссылками на выделенные
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -68,51 +72,58 @@ export function HelpRSGraphTerm() {
|
|||
|
||||
<div className='flex flex-col-reverse mb-3 sm:flex-row'>
|
||||
<div className='sm:w-56'>
|
||||
<h1>Общие</h1>
|
||||
<li>
|
||||
<IconOSS className='inline-icon' /> переход к связанной <LinkTopic text='ОСС' topic={HelpTopic.CC_OSS} />
|
||||
</li>
|
||||
<li>
|
||||
<IconFilter className='inline-icon' /> Открыть настройки
|
||||
</li>
|
||||
<li>
|
||||
<IconFocus className='inline-icon' /> Задать фокус
|
||||
</li>
|
||||
<li>
|
||||
<IconFitImage className='inline-icon' /> Вписать в экран
|
||||
</li>
|
||||
<li>
|
||||
<IconTypeGraph className='inline-icon' /> Открыть{' '}
|
||||
<LinkTopic text='граф ступеней' topic={HelpTopic.UI_TYPE_GRAPH} />
|
||||
</li>
|
||||
<h2>Общие</h2>
|
||||
<ul>
|
||||
<li>
|
||||
<kbd>Space</kbd> – перемещение экрана
|
||||
</li>
|
||||
<li>
|
||||
<IconOSS className='inline-icon' /> переход к связанной <LinkTopic text='ОСС' topic={HelpTopic.CC_OSS} />
|
||||
</li>
|
||||
<li>
|
||||
<IconFilter className='inline-icon' /> Открыть настройки
|
||||
</li>
|
||||
<li>
|
||||
<IconFocus className='inline-icon' /> Задать фокус
|
||||
</li>
|
||||
<li>
|
||||
<IconFitImage className='inline-icon' /> Вписать в экран
|
||||
</li>
|
||||
<li>
|
||||
<IconTypeGraph className='inline-icon' /> Открыть{' '}
|
||||
<LinkTopic text='граф ступеней' topic={HelpTopic.UI_TYPE_GRAPH} />
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<Divider vertical margins='mx-3' className='hidden sm:block' />
|
||||
|
||||
<div className='dense w-84'>
|
||||
<h1>Выделение</h1>
|
||||
<li>
|
||||
<IconGraphCollapse className='inline-icon' /> все влияющие
|
||||
</li>
|
||||
<li>
|
||||
<IconGraphExpand className='inline-icon' /> все зависимые
|
||||
</li>
|
||||
<li>
|
||||
<IconGraphMaximize className='inline-icon' /> зависимые только от выделенных
|
||||
</li>
|
||||
<li>
|
||||
<IconGraphInputs className='inline-icon' /> входящие напрямую
|
||||
</li>
|
||||
<li>
|
||||
<IconGraphOutputs className='inline-icon' /> исходящие напрямую
|
||||
</li>
|
||||
<li>
|
||||
<IconGraphCore className='inline-icon' /> выделить <LinkTopic text='Ядро' topic={HelpTopic.CC_SYSTEM} />
|
||||
</li>
|
||||
<li>
|
||||
<IconPredecessor className='inline-icon' /> выделить{' '}
|
||||
<LinkTopic text='собственные' topic={HelpTopic.CC_PROPAGATION} />
|
||||
</li>
|
||||
<h2>Выделение</h2>
|
||||
<ul>
|
||||
<li>
|
||||
<IconGraphCollapse className='inline-icon' /> все влияющие
|
||||
</li>
|
||||
<li>
|
||||
<IconGraphExpand className='inline-icon' /> все зависимые
|
||||
</li>
|
||||
<li>
|
||||
<IconGraphMaximize className='inline-icon' /> зависимые только от выделенных
|
||||
</li>
|
||||
<li>
|
||||
<IconGraphInputs className='inline-icon' /> входящие напрямую
|
||||
</li>
|
||||
<li>
|
||||
<IconGraphOutputs className='inline-icon' /> исходящие напрямую
|
||||
</li>
|
||||
<li>
|
||||
<IconGraphCore className='inline-icon' /> выделить <LinkTopic text='Ядро' topic={HelpTopic.CC_SYSTEM} />
|
||||
</li>
|
||||
<li>
|
||||
<IconPredecessor className='inline-icon' /> выделить{' '}
|
||||
<LinkTopic text='собственные' topic={HelpTopic.CC_PROPAGATION} />
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -19,49 +19,53 @@ export function HelpRSList() {
|
|||
return (
|
||||
<div className='dense'>
|
||||
<h1>Список конституент</h1>
|
||||
<li>
|
||||
<IconAlias className='inline-icon' />
|
||||
Конституенты обладают уникальным <LinkTopic text='Именем' topic={HelpTopic.CC_CONSTITUENTA} />
|
||||
</li>
|
||||
<li>при наведении на имя отображаются атрибуты</li>
|
||||
<li>
|
||||
пунктиром отображаются <LinkTopic text='наследованные' topic={HelpTopic.CC_OSS} /> конституенты
|
||||
</li>
|
||||
<ul>
|
||||
<li>
|
||||
<IconAlias className='inline-icon' />
|
||||
Конституенты обладают уникальным <LinkTopic text='Именем' topic={HelpTopic.CC_CONSTITUENTA} />
|
||||
</li>
|
||||
<li>при наведении на имя отображаются атрибуты</li>
|
||||
<li>
|
||||
пунктиром отображаются <LinkTopic text='наследованные' topic={HelpTopic.CC_OSS} /> конституенты
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h2>Управление списком</h2>
|
||||
<li>
|
||||
<IconOSS className='inline-icon' /> переход к связанной <LinkTopic text='ОСС' topic={HelpTopic.CC_OSS} />
|
||||
</li>
|
||||
<li>
|
||||
<IconReset className='inline-icon' /> сбросить выделение: <kbd>ESC</kbd>
|
||||
</li>
|
||||
<li>Клик на строку – выделение</li>
|
||||
<li>
|
||||
<kbd>Shift + клик</kbd> – выделение нескольких
|
||||
</li>
|
||||
<li>
|
||||
<kbd>Alt + клик</kbd> – Редактор
|
||||
</li>
|
||||
<li>
|
||||
<kbd>Двойной клик</kbd> – Редактор
|
||||
</li>
|
||||
<li>
|
||||
<IconMoveUp className='inline-icon' />
|
||||
<IconMoveDown className='inline-icon' /> <kbd>Alt + вверх/вниз</kbd> – перемещение
|
||||
</li>
|
||||
<ul>
|
||||
<li>
|
||||
<IconOSS className='inline-icon' /> переход к связанной <LinkTopic text='ОСС' topic={HelpTopic.CC_OSS} />
|
||||
</li>
|
||||
<li>
|
||||
<IconReset className='inline-icon' /> сбросить выделение: <kbd>ESC</kbd>
|
||||
</li>
|
||||
<li>Клик на строку – выделение</li>
|
||||
<li>
|
||||
<kbd>Shift + клик</kbd> – выделение нескольких
|
||||
</li>
|
||||
<li>
|
||||
<kbd>Alt + клик</kbd> – Редактор
|
||||
</li>
|
||||
<li>
|
||||
<kbd>Двойной клик</kbd> – Редактор
|
||||
</li>
|
||||
<li>
|
||||
<IconMoveUp className='inline-icon' />
|
||||
<IconMoveDown className='inline-icon' /> <kbd>Alt + вверх/вниз</kbd> – перемещение
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<IconClone className='inline-icon icon-green' /> клонировать выделенную: <kbd>Alt + V</kbd>
|
||||
</li>
|
||||
<li>
|
||||
<IconNewItem className='inline-icon icon-green' /> новая конституента: <kbd>Alt + `</kbd>
|
||||
</li>
|
||||
<li>
|
||||
<IconOpenList className='inline-icon icon-green' /> быстрое добавление: <kbd>Alt + 1-6,Q,W</kbd>
|
||||
</li>
|
||||
<li>
|
||||
<IconDestroy className='inline-icon icon-red' /> удаление выделенных: <kbd>Delete</kbd>
|
||||
</li>
|
||||
<li>
|
||||
<IconClone className='inline-icon icon-green' /> клонировать выделенную: <kbd>Alt + V</kbd>
|
||||
</li>
|
||||
<li>
|
||||
<IconNewItem className='inline-icon icon-green' /> новая конституента: <kbd>Alt + `</kbd>
|
||||
</li>
|
||||
<li>
|
||||
<IconOpenList className='inline-icon icon-green' /> быстрое добавление: <kbd>Alt + 1-6,Q,W</kbd>
|
||||
</li>
|
||||
<li>
|
||||
<IconDestroy className='inline-icon icon-red' /> удаление выделенных: <kbd>Delete</kbd>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<Divider margins='my-2' />
|
||||
|
||||
|
|
|
@ -30,72 +30,80 @@ export function HelpRSMenu() {
|
|||
</p>
|
||||
|
||||
<h2>Вкладки</h2>
|
||||
<li>
|
||||
<LinkTopic text='Карточка' topic={HelpTopic.UI_RS_CARD} /> – редактирование атрибутов схемы и версии
|
||||
</li>
|
||||
<li>
|
||||
<LinkTopic text='Содержание' topic={HelpTopic.UI_RS_LIST} /> – работа со списком конституент в табличной форме
|
||||
</li>
|
||||
<li>
|
||||
<LinkTopic text='Редактор' topic={HelpTopic.UI_RS_EDITOR} /> – редактирование отдельной{' '}
|
||||
<LinkTopic text='Конституенты' topic={HelpTopic.CC_CONSTITUENTA} />
|
||||
</li>
|
||||
<li>
|
||||
<LinkTopic text='Граф термов' topic={HelpTopic.UI_GRAPH_TERM} /> – графическое представление связей конституент
|
||||
</li>
|
||||
<ul>
|
||||
<li>
|
||||
<LinkTopic text='Карточка' topic={HelpTopic.UI_RS_CARD} /> – редактирование атрибутов схемы и версии
|
||||
</li>
|
||||
<li>
|
||||
<LinkTopic text='Содержание' topic={HelpTopic.UI_RS_LIST} /> – работа со списком конституент в табличной форме
|
||||
</li>
|
||||
<li>
|
||||
<LinkTopic text='Редактор' topic={HelpTopic.UI_RS_EDITOR} /> – редактирование отдельной{' '}
|
||||
<LinkTopic text='Конституенты' topic={HelpTopic.CC_CONSTITUENTA} />
|
||||
</li>
|
||||
<li>
|
||||
<LinkTopic text='Граф термов' topic={HelpTopic.UI_GRAPH_TERM} /> – графическое представление связей
|
||||
конституент
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div className='flex my-3'>
|
||||
<div>
|
||||
<h2>Меню схемы</h2>
|
||||
<li>
|
||||
<IconMenu size='1.25rem' className='inline-icon' /> Меню схемы – выпадающее меню с общими функциями
|
||||
</li>
|
||||
<li>
|
||||
<IconShare className='inline-icon' /> Поделиться – скопировать ссылку на схему
|
||||
</li>
|
||||
<li>
|
||||
<IconQR className='inline-icon' /> Отобразить QR-код схемы
|
||||
</li>
|
||||
<li>
|
||||
<IconRobot className='inline-icon' /> Генерировать запрос для LLM
|
||||
</li>
|
||||
<li>
|
||||
<IconClone className='inline-icon icon-green' /> Клонировать – создать копию схемы
|
||||
</li>
|
||||
<li>
|
||||
<IconDownload className='inline-icon' /> Выгрузить – сохранить в файле формата Экстеор
|
||||
</li>
|
||||
<li>
|
||||
<IconUpload className='inline-icon icon-red' /> Загрузить – заменить схему на содержимое файла Экстеор
|
||||
</li>
|
||||
<li>
|
||||
<IconDestroy className='inline-icon icon-red' /> Удалить – полностью удаляет схему из базы Портала
|
||||
</li>
|
||||
<ul>
|
||||
<li>
|
||||
<IconMenu size='1.25rem' className='inline-icon' /> Меню схемы – выпадающее меню с общими функциями
|
||||
</li>
|
||||
<li>
|
||||
<IconShare className='inline-icon' /> Поделиться – скопировать ссылку на схему
|
||||
</li>
|
||||
<li>
|
||||
<IconQR className='inline-icon' /> Отобразить QR-код схемы
|
||||
</li>
|
||||
<li>
|
||||
<IconRobot className='inline-icon' /> Генерировать запрос для LLM
|
||||
</li>
|
||||
<li>
|
||||
<IconClone className='inline-icon icon-green' /> Клонировать – создать копию схемы
|
||||
</li>
|
||||
<li>
|
||||
<IconDownload className='inline-icon' /> Выгрузить – сохранить в файле формата Экстеор
|
||||
</li>
|
||||
<li>
|
||||
<IconUpload className='inline-icon icon-red' /> Загрузить – заменить схему на содержимое файла Экстеор
|
||||
</li>
|
||||
<li>
|
||||
<IconDestroy className='inline-icon icon-red' /> Удалить – полностью удаляет схему из базы Портала
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<Divider vertical margins='mx-3' />
|
||||
|
||||
<div className='w-72'>
|
||||
<h2>Режимы работы</h2>
|
||||
<li>
|
||||
<IconAlert size='1.25rem' className='inline-icon icon-red' /> работа в анонимном режиме. Переход на страницу
|
||||
логина
|
||||
</li>
|
||||
<li>
|
||||
<IconArchive size='1.25rem' className='inline-icon' /> просмотр архивной версии. Переход к актуальной версии
|
||||
</li>
|
||||
<li>
|
||||
<IconReader size='1.25rem' className='inline-icon' /> режим Читатель
|
||||
</li>
|
||||
<li>
|
||||
<IconEditor size='1.25rem' className='inline-icon' /> режим Редактор
|
||||
</li>
|
||||
<li>
|
||||
<IconOwner size='1.25rem' className='inline-icon' /> режим Владелец
|
||||
</li>
|
||||
<li>
|
||||
<IconAdmin size='1.25rem' className='inline-icon' /> режим Администратор
|
||||
</li>
|
||||
<ul>
|
||||
<li>
|
||||
<IconAlert size='1.25rem' className='inline-icon icon-red' /> работа в анонимном режиме. Переход на
|
||||
страницу логина
|
||||
</li>
|
||||
<li>
|
||||
<IconArchive size='1.25rem' className='inline-icon' /> просмотр архивной версии. Переход к актуальной
|
||||
версии
|
||||
</li>
|
||||
<li>
|
||||
<IconReader size='1.25rem' className='inline-icon' /> режим Читатель
|
||||
</li>
|
||||
<li>
|
||||
<IconEditor size='1.25rem' className='inline-icon' /> режим Редактор
|
||||
</li>
|
||||
<li>
|
||||
<IconOwner size='1.25rem' className='inline-icon' /> режим Владелец
|
||||
</li>
|
||||
<li>
|
||||
<IconAdmin size='1.25rem' className='inline-icon' /> режим Администратор
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -5,16 +5,18 @@ export function HelpSubstitutions() {
|
|||
<p>Пара отождествлений, обозначает замену вхождений одной конституенты на другую.</p>
|
||||
<p>
|
||||
Таблица отождествлений накладывает следующие ограничения:
|
||||
<li>конституента может быть удаляемой только в одном отождествлении</li>
|
||||
<li>удаляемые конституенты не могут быть замещающими в отождествлениях</li>
|
||||
<li>базисные множества могут замещать только другие базисные множества</li>
|
||||
<li>константные множества могут замещать только другие константные множества</li>
|
||||
<li>
|
||||
при отождествлении конституент, отличных от базисных и константных множеств, их типизации должны совпадать с
|
||||
учетом других отождествлений
|
||||
</li>
|
||||
<li>логические выражения могут замещать только другие логические выражения</li>
|
||||
<li>при отождествлении параметризованных конституент количество и типизации аргументов должно совпадать</li>
|
||||
<ul>
|
||||
<li>конституента может быть удаляемой только в одном отождествлении</li>
|
||||
<li>удаляемые конституенты не могут быть замещающими в отождествлениях</li>
|
||||
<li>базисные множества могут замещать только другие базисные множества</li>
|
||||
<li>константные множества могут замещать только другие константные множества</li>
|
||||
<li>
|
||||
при отождествлении конституент, отличных от базисных и константных множеств, их типизации должны совпадать с
|
||||
учетом других отождествлений
|
||||
</li>
|
||||
<li>логические выражения могут замещать только другие логические выражения</li>
|
||||
<li>при отождествлении параметризованных конституент количество и типизации аргументов должно совпадать</li>
|
||||
</ul>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -7,26 +7,40 @@ export function HelpTypeGraph() {
|
|||
<h1>Граф ступеней</h1>
|
||||
<p>
|
||||
Граф связей между ступенями, используемыми в данном выражении или{' '}
|
||||
<LinkTopic text='КС' topic={HelpTopic.CC_OSS} />. Исторически отображался в форме мультиграфа (М-граф). В
|
||||
Портале кратные ребра представлены перечислением индексов компонент произведения.
|
||||
<LinkTopic text='КС' topic={HelpTopic.CC_OSS} />.<br />
|
||||
Исторически отображался в форме мультиграфа (М-граф).
|
||||
<br />
|
||||
Кратные ребра представлены перечислением индексов компонент произведения.
|
||||
</p>
|
||||
<li>ребра без надписей означают взятие булеана</li>
|
||||
<li>цифры на ребрах означают номера компонент декартова произведения</li>
|
||||
<li>цифры на узлах означают количество конституент в данной ступени</li>
|
||||
<li>основаниями дерева являются ступени базисных, константных множеств</li>
|
||||
<li>ступень терм-функции - произведение ступеней результата и аргументов</li>
|
||||
<li>ступень предикат-функции - произведение ступеней аргументов</li>
|
||||
<ul>
|
||||
<li>ребра без надписей означают взятие булеана</li>
|
||||
<li>цифры на ребрах означают номера компонент декартова произведения</li>
|
||||
<li>цифры на узлах означают количество конституент в данной ступени</li>
|
||||
<li>основаниями дерева являются ступени базисных, константных множеств</li>
|
||||
<li>ступень терм-функции - произведение ступеней результата и аргументов</li>
|
||||
<li>ступень предикат-функции - произведение ступеней аргументов</li>
|
||||
</ul>
|
||||
|
||||
<h2>Виды узлов</h2>
|
||||
<li>
|
||||
<span className='bg-secondary'>ступень-основание</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className='bg-accent-teal'>ступень-булеан</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className='bg-accent-orange'>ступень декартова произведения</span>
|
||||
</li>
|
||||
<h2>Цвета узлов</h2>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<span className='bg-secondary'>ступень-основание</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className='bg-accent-teal'>ступень-булеан</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className='bg-accent-orange'>ступень декартова произведения</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h2>Команды</h2>
|
||||
<ul>
|
||||
<li>
|
||||
<kbd>Space</kbd> – перемещение экрана
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -10,12 +10,13 @@ export const useRenameLocation = () => {
|
|||
const mutation = useMutation({
|
||||
mutationKey: [libraryApi.baseKey, 'rename-location'],
|
||||
mutationFn: libraryApi.renameLocation,
|
||||
onSuccess: () =>
|
||||
Promise.allSettled([
|
||||
onSuccess: async () => {
|
||||
await Promise.allSettled([
|
||||
client.invalidateQueries({ queryKey: [KEYS.library] }),
|
||||
client.invalidateQueries({ queryKey: [KEYS.rsform] }),
|
||||
client.invalidateQueries({ queryKey: [KEYS.oss] })
|
||||
]),
|
||||
]);
|
||||
},
|
||||
onError: () => client.invalidateQueries()
|
||||
});
|
||||
return {
|
||||
|
|
|
@ -16,12 +16,12 @@ export const useSetAccessPolicy = () => {
|
|||
const mutation = useMutation({
|
||||
mutationKey: [KEYS.global_mutation, libraryApi.baseKey, 'set-location'],
|
||||
mutationFn: libraryApi.setAccessPolicy,
|
||||
onSuccess: (_, variables) => {
|
||||
onSuccess: async (_, variables) => {
|
||||
const ossKey = KEYS.composite.ossItem({ itemID: variables.itemID });
|
||||
const ossData: IOperationSchemaDTO | undefined = client.getQueryData(ossKey);
|
||||
if (ossData) {
|
||||
client.setQueryData(ossKey, { ...ossData, access_policy: variables.policy });
|
||||
return Promise.allSettled([
|
||||
await Promise.allSettled([
|
||||
client.invalidateQueries({ queryKey: KEYS.composite.libraryList }),
|
||||
...ossData.operations
|
||||
.map(item => {
|
||||
|
@ -33,6 +33,7 @@ export const useSetAccessPolicy = () => {
|
|||
})
|
||||
.filter(item => !!item)
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
const rsKey = KEYS.composite.rsItem({ itemID: variables.itemID });
|
||||
|
|
|
@ -12,12 +12,12 @@ export const useSetEditors = () => {
|
|||
const mutation = useMutation({
|
||||
mutationKey: [KEYS.global_mutation, libraryApi.baseKey, 'set-location'],
|
||||
mutationFn: libraryApi.setEditors,
|
||||
onSuccess: (_, variables) => {
|
||||
onSuccess: async (_, variables) => {
|
||||
const ossKey = KEYS.composite.ossItem({ itemID: variables.itemID });
|
||||
const ossData: IOperationSchemaDTO | undefined = client.getQueryData(ossKey);
|
||||
if (ossData) {
|
||||
client.setQueryData(ossKey, { ...ossData, editors: variables.editors });
|
||||
return Promise.allSettled(
|
||||
await Promise.allSettled(
|
||||
ossData.operations
|
||||
.map(item => {
|
||||
if (!item.result) {
|
||||
|
@ -28,6 +28,7 @@ export const useSetEditors = () => {
|
|||
})
|
||||
.filter(item => !!item)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const rsKey = KEYS.composite.rsItem({ itemID: variables.itemID });
|
||||
|
|
|
@ -16,12 +16,12 @@ export const useSetLocation = () => {
|
|||
const mutation = useMutation({
|
||||
mutationKey: [KEYS.global_mutation, libraryApi.baseKey, 'set-location'],
|
||||
mutationFn: libraryApi.setLocation,
|
||||
onSuccess: (_, variables) => {
|
||||
onSuccess: async (_, variables) => {
|
||||
const ossKey = KEYS.composite.ossItem({ itemID: variables.itemID });
|
||||
const ossData: IOperationSchemaDTO | undefined = client.getQueryData(ossKey);
|
||||
if (ossData) {
|
||||
client.setQueryData(ossKey, { ...ossData, location: variables.location });
|
||||
return Promise.allSettled([
|
||||
await Promise.allSettled([
|
||||
client.invalidateQueries({ queryKey: libraryApi.libraryListKey }),
|
||||
...ossData.operations
|
||||
.map(item => {
|
||||
|
@ -33,6 +33,7 @@ export const useSetLocation = () => {
|
|||
})
|
||||
.filter(item => !!item)
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
const rsKey = KEYS.composite.rsItem({ itemID: variables.itemID });
|
||||
|
|
|
@ -16,12 +16,12 @@ export const useSetOwner = () => {
|
|||
const mutation = useMutation({
|
||||
mutationKey: [KEYS.global_mutation, libraryApi.baseKey, 'set-owner'],
|
||||
mutationFn: libraryApi.setOwner,
|
||||
onSuccess: (_, variables) => {
|
||||
onSuccess: async (_, variables) => {
|
||||
const ossKey = KEYS.composite.ossItem({ itemID: variables.itemID });
|
||||
const ossData: IOperationSchemaDTO | undefined = client.getQueryData(ossKey);
|
||||
if (ossData) {
|
||||
client.setQueryData(ossKey, { ...ossData, owner: variables.owner });
|
||||
return Promise.allSettled([
|
||||
await Promise.allSettled([
|
||||
client.invalidateQueries({ queryKey: libraryApi.libraryListKey }),
|
||||
...ossData.operations
|
||||
.map(item => {
|
||||
|
|
|
@ -16,7 +16,7 @@ export const useUpdateItem = () => {
|
|||
const mutation = useMutation({
|
||||
mutationKey: [KEYS.global_mutation, libraryApi.baseKey, 'update-item'],
|
||||
mutationFn: libraryApi.updateItem,
|
||||
onSuccess: (data: ILibraryItem) => {
|
||||
onSuccess: async (data: ILibraryItem) => {
|
||||
const itemKey =
|
||||
data.item_type === LibraryItemType.RSFORM
|
||||
? KEYS.composite.rsItem({ itemID: data.id })
|
||||
|
@ -30,7 +30,7 @@ export const useUpdateItem = () => {
|
|||
if (data.item_type === LibraryItemType.RSFORM) {
|
||||
const schema: IRSFormDTO | undefined = client.getQueryData(itemKey);
|
||||
if (schema) {
|
||||
return Promise.allSettled(
|
||||
await Promise.allSettled(
|
||||
schema.oss.map(item => client.invalidateQueries({ queryKey: KEYS.composite.ossItem({ itemID: item.id }) }))
|
||||
);
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import { cn } from '@/components/utils';
|
|||
import { prefixes } from '@/utils/constants';
|
||||
import { tripleToggleColor } from '@/utils/utils';
|
||||
|
||||
import { useLibrarySuspense } from '../../backend/use-library';
|
||||
import { IconItemVisibility } from '../../components/icon-item-visibility';
|
||||
import { IconLocationHead } from '../../components/icon-location-head';
|
||||
import { describeLocationHead, labelLocationHead } from '../../labels';
|
||||
|
@ -33,6 +34,7 @@ interface ToolbarSearchProps {
|
|||
}
|
||||
|
||||
export function ToolbarSearch({ className, total, filtered }: ToolbarSearchProps) {
|
||||
const { items } = useLibrarySuspense();
|
||||
const userMenu = useDropdown();
|
||||
const headMenu = useDropdown();
|
||||
|
||||
|
@ -58,6 +60,10 @@ export function ToolbarSearch({ className, total, filtered }: ToolbarSearchProps
|
|||
|
||||
const userActive = isOwned !== null || isEditor !== null || filterUser !== null;
|
||||
|
||||
function filterNonEmptyUsers(userID: number): boolean {
|
||||
return items.some(item => item.owner === userID);
|
||||
}
|
||||
|
||||
function handleChange(newValue: LocationHead | null) {
|
||||
headMenu.hide();
|
||||
setHead(newValue);
|
||||
|
@ -114,6 +120,7 @@ export function ToolbarSearch({ className, total, filtered }: ToolbarSearchProps
|
|||
placeholder='Выберите владельца'
|
||||
noBorder
|
||||
className='min-w-60 mx-1 mb-1'
|
||||
filter={filterNonEmptyUsers}
|
||||
value={filterUser}
|
||||
onChange={setFilterUser}
|
||||
/>
|
||||
|
|
|
@ -7,7 +7,15 @@ import { type ILibraryItem } from '@/features/library';
|
|||
import { Graph } from '@/models/graph';
|
||||
import { type RO } from '@/utils/meta';
|
||||
|
||||
import { type IBlock, type IOperation, type IOperationSchema, type IOperationSchemaStats } from '../models/oss';
|
||||
import {
|
||||
type IBlock,
|
||||
type IOperation,
|
||||
type IOperationSchema,
|
||||
type IOperationSchemaStats,
|
||||
type IOssItem,
|
||||
NodeType
|
||||
} from '../models/oss';
|
||||
import { constructNodeID } from '../models/oss-api';
|
||||
import { BLOCK_NODE_MIN_HEIGHT, BLOCK_NODE_MIN_WIDTH } from '../pages/oss-page/editor-oss-graph/graph/block-node';
|
||||
|
||||
import { type IOperationSchemaDTO, OperationType } from './types';
|
||||
|
@ -16,8 +24,9 @@ import { type IOperationSchemaDTO, OperationType } from './types';
|
|||
export class OssLoader {
|
||||
private oss: IOperationSchema;
|
||||
private graph: Graph = new Graph();
|
||||
private hierarchy: Graph = new Graph();
|
||||
private hierarchy: Graph<string> = new Graph<string>();
|
||||
private operationByID = new Map<number, IOperation>();
|
||||
private itemByNodeID = new Map<string, IOssItem>();
|
||||
private blockByID = new Map<number, IBlock>();
|
||||
private schemaIDs: number[] = [];
|
||||
private items: RO<ILibraryItem[]>;
|
||||
|
@ -37,6 +46,7 @@ export class OssLoader {
|
|||
|
||||
result.operationByID = this.operationByID;
|
||||
result.blockByID = this.blockByID;
|
||||
result.itemByNodeID = this.itemByNodeID;
|
||||
result.graph = this.graph;
|
||||
result.hierarchy = this.hierarchy;
|
||||
result.schemas = this.schemaIDs;
|
||||
|
@ -46,18 +56,24 @@ export class OssLoader {
|
|||
|
||||
private prepareLookups() {
|
||||
this.oss.operations.forEach(operation => {
|
||||
operation.nodeID = constructNodeID(NodeType.OPERATION, operation.id);
|
||||
operation.nodeType = NodeType.OPERATION;
|
||||
this.itemByNodeID.set(operation.nodeID, operation);
|
||||
this.operationByID.set(operation.id, operation);
|
||||
this.graph.addNode(operation.id);
|
||||
this.hierarchy.addNode(operation.id);
|
||||
this.hierarchy.addNode(operation.nodeID);
|
||||
if (operation.parent) {
|
||||
this.hierarchy.addEdge(-operation.parent, operation.id);
|
||||
this.hierarchy.addEdge(constructNodeID(NodeType.BLOCK, operation.parent), operation.nodeID);
|
||||
}
|
||||
});
|
||||
this.oss.blocks.forEach(block => {
|
||||
block.nodeID = constructNodeID(NodeType.BLOCK, block.id);
|
||||
block.nodeType = NodeType.BLOCK;
|
||||
this.itemByNodeID.set(block.nodeID, block);
|
||||
this.blockByID.set(block.id, block);
|
||||
this.hierarchy.addNode(-block.id);
|
||||
this.hierarchy.addNode(block.nodeID);
|
||||
if (block.parent) {
|
||||
this.hierarchy.addEdge(-block.parent, -block.id);
|
||||
this.hierarchy.addEdge(constructNodeID(NodeType.BLOCK, block.parent), block.nodeID);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -72,6 +72,12 @@ export type IRelocateConstituentsDTO = z.infer<typeof schemaRelocateConstituents
|
|||
/** Represents {@link IConstituenta} reference. */
|
||||
export type IConstituentaReference = z.infer<typeof schemaConstituentaReference>;
|
||||
|
||||
/** Represents {@link IOperation} position. */
|
||||
export type IOperationPosition = z.infer<typeof schemaOperationPosition>;
|
||||
|
||||
/** Represents {@link IBlock} position. */
|
||||
export type IBlockPosition = z.infer<typeof schemaBlockPosition>;
|
||||
|
||||
// ====== Schemas ======
|
||||
export const schemaOperationType = z.enum(Object.values(OperationType) as [OperationType, ...OperationType[]]);
|
||||
|
||||
|
|
|
@ -10,9 +10,9 @@ export const useCreateInput = () => {
|
|||
const mutation = useMutation({
|
||||
mutationKey: [KEYS.global_mutation, ossApi.baseKey, 'create-input'],
|
||||
mutationFn: ossApi.createInput,
|
||||
onSuccess: data => {
|
||||
onSuccess: async data => {
|
||||
client.setQueryData(ossApi.getOssQueryOptions({ itemID: data.oss.id }).queryKey, data.oss);
|
||||
return Promise.allSettled([
|
||||
await Promise.allSettled([
|
||||
client.invalidateQueries({ queryKey: KEYS.composite.libraryList }),
|
||||
client.invalidateQueries({ queryKey: [KEYS.rsform] })
|
||||
]);
|
||||
|
|
|
@ -10,9 +10,9 @@ export const useDeleteBlock = () => {
|
|||
const mutation = useMutation({
|
||||
mutationKey: [KEYS.global_mutation, ossApi.baseKey, 'delete-block'],
|
||||
mutationFn: ossApi.deleteBlock,
|
||||
onSuccess: data => {
|
||||
onSuccess: async data => {
|
||||
client.setQueryData(ossApi.getOssQueryOptions({ itemID: data.id }).queryKey, data);
|
||||
return Promise.allSettled([
|
||||
await Promise.allSettled([
|
||||
client.invalidateQueries({ queryKey: KEYS.composite.libraryList }),
|
||||
client.invalidateQueries({ queryKey: [KEYS.rsform] })
|
||||
]);
|
||||
|
|
|
@ -10,9 +10,9 @@ export const useDeleteOperation = () => {
|
|||
const mutation = useMutation({
|
||||
mutationKey: [KEYS.global_mutation, ossApi.baseKey, 'delete-operation'],
|
||||
mutationFn: ossApi.deleteOperation,
|
||||
onSuccess: data => {
|
||||
onSuccess: async data => {
|
||||
client.setQueryData(ossApi.getOssQueryOptions({ itemID: data.id }).queryKey, data);
|
||||
return Promise.allSettled([
|
||||
await Promise.allSettled([
|
||||
client.invalidateQueries({ queryKey: KEYS.composite.libraryList }),
|
||||
client.invalidateQueries({ queryKey: [KEYS.rsform] })
|
||||
]);
|
||||
|
|
|
@ -10,9 +10,9 @@ export const useExecuteOperation = () => {
|
|||
const mutation = useMutation({
|
||||
mutationKey: [KEYS.global_mutation, ossApi.baseKey, 'execute-operation'],
|
||||
mutationFn: ossApi.executeOperation,
|
||||
onSuccess: data => {
|
||||
onSuccess: async data => {
|
||||
client.setQueryData(ossApi.getOssQueryOptions({ itemID: data.id }).queryKey, data);
|
||||
return Promise.allSettled([
|
||||
await Promise.allSettled([
|
||||
client.invalidateQueries({ queryKey: KEYS.composite.libraryList }),
|
||||
client.invalidateQueries({ queryKey: [KEYS.rsform] })
|
||||
]);
|
||||
|
|
|
@ -10,9 +10,9 @@ export const useMoveItems = () => {
|
|||
const mutation = useMutation({
|
||||
mutationKey: [KEYS.global_mutation, ossApi.baseKey, 'move-items'],
|
||||
mutationFn: ossApi.moveItems,
|
||||
onSuccess: data => {
|
||||
onSuccess: async data => {
|
||||
client.setQueryData(ossApi.getOssQueryOptions({ itemID: data.id }).queryKey, data);
|
||||
return Promise.allSettled([
|
||||
await Promise.allSettled([
|
||||
client.invalidateQueries({ queryKey: KEYS.composite.libraryList }),
|
||||
client.invalidateQueries({ queryKey: [KEYS.rsform] })
|
||||
]);
|
||||
|
|
|
@ -10,9 +10,9 @@ export const useRelocateConstituents = () => {
|
|||
const mutation = useMutation({
|
||||
mutationKey: [KEYS.global_mutation, ossApi.baseKey, 'relocate-constituents'],
|
||||
mutationFn: ossApi.relocateConstituents,
|
||||
onSuccess: data => {
|
||||
onSuccess: async data => {
|
||||
client.setQueryData(ossApi.getOssQueryOptions({ itemID: data.id }).queryKey, data);
|
||||
return Promise.allSettled([
|
||||
await Promise.allSettled([
|
||||
client.invalidateQueries({ queryKey: KEYS.composite.libraryList }),
|
||||
client.invalidateQueries({ queryKey: [KEYS.rsform] })
|
||||
]);
|
||||
|
|
|
@ -10,9 +10,9 @@ export const useUpdateInput = () => {
|
|||
const mutation = useMutation({
|
||||
mutationKey: [KEYS.global_mutation, ossApi.baseKey, 'update-input'],
|
||||
mutationFn: ossApi.updateInput,
|
||||
onSuccess: data => {
|
||||
onSuccess: async data => {
|
||||
client.setQueryData(ossApi.getOssQueryOptions({ itemID: data.id }).queryKey, data);
|
||||
return Promise.allSettled([
|
||||
await Promise.allSettled([
|
||||
client.invalidateQueries({ queryKey: KEYS.composite.libraryList }),
|
||||
client.invalidateQueries({ queryKey: [KEYS.rsform] })
|
||||
]);
|
||||
|
|
|
@ -9,24 +9,22 @@ import { ComboBox } from '@/components/input/combo-box';
|
|||
import { type Styling } from '@/components/props';
|
||||
import { cn } from '@/components/utils';
|
||||
import { NoData } from '@/components/view';
|
||||
import { type RO } from '@/utils/meta';
|
||||
|
||||
import { labelOssItem } from '../labels';
|
||||
import { type IOperationSchema, type IOssItem } from '../models/oss';
|
||||
import { getItemID, isOperation } from '../models/oss-api';
|
||||
import { type IOperationSchema, type IOssItem, NodeType } from '../models/oss';
|
||||
|
||||
const SELECTION_CLEAR_TIMEOUT = 1000;
|
||||
|
||||
interface PickMultiOperationProps extends Styling {
|
||||
value: number[];
|
||||
onChange: (newValue: number[]) => void;
|
||||
interface PickContentsProps extends Styling {
|
||||
value: IOssItem[];
|
||||
onChange: (newValue: IOssItem[]) => void;
|
||||
schema: IOperationSchema;
|
||||
rows?: number;
|
||||
exclude?: number[];
|
||||
exclude?: IOssItem[];
|
||||
disallowBlocks?: boolean;
|
||||
}
|
||||
|
||||
const columnHelper = createColumnHelper<RO<IOssItem>>();
|
||||
const columnHelper = createColumnHelper<IOssItem>();
|
||||
|
||||
export function PickContents({
|
||||
rows,
|
||||
|
@ -37,29 +35,26 @@ export function PickContents({
|
|||
onChange,
|
||||
className,
|
||||
...restProps
|
||||
}: PickMultiOperationProps) {
|
||||
const selectedItems = value
|
||||
.map(itemID => (itemID > 0 ? schema.operationByID.get(itemID) : schema.blockByID.get(-itemID)))
|
||||
.filter(item => item !== undefined);
|
||||
const [lastSelected, setLastSelected] = useState<RO<IOssItem> | null>(null);
|
||||
const items = [
|
||||
...(disallowBlocks ? [] : schema.blocks.filter(item => !value.includes(-item.id) && !exclude?.includes(-item.id))),
|
||||
...schema.operations.filter(item => !value.includes(item.id) && !exclude?.includes(item.id))
|
||||
}: PickContentsProps) {
|
||||
const [lastSelected, setLastSelected] = useState<IOssItem | null>(null);
|
||||
const items: IOssItem[] = [
|
||||
...(disallowBlocks ? [] : schema.blocks.filter(item => !value.includes(item) && !exclude?.includes(item))),
|
||||
...schema.operations.filter(item => !value.includes(item) && !exclude?.includes(item))
|
||||
];
|
||||
|
||||
function handleDelete(target: number) {
|
||||
function handleDelete(target: IOssItem) {
|
||||
onChange(value.filter(item => item !== target));
|
||||
}
|
||||
|
||||
function handleSelect(target: RO<IOssItem> | null) {
|
||||
function handleSelect(target: IOssItem | null) {
|
||||
if (target) {
|
||||
setLastSelected(target);
|
||||
onChange([...value, getItemID(target)]);
|
||||
onChange([...value, target]);
|
||||
setTimeout(() => setLastSelected(null), SELECTION_CLEAR_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
function handleMoveUp(target: number) {
|
||||
function handleMoveUp(target: IOssItem) {
|
||||
const index = value.indexOf(target);
|
||||
if (index > 0) {
|
||||
const newSelected = [...value];
|
||||
|
@ -69,7 +64,7 @@ export function PickContents({
|
|||
}
|
||||
}
|
||||
|
||||
function handleMoveDown(target: number) {
|
||||
function handleMoveDown(target: IOssItem) {
|
||||
const index = value.indexOf(target);
|
||||
if (index < value.length - 1) {
|
||||
const newSelected = [...value];
|
||||
|
@ -80,13 +75,13 @@ export function PickContents({
|
|||
}
|
||||
|
||||
const columns = [
|
||||
columnHelper.accessor(item => isOperation(item), {
|
||||
columnHelper.accessor(item => item.nodeType === NodeType.OPERATION, {
|
||||
id: 'type',
|
||||
header: 'Тип',
|
||||
size: 150,
|
||||
minSize: 150,
|
||||
maxSize: 150,
|
||||
cell: props => <div>{isOperation(props.row.original) ? 'Операция' : 'Блок'}</div>
|
||||
cell: props => <div>{props.getValue() ? 'Операция' : 'Блок'}</div>
|
||||
}),
|
||||
columnHelper.accessor('title', {
|
||||
id: 'title',
|
||||
|
@ -106,21 +101,21 @@ export function PickContents({
|
|||
noHover
|
||||
className='px-0'
|
||||
icon={<IconRemove size='1rem' className='icon-red' />}
|
||||
onClick={() => handleDelete(getItemID(props.row.original))}
|
||||
onClick={() => handleDelete(props.row.original)}
|
||||
/>
|
||||
<MiniButton
|
||||
title='Переместить выше'
|
||||
noHover
|
||||
className='px-0'
|
||||
icon={<IconMoveUp size='1rem' className='icon-primary' />}
|
||||
onClick={() => handleMoveUp(getItemID(props.row.original))}
|
||||
onClick={() => handleMoveUp(props.row.original)}
|
||||
/>
|
||||
<MiniButton
|
||||
title='Переместить ниже'
|
||||
noHover
|
||||
className='px-0'
|
||||
icon={<IconMoveDown size='1rem' className='icon-primary' />}
|
||||
onClick={() => handleMoveDown(getItemID(props.row.original))}
|
||||
onClick={() => handleMoveDown(props.row.original)}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
@ -134,7 +129,7 @@ export function PickContents({
|
|||
items={items}
|
||||
value={lastSelected}
|
||||
placeholder='Выберите операцию или блок'
|
||||
idFunc={item => String(getItemID(item))}
|
||||
idFunc={item => item.nodeID}
|
||||
labelValueFunc={item => labelOssItem(item)}
|
||||
labelOptionFunc={item => labelOssItem(item)}
|
||||
onChange={handleSelect}
|
||||
|
@ -145,7 +140,7 @@ export function PickContents({
|
|||
rows={rows}
|
||||
contentHeight='1.3rem'
|
||||
className='cc-scroll-y text-sm select-none border-y rounded-b-md'
|
||||
data={selectedItems}
|
||||
data={value}
|
||||
columns={columns}
|
||||
headPosition='0rem'
|
||||
noDataComponent={
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import { Tooltip } from '@/components/container';
|
||||
import { globalIDs } from '@/utils/constants';
|
||||
|
||||
import { type IBlock, type IOperation } from '../models/oss';
|
||||
import { isOperation } from '../models/oss-api';
|
||||
import { NodeType } from '../models/oss';
|
||||
import { useOperationTooltipStore } from '../stores/operation-tooltip';
|
||||
|
||||
import { InfoBlock } from './info-block';
|
||||
|
@ -10,7 +9,7 @@ import { InfoOperation } from './info-operation';
|
|||
|
||||
export function OperationTooltip() {
|
||||
const hoverItem = useOperationTooltipStore(state => state.hoverItem);
|
||||
const isOperationNode = isOperation(hoverItem);
|
||||
const isOperationNode = hoverItem?.nodeType === NodeType.OPERATION;
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
|
@ -20,8 +19,8 @@ export function OperationTooltip() {
|
|||
className='max-w-140 dense max-h-120! overflow-y-auto!'
|
||||
hidden={!hoverItem}
|
||||
>
|
||||
{hoverItem && isOperationNode ? <InfoOperation operation={hoverItem as IOperation} /> : null}
|
||||
{hoverItem && !isOperationNode ? <InfoBlock block={hoverItem as IBlock} /> : null}
|
||||
{hoverItem && isOperationNode ? <InfoOperation operation={hoverItem} /> : null}
|
||||
{hoverItem && !isOperationNode ? <InfoBlock block={hoverItem} /> : null}
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import { useDialogsStore } from '@/stores/dialogs';
|
|||
|
||||
import { type ICreateBlockDTO, schemaCreateBlock } from '../../backend/types';
|
||||
import { useCreateBlock } from '../../backend/use-create-block';
|
||||
import { type IOssItem, NodeType } from '../../models/oss';
|
||||
import { type LayoutManager } from '../../models/oss-layout-api';
|
||||
import { BLOCK_NODE_MIN_HEIGHT, BLOCK_NODE_MIN_WIDTH } from '../../pages/oss-page/editor-oss-graph/graph/block-node';
|
||||
|
||||
|
@ -20,7 +21,7 @@ import { TabBlockChildren } from './tab-block-children';
|
|||
|
||||
export interface DlgCreateBlockProps {
|
||||
manager: LayoutManager;
|
||||
initialChildren: number[];
|
||||
initialChildren: IOssItem[];
|
||||
initialParent: number | null;
|
||||
defaultX: number;
|
||||
defaultY: number;
|
||||
|
@ -52,8 +53,8 @@ export function DlgCreateBlock() {
|
|||
position_y: defaultY,
|
||||
width: BLOCK_NODE_MIN_WIDTH,
|
||||
height: BLOCK_NODE_MIN_HEIGHT,
|
||||
children_blocks: initialChildren.filter(id => id < 0).map(id => -id),
|
||||
children_operations: initialChildren.filter(id => id > 0),
|
||||
children_blocks: initialChildren.filter(item => item.nodeType === NodeType.BLOCK).map(item => item.id),
|
||||
children_operations: initialChildren.filter(item => item.nodeType === NodeType.OPERATION).map(item => item.id),
|
||||
layout: manager.layout
|
||||
},
|
||||
mode: 'onChange'
|
||||
|
@ -65,11 +66,12 @@ export function DlgCreateBlock() {
|
|||
const isValid = !!title && !manager.oss.blocks.some(block => block.title === title);
|
||||
|
||||
function onSubmit(data: ICreateBlockDTO) {
|
||||
const rectangle = manager.calculateNewBlockPosition(data);
|
||||
const rectangle = manager.newBlockPosition(data);
|
||||
data.position_x = rectangle.x;
|
||||
data.position_y = rectangle.y;
|
||||
data.width = rectangle.width;
|
||||
data.height = rectangle.height;
|
||||
data.layout = manager.layout;
|
||||
void createBlock({ itemID: manager.oss.id, data: data }).then(response => onCreate?.(response.new_block.id));
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,8 @@ import { useDialogsStore } from '@/stores/dialogs';
|
|||
|
||||
import { type ICreateBlockDTO } from '../../backend/types';
|
||||
import { SelectParent } from '../../components/select-parent';
|
||||
import { NodeType } from '../../models/oss';
|
||||
import { constructNodeID } from '../../models/oss-api';
|
||||
|
||||
import { type DlgCreateBlockProps } from './dlg-create-block';
|
||||
|
||||
|
@ -18,10 +20,8 @@ export function TabBlockCard() {
|
|||
formState: { errors }
|
||||
} = useFormContext<ICreateBlockDTO>();
|
||||
const children_blocks = useWatch({ control, name: 'children_blocks' });
|
||||
const all_children = [
|
||||
...children_blocks,
|
||||
...manager.oss.hierarchy.expandAllOutputs(children_blocks.filter(id => id < 0).map(id => -id)).map(id => -id)
|
||||
];
|
||||
const block_ids = children_blocks.map(id => constructNodeID(NodeType.BLOCK, id));
|
||||
const all_children = [...block_ids, ...manager.oss.hierarchy.expandAllOutputs(block_ids)];
|
||||
|
||||
return (
|
||||
<div className='cc-fade-in cc-column'>
|
||||
|
@ -36,7 +36,7 @@ export function TabBlockCard() {
|
|||
control={control}
|
||||
render={({ field }) => (
|
||||
<SelectParent
|
||||
items={manager.oss.blocks.filter(block => !all_children.includes(block.id))}
|
||||
items={manager.oss.blocks.filter(block => !all_children.includes(block.nodeID))}
|
||||
value={field.value ? manager.oss.blockByID.get(field.value) ?? null : null}
|
||||
placeholder='Блок содержания не выбран'
|
||||
onChange={value => field.onChange(value ? value.id : null)}
|
||||
|
|
|
@ -6,6 +6,7 @@ import { useDialogsStore } from '@/stores/dialogs';
|
|||
|
||||
import { type ICreateBlockDTO } from '../../backend/types';
|
||||
import { PickContents } from '../../components/pick-contents';
|
||||
import { type IOssItem, NodeType } from '../../models/oss';
|
||||
|
||||
import { type DlgCreateBlockProps } from './dlg-create-block';
|
||||
|
||||
|
@ -15,19 +16,31 @@ export function TabBlockChildren() {
|
|||
const parent = useWatch({ control, name: 'item_data.parent' });
|
||||
const children_blocks = useWatch({ control, name: 'children_blocks' });
|
||||
const children_operations = useWatch({ control, name: 'children_operations' });
|
||||
const exclude = parent ? [-parent, ...manager.oss.hierarchy.expandAllInputs([-parent]).filter(id => id < 0)] : [];
|
||||
|
||||
const value = [...children_blocks.map(id => -id), ...children_operations];
|
||||
const parentItem = parent ? manager.oss.blockByID.get(parent) : null;
|
||||
const internalBlocks = parentItem
|
||||
? manager.oss.hierarchy
|
||||
.expandAllInputs([parentItem.nodeID])
|
||||
.map(id => manager.oss.itemByNodeID.get(id))
|
||||
.filter(item => item !== null && item?.nodeType === NodeType.BLOCK)
|
||||
: [];
|
||||
|
||||
function handleChangeSelected(newValue: number[]) {
|
||||
const exclude = parentItem ? [parentItem, ...internalBlocks] : [];
|
||||
|
||||
const value = [
|
||||
...children_blocks.map(id => manager.oss.blockByID.get(id)!),
|
||||
...children_operations.map(id => manager.oss.operationByID.get(id)!)
|
||||
];
|
||||
|
||||
function handleChangeSelected(newValue: IOssItem[]) {
|
||||
setValue(
|
||||
'children_blocks',
|
||||
newValue.filter(id => id < 0).map(id => -id),
|
||||
newValue.filter(item => item.nodeType === NodeType.BLOCK).map(item => item.id),
|
||||
{ shouldValidate: true }
|
||||
);
|
||||
setValue(
|
||||
'children_operations',
|
||||
newValue.filter(id => id > 0),
|
||||
newValue.filter(item => item.nodeType === NodeType.OPERATION).map(item => item.id),
|
||||
{ shouldValidate: true }
|
||||
);
|
||||
}
|
||||
|
|
|
@ -64,9 +64,10 @@ export function DlgCreateOperation() {
|
|||
const isValid = !!alias && !manager.oss.operations.some(operation => operation.alias === alias);
|
||||
|
||||
function onSubmit(data: ICreateOperationDTO) {
|
||||
const target = manager.calculateNewOperationPosition(data);
|
||||
const target = manager.newOperationPosition(data);
|
||||
data.position_x = target.x;
|
||||
data.position_y = target.y;
|
||||
data.layout = manager.layout;
|
||||
void createOperation({ itemID: manager.oss.id, data: data }).then(response =>
|
||||
onCreate?.(response.new_operation.id)
|
||||
);
|
||||
|
|
|
@ -42,6 +42,10 @@ export function DlgEditBlock() {
|
|||
});
|
||||
|
||||
function onSubmit(data: IUpdateBlockDTO) {
|
||||
if (data.item_data.parent !== target.parent) {
|
||||
manager.onBlockChangeParent(data.target, data.item_data.parent);
|
||||
data.layout = manager.layout;
|
||||
}
|
||||
return updateBlock({ itemID: manager.oss.id, data });
|
||||
}
|
||||
|
||||
|
|
|
@ -58,9 +58,10 @@ export function DlgEditOperation() {
|
|||
const [activeTab, setActiveTab] = useState<TabID>(TabID.CARD);
|
||||
|
||||
function onSubmit(data: IUpdateOperationDTO) {
|
||||
// if (data.item_data.parent !== target.parent) {
|
||||
// data.layout = updateLayoutOnOperationChange(data.target, data.item_data.parent, data.layout);
|
||||
// }
|
||||
if (data.item_data.parent !== target.parent) {
|
||||
manager.onOperationChangeParent(data.target, data.item_data.parent);
|
||||
data.layout = manager.layout;
|
||||
}
|
||||
return updateOperation({ itemID: manager.oss.id, data });
|
||||
}
|
||||
|
||||
|
|
|
@ -5,9 +5,9 @@ import {
|
|||
type IOperation,
|
||||
type IOssItem,
|
||||
type ISubstitutionErrorDescription,
|
||||
NodeType,
|
||||
SubstitutionErrorType
|
||||
} from './models/oss';
|
||||
import { isOperation } from './models/oss-api';
|
||||
|
||||
/** Retrieves label for {@link OperationType}. */
|
||||
export function labelOperationType(itemType: OperationType): string {
|
||||
|
@ -58,7 +58,7 @@ export function describeSubstitutionError(error: RO<ISubstitutionErrorDescriptio
|
|||
|
||||
/** Retrieves label for {@link IOssItem}. */
|
||||
export function labelOssItem(item: RO<IOssItem>): string {
|
||||
if (isOperation(item)) {
|
||||
if (item.nodeType === NodeType.OPERATION) {
|
||||
return `${(item as IOperation).alias}: ${item.title}`;
|
||||
} else {
|
||||
return `Блок: ${item.title}`;
|
||||
|
|
|
@ -20,23 +20,16 @@ import {
|
|||
} from '@/features/rsform/models/rslang-api';
|
||||
|
||||
import { infoMsg } from '@/utils/labels';
|
||||
import { type RO } from '@/utils/meta';
|
||||
|
||||
import { Graph } from '../../../models/graph';
|
||||
import { describeSubstitutionError } from '../labels';
|
||||
|
||||
import { type IOperationSchema, type IOssItem, SubstitutionErrorType } from './oss';
|
||||
import { type IOperationSchema, NodeType, SubstitutionErrorType } from './oss';
|
||||
|
||||
const STARTING_SUB_INDEX = 900; // max semantic index for starting substitution
|
||||
|
||||
/** Checks if element is {@link IOperation} or {@link IBlock}. */
|
||||
export function isOperation(item: RO<IOssItem> | null): boolean {
|
||||
return !!item && 'arguments' in item;
|
||||
}
|
||||
|
||||
/** Extract contiguous ID of {@link IOperation} or {@link IBlock}. */
|
||||
export function getItemID(item: RO<IOssItem>): number {
|
||||
return isOperation(item) ? item.id : -item.id;
|
||||
export function constructNodeID(type: NodeType, itemID: number): string {
|
||||
return type === NodeType.OPERATION ? 'o' + String(itemID) : 'b' + String(itemID);
|
||||
}
|
||||
|
||||
/** Sorts library items relevant for the specified {@link IOperationSchema}. */
|
||||
|
|
|
@ -1,12 +1,16 @@
|
|||
import { type ICreateBlockDTO, type ICreateOperationDTO, type IOssLayout } from '../backend/types';
|
||||
import {
|
||||
type IBlockPosition,
|
||||
type ICreateBlockDTO,
|
||||
type ICreateOperationDTO,
|
||||
type IOperationPosition,
|
||||
type IOssLayout
|
||||
} from '../backend/types';
|
||||
|
||||
import { type IOperationSchema } from './oss';
|
||||
import { type Position2D, type Rectangle2D } from './oss-layout';
|
||||
|
||||
export const GRID_SIZE = 10; // pixels - size of OSS grid
|
||||
const MIN_DISTANCE = 2 * GRID_SIZE; // pixels - minimum distance between nodes
|
||||
const DISTANCE_X = 180; // pixels - insert x-distance between node centers
|
||||
const DISTANCE_Y = 100; // pixels - insert y-distance between node centers
|
||||
|
||||
const OPERATION_NODE_WIDTH = 150;
|
||||
const OPERATION_NODE_HEIGHT = 40;
|
||||
|
@ -26,93 +30,236 @@ export class LayoutManager {
|
|||
}
|
||||
|
||||
/** Calculate insert position for a new {@link IOperation} */
|
||||
calculateNewOperationPosition(data: ICreateOperationDTO): Position2D {
|
||||
// TODO: check parent node
|
||||
|
||||
const result = { x: data.position_x, y: data.position_y };
|
||||
newOperationPosition(data: ICreateOperationDTO): Position2D {
|
||||
let result = { x: data.position_x, y: data.position_y };
|
||||
const operations = this.layout.operations;
|
||||
const parentNode = this.layout.blocks.find(pos => pos.id === data.item_data.parent);
|
||||
if (operations.length === 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (data.arguments.length === 0) {
|
||||
let inputsPositions = operations.filter(pos =>
|
||||
this.oss.operations.find(operation => operation.arguments.length === 0 && operation.id === pos.id)
|
||||
);
|
||||
if (inputsPositions.length === 0) {
|
||||
inputsPositions = operations;
|
||||
}
|
||||
const maxX = Math.max(...inputsPositions.map(node => node.x));
|
||||
const minY = Math.min(...inputsPositions.map(node => node.y));
|
||||
result.x = maxX + DISTANCE_X;
|
||||
result.y = minY;
|
||||
if (data.arguments.length !== 0) {
|
||||
result = calculatePositionFromArgs(data.arguments, operations);
|
||||
} else if (parentNode) {
|
||||
result.x = parentNode.x + MIN_DISTANCE;
|
||||
result.y = parentNode.y + MIN_DISTANCE;
|
||||
} else {
|
||||
const argNodes = operations.filter(pos => data.arguments.includes(pos.id));
|
||||
const maxY = Math.max(...argNodes.map(node => node.y));
|
||||
const minX = Math.min(...argNodes.map(node => node.x));
|
||||
const maxX = Math.max(...argNodes.map(node => node.x));
|
||||
result.x = Math.ceil((maxX + minX) / 2 / GRID_SIZE) * GRID_SIZE;
|
||||
result.y = maxY + DISTANCE_Y;
|
||||
result = this.calculatePositionForFreeOperation(result);
|
||||
}
|
||||
|
||||
let flagIntersect = false;
|
||||
do {
|
||||
flagIntersect = operations.some(
|
||||
position => Math.abs(position.x - result.x) < MIN_DISTANCE && Math.abs(position.y - result.y) < MIN_DISTANCE
|
||||
);
|
||||
if (flagIntersect) {
|
||||
result.x += MIN_DISTANCE;
|
||||
result.y += MIN_DISTANCE;
|
||||
result = preventOverlap(
|
||||
{ ...result, width: OPERATION_NODE_WIDTH, height: OPERATION_NODE_HEIGHT },
|
||||
operations.map(node => ({ ...node, width: OPERATION_NODE_WIDTH, height: OPERATION_NODE_HEIGHT }))
|
||||
);
|
||||
|
||||
if (parentNode) {
|
||||
const borderX = result.x + OPERATION_NODE_WIDTH + MIN_DISTANCE;
|
||||
const borderY = result.y + OPERATION_NODE_HEIGHT + MIN_DISTANCE;
|
||||
if (borderX > parentNode.x + parentNode.width) {
|
||||
parentNode.width = borderX - parentNode.x;
|
||||
}
|
||||
} while (flagIntersect);
|
||||
return result;
|
||||
if (borderY > parentNode.y + parentNode.height) {
|
||||
parentNode.height = borderY - parentNode.y;
|
||||
}
|
||||
// TODO: trigger cascading updates
|
||||
}
|
||||
|
||||
return { x: result.x, y: result.y };
|
||||
}
|
||||
|
||||
/** Calculate insert position for a new {@link IBlock} */
|
||||
calculateNewBlockPosition(data: ICreateBlockDTO): Rectangle2D {
|
||||
newBlockPosition(data: ICreateBlockDTO): Rectangle2D {
|
||||
const block_nodes = data.children_blocks
|
||||
.map(id => this.layout.blocks.find(block => block.id === id))
|
||||
.filter(node => !!node);
|
||||
const operation_nodes = data.children_operations
|
||||
.map(id => this.layout.operations.find(operation => operation.id === id))
|
||||
.filter(node => !!node);
|
||||
const parentNode = this.layout.blocks.find(pos => pos.id === data.item_data.parent);
|
||||
|
||||
let result: Rectangle2D = { x: data.position_x, y: data.position_y, width: data.width, height: data.height };
|
||||
|
||||
if (block_nodes.length !== 0 || operation_nodes.length !== 0) {
|
||||
result = calculatePositionFromChildren(
|
||||
{ x: data.position_x, y: data.position_y, width: data.width, height: data.height },
|
||||
operation_nodes,
|
||||
block_nodes
|
||||
);
|
||||
} else if (parentNode) {
|
||||
result = {
|
||||
x: parentNode.x + MIN_DISTANCE,
|
||||
y: parentNode.y + MIN_DISTANCE,
|
||||
width: data.width,
|
||||
height: data.height
|
||||
};
|
||||
} else {
|
||||
result = this.calculatePositionForFreeBlock(result);
|
||||
}
|
||||
|
||||
if (block_nodes.length === 0 && operation_nodes.length === 0) {
|
||||
return { x: data.position_x, y: data.position_y, width: data.width, height: data.height };
|
||||
if (parentNode) {
|
||||
const siblings = this.oss.blocks.filter(block => block.parent === parentNode.id).map(block => block.id);
|
||||
if (siblings.length > 0) {
|
||||
result = preventOverlap(
|
||||
result,
|
||||
this.layout.blocks.filter(block => siblings.includes(block.id))
|
||||
);
|
||||
}
|
||||
} else {
|
||||
const rootBlocks = this.oss.blocks.filter(block => block.parent === null).map(block => block.id);
|
||||
if (rootBlocks.length > 0) {
|
||||
result = preventOverlap(
|
||||
result,
|
||||
this.layout.blocks.filter(block => rootBlocks.includes(block.id))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let left = undefined;
|
||||
let top = undefined;
|
||||
let right = undefined;
|
||||
let bottom = undefined;
|
||||
|
||||
for (const block of block_nodes) {
|
||||
left = !left ? block.x - MIN_DISTANCE : Math.min(left, block.x - MIN_DISTANCE);
|
||||
top = !top ? block.y - MIN_DISTANCE : Math.min(top, block.y - MIN_DISTANCE);
|
||||
right = !right
|
||||
? Math.max(left + data.width, block.x + block.width + MIN_DISTANCE)
|
||||
: Math.max(right, block.x + block.width + MIN_DISTANCE);
|
||||
bottom = !bottom
|
||||
? Math.max(top + data.height, block.y + block.height + MIN_DISTANCE)
|
||||
: Math.max(bottom, block.y + block.height + MIN_DISTANCE);
|
||||
if (parentNode) {
|
||||
const borderX = result.x + result.width + MIN_DISTANCE;
|
||||
const borderY = result.y + result.height + MIN_DISTANCE;
|
||||
if (borderX > parentNode.x + parentNode.width) {
|
||||
parentNode.width = borderX - parentNode.x;
|
||||
}
|
||||
if (borderY > parentNode.y + parentNode.height) {
|
||||
parentNode.height = borderY - parentNode.y;
|
||||
}
|
||||
// TODO: trigger cascading updates
|
||||
}
|
||||
|
||||
for (const operation of operation_nodes) {
|
||||
left = !left ? operation.x - MIN_DISTANCE : Math.min(left, operation.x - MIN_DISTANCE);
|
||||
top = !top ? operation.y - MIN_DISTANCE : Math.min(top, operation.y - MIN_DISTANCE);
|
||||
right = !right
|
||||
? Math.max(left + data.width, operation.x + OPERATION_NODE_WIDTH + MIN_DISTANCE)
|
||||
: Math.max(right, operation.x + OPERATION_NODE_WIDTH + MIN_DISTANCE);
|
||||
bottom = !bottom
|
||||
? Math.max(top + data.height, operation.y + OPERATION_NODE_HEIGHT + MIN_DISTANCE)
|
||||
: Math.max(bottom, operation.y + OPERATION_NODE_HEIGHT + MIN_DISTANCE);
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Update layout when parent changes */
|
||||
onOperationChangeParent(targetID: number, newParent: number | null) {
|
||||
console.error('not implemented', targetID, newParent);
|
||||
}
|
||||
|
||||
/** Update layout when parent changes */
|
||||
onBlockChangeParent(targetID: number, newParent: number | null) {
|
||||
console.error('not implemented', targetID, newParent);
|
||||
}
|
||||
|
||||
private calculatePositionForFreeOperation(initial: Position2D): Position2D {
|
||||
const operations = this.layout.operations;
|
||||
if (operations.length === 0) {
|
||||
return initial;
|
||||
}
|
||||
|
||||
const freeInputs = this.oss.operations
|
||||
.filter(operation => operation.arguments.length === 0 && operation.parent === null)
|
||||
.map(operation => operation.id);
|
||||
let inputsPositions = operations.filter(pos => freeInputs.includes(pos.id));
|
||||
if (inputsPositions.length === 0) {
|
||||
inputsPositions = operations;
|
||||
}
|
||||
const maxX = Math.max(...inputsPositions.map(node => node.x));
|
||||
const minY = Math.min(...inputsPositions.map(node => node.y));
|
||||
return {
|
||||
x: left ?? data.position_x,
|
||||
y: top ?? data.position_y,
|
||||
width: right && left ? right - left : data.width,
|
||||
height: bottom && top ? bottom - top : data.height
|
||||
x: maxX + OPERATION_NODE_WIDTH + MIN_DISTANCE + GRID_SIZE,
|
||||
y: minY
|
||||
};
|
||||
}
|
||||
|
||||
private calculatePositionForFreeBlock(initial: Rectangle2D): Rectangle2D {
|
||||
const rootBlocks = this.oss.blocks.filter(block => block.parent === null).map(block => block.id);
|
||||
const blocksPositions = this.layout.blocks.filter(pos => rootBlocks.includes(pos.id));
|
||||
if (blocksPositions.length === 0) {
|
||||
return initial;
|
||||
}
|
||||
const maxX = Math.max(...blocksPositions.map(node => node.x + node.width));
|
||||
const minY = Math.min(...blocksPositions.map(node => node.y));
|
||||
return { ...initial, x: maxX + MIN_DISTANCE, y: minY };
|
||||
}
|
||||
}
|
||||
|
||||
// ======= Internals =======
|
||||
function rectanglesOverlap(a: Rectangle2D, b: Rectangle2D): boolean {
|
||||
return !(
|
||||
a.x + a.width + MIN_DISTANCE <= b.x ||
|
||||
b.x + b.width + MIN_DISTANCE <= a.x ||
|
||||
a.y + a.height + MIN_DISTANCE <= b.y ||
|
||||
b.y + b.height + MIN_DISTANCE <= a.y
|
||||
);
|
||||
}
|
||||
|
||||
function getOverlapAmount(a: Rectangle2D, b: Rectangle2D): Position2D {
|
||||
const xOverlap = Math.max(0, Math.min(a.x + a.width, b.x + b.width) - Math.max(a.x, b.x));
|
||||
const yOverlap = Math.max(0, Math.min(a.y + a.height, b.y + b.height) - Math.max(a.y, b.y));
|
||||
return { x: xOverlap, y: yOverlap };
|
||||
}
|
||||
|
||||
function preventOverlap(target: Rectangle2D, fixedRectangles: Rectangle2D[]): Rectangle2D {
|
||||
let hasOverlap: boolean;
|
||||
do {
|
||||
hasOverlap = false;
|
||||
for (const fixed of fixedRectangles) {
|
||||
if (rectanglesOverlap(target, fixed)) {
|
||||
hasOverlap = true;
|
||||
const overlap = getOverlapAmount(target, fixed);
|
||||
if (overlap.x >= overlap.y) {
|
||||
target.x += overlap.x + MIN_DISTANCE;
|
||||
} else {
|
||||
target.y += overlap.y + MIN_DISTANCE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (hasOverlap);
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
function calculatePositionFromArgs(args: number[], operations: IOperationPosition[]): Position2D {
|
||||
const argNodes = operations.filter(pos => args.includes(pos.id));
|
||||
const maxY = Math.max(...argNodes.map(node => node.y));
|
||||
const minX = Math.min(...argNodes.map(node => node.x));
|
||||
const maxX = Math.max(...argNodes.map(node => node.x));
|
||||
return {
|
||||
x: Math.ceil((maxX + minX) / 2 / GRID_SIZE) * GRID_SIZE,
|
||||
y: maxY + 2 * OPERATION_NODE_HEIGHT + MIN_DISTANCE
|
||||
};
|
||||
}
|
||||
|
||||
function calculatePositionFromChildren(
|
||||
initial: Rectangle2D,
|
||||
operations: IOperationPosition[],
|
||||
blocks: IBlockPosition[]
|
||||
): Rectangle2D {
|
||||
let left = undefined;
|
||||
let top = undefined;
|
||||
let right = undefined;
|
||||
let bottom = undefined;
|
||||
|
||||
for (const block of blocks) {
|
||||
left = left === undefined ? block.x - MIN_DISTANCE : Math.min(left, block.x - MIN_DISTANCE);
|
||||
top = top === undefined ? block.y - MIN_DISTANCE : Math.min(top, block.y - MIN_DISTANCE);
|
||||
right =
|
||||
right === undefined
|
||||
? Math.max(left + initial.width, block.x + block.width + MIN_DISTANCE)
|
||||
: Math.max(right, block.x + block.width + MIN_DISTANCE);
|
||||
bottom = !bottom
|
||||
? Math.max(top + initial.height, block.y + block.height + MIN_DISTANCE)
|
||||
: Math.max(bottom, block.y + block.height + MIN_DISTANCE);
|
||||
}
|
||||
|
||||
for (const operation of operations) {
|
||||
left = left === undefined ? operation.x - MIN_DISTANCE : Math.min(left, operation.x - MIN_DISTANCE);
|
||||
top = top === undefined ? operation.y - MIN_DISTANCE : Math.min(top, operation.y - MIN_DISTANCE);
|
||||
right =
|
||||
right === undefined
|
||||
? Math.max(left + initial.width, operation.x + OPERATION_NODE_WIDTH + MIN_DISTANCE)
|
||||
: Math.max(right, operation.x + OPERATION_NODE_WIDTH + MIN_DISTANCE);
|
||||
bottom = !bottom
|
||||
? Math.max(top + initial.height, operation.y + OPERATION_NODE_HEIGHT + MIN_DISTANCE)
|
||||
: Math.max(bottom, operation.y + OPERATION_NODE_HEIGHT + MIN_DISTANCE);
|
||||
}
|
||||
|
||||
return {
|
||||
x: left ?? initial.x,
|
||||
y: top ?? initial.y,
|
||||
width: right !== undefined && left !== undefined ? right - left : initial.width,
|
||||
height: bottom !== undefined && top !== undefined ? bottom - top : initial.height
|
||||
};
|
||||
}
|
||||
|
|
|
@ -11,8 +11,17 @@ import {
|
|||
type IOperationSchemaDTO
|
||||
} from '../backend/types';
|
||||
|
||||
/** Represents OSS node type. */
|
||||
export const NodeType = {
|
||||
OPERATION: 1,
|
||||
BLOCK: 2
|
||||
} as const;
|
||||
export type NodeType = (typeof NodeType)[keyof typeof NodeType];
|
||||
|
||||
/** Represents Operation. */
|
||||
export interface IOperation extends IOperationDTO {
|
||||
nodeID: string;
|
||||
nodeType: typeof NodeType.OPERATION;
|
||||
x: number;
|
||||
y: number;
|
||||
is_owned: boolean;
|
||||
|
@ -23,12 +32,17 @@ export interface IOperation extends IOperationDTO {
|
|||
|
||||
/** Represents Block. */
|
||||
export interface IBlock extends IBlockDTO {
|
||||
nodeID: string;
|
||||
nodeType: typeof NodeType.BLOCK;
|
||||
x: number;
|
||||
y: number;
|
||||
width: number;
|
||||
height: number;
|
||||
}
|
||||
|
||||
/** Represents item of OperationSchema. */
|
||||
export type IOssItem = IOperation | IBlock;
|
||||
|
||||
/** Represents {@link IOperationSchema} statistics. */
|
||||
export interface IOperationSchemaStats {
|
||||
count_all: number;
|
||||
|
@ -45,16 +59,14 @@ export interface IOperationSchema extends IOperationSchemaDTO {
|
|||
blocks: IBlock[];
|
||||
|
||||
graph: Graph;
|
||||
hierarchy: Graph;
|
||||
hierarchy: Graph<string>;
|
||||
schemas: number[];
|
||||
stats: IOperationSchemaStats;
|
||||
operationByID: Map<number, IOperation>;
|
||||
blockByID: Map<number, IBlock>;
|
||||
itemByNodeID: Map<string, IOssItem>;
|
||||
}
|
||||
|
||||
/** Represents item of OperationSchema. */
|
||||
export type IOssItem = IOperation | IBlock;
|
||||
|
||||
/** Represents substitution error description. */
|
||||
export interface ISubstitutionErrorDescription {
|
||||
errorType: SubstitutionErrorType;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
'use no memo'; // TODO: remove when react hook forms are compliant with react compiler
|
||||
'use client';
|
||||
|
||||
import { useRef } from 'react';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { useForm, useWatch } from 'react-hook-form';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
|
||||
|
@ -47,10 +47,12 @@ export function FormOSS() {
|
|||
const readOnly = useWatch({ control, name: 'read_only' });
|
||||
|
||||
const prevDirty = useRef(isDirty);
|
||||
if (prevDirty.current !== isDirty) {
|
||||
prevDirty.current = isDirty;
|
||||
setIsModified(isDirty);
|
||||
}
|
||||
useEffect(() => {
|
||||
if (prevDirty.current !== isDirty) {
|
||||
prevDirty.current = isDirty;
|
||||
setIsModified(isDirty);
|
||||
}
|
||||
}, [isDirty, setIsModified]);
|
||||
|
||||
function onSubmit(data: IUpdateLibraryItemDTO) {
|
||||
return updateOss(data).then(() => reset({ ...data }));
|
||||
|
|
|
@ -4,8 +4,7 @@ import { useRef } from 'react';
|
|||
|
||||
import { Dropdown } from '@/components/dropdown';
|
||||
|
||||
import { type IBlock, type IOperation, type IOssItem } from '../../../../models/oss';
|
||||
import { isOperation } from '../../../../models/oss-api';
|
||||
import { type IOssItem, NodeType } from '../../../../models/oss';
|
||||
|
||||
import { MenuBlock } from './menu-block';
|
||||
import { MenuOperation } from './menu-operation';
|
||||
|
@ -27,7 +26,6 @@ interface ContextMenuProps extends ContextMenuData {
|
|||
|
||||
export function ContextMenu({ isOpen, item, cursorX, cursorY, onHide }: ContextMenuProps) {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const isOperationNode = isOperation(item);
|
||||
|
||||
function handleBlur(event: React.FocusEvent<HTMLDivElement>) {
|
||||
if (!ref.current?.contains(event.relatedTarget as Node)) {
|
||||
|
@ -39,21 +37,20 @@ export function ContextMenu({ isOpen, item, cursorX, cursorY, onHide }: ContextM
|
|||
<div
|
||||
ref={ref}
|
||||
onBlur={handleBlur}
|
||||
className='relative'
|
||||
style={{ top: `calc(${cursorY}px - 2.5rem)`, left: cursorX }}
|
||||
className='fixed z-tooltip'
|
||||
style={{ top: `calc(${cursorY}px + 0.5rem)`, left: cursorX }}
|
||||
>
|
||||
<Dropdown
|
||||
className='z-navigation!'
|
||||
isOpen={isOpen}
|
||||
stretchLeft={cursorX >= window.innerWidth - MENU_WIDTH}
|
||||
stretchTop={cursorY >= window.innerHeight - MENU_HEIGHT}
|
||||
margin={cursorY >= window.innerHeight - MENU_HEIGHT ? 'mb-3' : 'mt-3'}
|
||||
>
|
||||
{!!item ? (
|
||||
isOperationNode ? (
|
||||
<MenuOperation operation={item as IOperation} onHide={onHide} />
|
||||
item.nodeType === NodeType.OPERATION ? (
|
||||
<MenuOperation operation={item} onHide={onHide} />
|
||||
) : (
|
||||
<MenuBlock block={item as IBlock} onHide={onHide} />
|
||||
<MenuBlock block={item} onHide={onHide} />
|
||||
)
|
||||
) : null}
|
||||
</Dropdown>
|
||||
|
|
|
@ -16,15 +16,15 @@ export const BLOCK_NODE_MIN_WIDTH = 160;
|
|||
export const BLOCK_NODE_MIN_HEIGHT = 100;
|
||||
|
||||
export function BlockNode(node: BlockInternalNode) {
|
||||
const { selected, schema } = useOssEdit();
|
||||
const { selectedItems, schema } = useOssEdit();
|
||||
const dropTarget = useDraggingStore(state => state.dropTarget);
|
||||
const isDragging = useDraggingStore(state => state.isDragging);
|
||||
const showCoordinates = useOSSGraphStore(state => state.showCoordinates);
|
||||
const setHover = useOperationTooltipStore(state => state.setHoverItem);
|
||||
|
||||
const focus = selected.length === 1 ? selected[0] : null;
|
||||
const isParent = (!!focus && schema.hierarchy.at(focus)?.inputs.includes(-node.data.block.id)) ?? false;
|
||||
const isChild = (!!focus && schema.hierarchy.at(focus)?.outputs.includes(-node.data.block.id)) ?? false;
|
||||
const focus = selectedItems.length === 1 ? selectedItems[0] : null;
|
||||
const isParent = (!!focus && schema.hierarchy.at(focus.nodeID)?.inputs.includes(node.data.block.nodeID)) ?? false;
|
||||
const isChild = (!!focus && schema.hierarchy.at(focus.nodeID)?.outputs.includes(node.data.block.nodeID)) ?? false;
|
||||
return (
|
||||
<>
|
||||
<NodeResizeControl minWidth={BLOCK_NODE_MIN_WIDTH} minHeight={BLOCK_NODE_MIN_HEIGHT}>
|
||||
|
|
|
@ -21,9 +21,9 @@ interface NodeCoreProps {
|
|||
}
|
||||
|
||||
export function NodeCore({ node }: NodeCoreProps) {
|
||||
const { selected, schema } = useOssEdit();
|
||||
const focus = selected.length === 1 ? selected[0] : null;
|
||||
const isChild = (!!focus && schema.hierarchy.at(focus)?.outputs.includes(node.data.operation.id)) ?? false;
|
||||
const { selectedItems, schema } = useOssEdit();
|
||||
const focus = selectedItems.length === 1 ? selectedItems[0] : null;
|
||||
const isChild = (!!focus && schema.hierarchy.at(focus.nodeID)?.outputs.includes(node.data.operation.nodeID)) ?? false;
|
||||
|
||||
const setHover = useOperationTooltipStore(state => state.setHoverItem);
|
||||
const showCoordinates = useOSSGraphStore(state => state.showCoordinates);
|
||||
|
|
|
@ -3,12 +3,12 @@
|
|||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { type Edge, type Node, useEdgesState, useNodesState, useOnSelectionChange, useReactFlow } from 'reactflow';
|
||||
|
||||
import { type IOperationSchema } from '@/features/oss/models/oss';
|
||||
import { type Position2D } from '@/features/oss/models/oss-layout';
|
||||
import { useOSSGraphStore } from '@/features/oss/stores/oss-graph';
|
||||
|
||||
import { PARAMETER } from '@/utils/constants';
|
||||
|
||||
import { type IOperationSchema, NodeType } from '../../../models/oss';
|
||||
import { constructNodeID } from '../../../models/oss-api';
|
||||
import { type Position2D } from '../../../models/oss-layout';
|
||||
import { useOSSGraphStore } from '../../../stores/oss-graph';
|
||||
import { useOssEdit } from '../oss-edit-context';
|
||||
|
||||
import { flowOptions } from './oss-flow';
|
||||
|
@ -29,10 +29,10 @@ export const OssFlowState = ({ children }: React.PropsWithChildren) => {
|
|||
const [edges, setEdges, onEdgesChange] = useEdgesState<Edge>([]);
|
||||
|
||||
function onSelectionChange({ nodes }: { nodes: Node[] }) {
|
||||
const ids = nodes.map(node => Number(node.id));
|
||||
const ids = nodes.map(node => node.id);
|
||||
setSelected(prev => [
|
||||
...prev.filter(nodeID => ids.includes(nodeID)),
|
||||
...ids.filter(nodeID => !prev.includes(Number(nodeID)))
|
||||
...ids.filter(nodeID => !prev.includes(nodeID))
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -41,46 +41,45 @@ export const OssFlowState = ({ children }: React.PropsWithChildren) => {
|
|||
});
|
||||
|
||||
const resetGraph = useCallback(() => {
|
||||
const newNodes: Node[] = [
|
||||
...schema.hierarchy
|
||||
.topologicalOrder()
|
||||
.filter(id => id < 0)
|
||||
.map(id => {
|
||||
const block = schema.blockByID.get(-id)!;
|
||||
return {
|
||||
id: String(id),
|
||||
type: 'block',
|
||||
data: { label: block.title, block: block },
|
||||
position: computeRelativePosition(schema, { x: block.x, y: block.y }, block.parent),
|
||||
style: {
|
||||
width: block.width,
|
||||
height: block.height
|
||||
},
|
||||
parentId: block.parent ? `-${block.parent}` : undefined,
|
||||
zIndex: Z_BLOCK
|
||||
};
|
||||
}),
|
||||
...schema.operations.map(operation => ({
|
||||
id: String(operation.id),
|
||||
type: operation.operation_type.toString(),
|
||||
data: { label: operation.alias, operation: operation },
|
||||
position: computeRelativePosition(schema, { x: operation.x, y: operation.y }, operation.parent),
|
||||
parentId: operation.parent ? `-${operation.parent}` : undefined,
|
||||
zIndex: Z_SCHEMA
|
||||
}))
|
||||
];
|
||||
const newNodes: Node[] = schema.hierarchy.topologicalOrder().map(nodeID => {
|
||||
const item = schema.itemByNodeID.get(nodeID)!;
|
||||
if (item.nodeType === NodeType.BLOCK) {
|
||||
return {
|
||||
id: nodeID,
|
||||
type: 'block',
|
||||
data: { label: item.title, block: item },
|
||||
position: computeRelativePosition(schema, { x: item.x, y: item.y }, item.parent),
|
||||
style: {
|
||||
width: item.width,
|
||||
height: item.height
|
||||
},
|
||||
parentId: item.parent ? constructNodeID(NodeType.BLOCK, item.parent) : undefined,
|
||||
zIndex: Z_BLOCK
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
id: item.nodeID,
|
||||
type: item.operation_type.toString(),
|
||||
data: { label: item.alias, operation: item },
|
||||
position: computeRelativePosition(schema, { x: item.x, y: item.y }, item.parent),
|
||||
parentId: item.parent ? constructNodeID(NodeType.BLOCK, item.parent) : undefined,
|
||||
zIndex: Z_SCHEMA
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
const newEdges: Edge[] = schema.arguments.map((argument, index) => ({
|
||||
id: String(index),
|
||||
source: String(argument.argument),
|
||||
target: String(argument.operation),
|
||||
type: edgeStraight ? 'straight' : 'simplebezier',
|
||||
animated: edgeAnimate,
|
||||
targetHandle:
|
||||
schema.operationByID.get(argument.argument)!.x > schema.operationByID.get(argument.operation)!.x
|
||||
? 'right'
|
||||
: 'left'
|
||||
}));
|
||||
const newEdges: Edge[] = schema.arguments.map((argument, index) => {
|
||||
const source = schema.operationByID.get(argument.argument)!;
|
||||
const target = schema.operationByID.get(argument.operation)!;
|
||||
return {
|
||||
id: String(index),
|
||||
source: source.nodeID,
|
||||
target: target.nodeID,
|
||||
type: edgeStraight ? 'straight' : 'simplebezier',
|
||||
animated: edgeAnimate,
|
||||
targetHandle: source.x > target.x ? 'right' : 'left'
|
||||
};
|
||||
});
|
||||
|
||||
setNodes(newNodes);
|
||||
setEdges(newEdges);
|
||||
|
|
|
@ -12,6 +12,7 @@ import { promptText } from '@/utils/labels';
|
|||
import { useDeleteBlock } from '../../../backend/use-delete-block';
|
||||
import { useMutatingOss } from '../../../backend/use-mutating-oss';
|
||||
import { useUpdateLayout } from '../../../backend/use-update-layout';
|
||||
import { type IOssItem, NodeType } from '../../../models/oss';
|
||||
import { type OssNode, type Position2D } from '../../../models/oss-layout';
|
||||
import { GRID_SIZE, LayoutManager } from '../../../models/oss-layout-api';
|
||||
import { useOSSGraphStore } from '../../../stores/oss-graph';
|
||||
|
@ -41,7 +42,7 @@ export const flowOptions = {
|
|||
|
||||
export function OssFlow() {
|
||||
const mainHeight = useMainHeight();
|
||||
const { navigateOperationSchema, schema, selected, isMutable, canDeleteOperation } = useOssEdit();
|
||||
const { navigateOperationSchema, schema, selected, selectedItems, isMutable, canDeleteOperation } = useOssEdit();
|
||||
const { screenToFlowPosition } = useReactFlow();
|
||||
const { containMovement, nodes, onNodesChange, edges, onEdgesChange, resetGraph, resetView } = useOssFlow();
|
||||
const store = useStoreApi();
|
||||
|
@ -76,20 +77,21 @@ export function OssFlow() {
|
|||
manager: new LayoutManager(schema, getLayout()),
|
||||
defaultX: targetPosition.x,
|
||||
defaultY: targetPosition.y,
|
||||
initialInputs: selected.filter(id => id > 0),
|
||||
initialParent: extractSingleBlock(selected),
|
||||
initialInputs: selectedItems.filter(item => item?.nodeType === NodeType.OPERATION).map(item => item.id),
|
||||
initialParent: extractBlockParent(selectedItems),
|
||||
onCreate: resetView
|
||||
});
|
||||
}
|
||||
|
||||
function handleCreateBlock() {
|
||||
const targetPosition = screenToFlowPosition({ x: window.innerWidth / 2, y: window.innerHeight / 2 });
|
||||
const parent = extractSingleBlock(selected);
|
||||
const parent = extractBlockParent(selectedItems);
|
||||
showCreateBlock({
|
||||
manager: new LayoutManager(schema, getLayout()),
|
||||
defaultX: targetPosition.x,
|
||||
defaultY: targetPosition.y,
|
||||
initialChildren: parent !== null ? [] : selected,
|
||||
initialChildren:
|
||||
parent !== null && selectedItems.length === 1 && parent === selectedItems[0].id ? [] : selectedItems,
|
||||
initialParent: parent,
|
||||
onCreate: resetView
|
||||
});
|
||||
|
@ -99,25 +101,24 @@ export function OssFlow() {
|
|||
if (selected.length !== 1) {
|
||||
return;
|
||||
}
|
||||
if (selected[0] > 0) {
|
||||
const operation = schema.operationByID.get(selected[0]);
|
||||
if (!operation || !canDeleteOperation(operation)) {
|
||||
const item = schema.itemByNodeID.get(selected[0]);
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
if (item.nodeType === NodeType.OPERATION) {
|
||||
if (!canDeleteOperation(item)) {
|
||||
return;
|
||||
}
|
||||
showDeleteOperation({
|
||||
oss: schema,
|
||||
target: operation,
|
||||
target: item,
|
||||
layout: getLayout()
|
||||
});
|
||||
} else {
|
||||
const block = schema.blockByID.get(-selected[0]);
|
||||
if (!block) {
|
||||
return;
|
||||
}
|
||||
if (!window.confirm(promptText.deleteBlock)) {
|
||||
return;
|
||||
}
|
||||
void deleteBlock({ itemID: schema.id, data: { target: block.id, layout: getLayout() } });
|
||||
void deleteBlock({ itemID: schema.id, data: { target: item.id, layout: getLayout() } });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -219,7 +220,10 @@ export function OssFlow() {
|
|||
}
|
||||
|
||||
// -------- Internals --------
|
||||
function extractSingleBlock(selected: number[]): number | null {
|
||||
const blocks = selected.filter(id => id < 0);
|
||||
return blocks.length === 1 ? -blocks[0] : null;
|
||||
function extractBlockParent(selectedItems: IOssItem[]): number | null {
|
||||
if (selectedItems.length === 1 && selectedItems[0].nodeType === NodeType.BLOCK) {
|
||||
return selectedItems[0].id;
|
||||
}
|
||||
const parents = selectedItems.map(item => item.parent).filter(id => id !== null);
|
||||
return parents.length === 0 ? null : parents[0];
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ import { OperationType } from '../../../backend/types';
|
|||
import { useExecuteOperation } from '../../../backend/use-execute-operation';
|
||||
import { useMutatingOss } from '../../../backend/use-mutating-oss';
|
||||
import { useUpdateLayout } from '../../../backend/use-update-layout';
|
||||
import { NodeType } from '../../../models/oss';
|
||||
import { LayoutManager } from '../../../models/oss-layout-api';
|
||||
import { useOssEdit } from '../oss-edit-context';
|
||||
|
||||
|
@ -48,11 +49,13 @@ export function ToolbarOssGraph({
|
|||
className,
|
||||
...restProps
|
||||
}: ToolbarOssGraphProps) {
|
||||
const { schema, selected, isMutable, canDeleteOperation: canDelete } = useOssEdit();
|
||||
const { schema, selectedItems, isMutable, canDeleteOperation: canDelete } = useOssEdit();
|
||||
const isProcessing = useMutatingOss();
|
||||
const { resetView } = useOssFlow();
|
||||
const selectedOperation = selected.length !== 1 ? null : schema.operationByID.get(selected[0]) ?? null;
|
||||
const selectedBlock = selected.length !== 1 ? null : schema.blockByID.get(-selected[0]) ?? null;
|
||||
const selectedOperation =
|
||||
selectedItems.length === 1 && selectedItems[0].nodeType === NodeType.OPERATION ? selectedItems[0] : null;
|
||||
const selectedBlock =
|
||||
selectedItems.length === 1 && selectedItems[0].nodeType === NodeType.BLOCK ? selectedItems[0] : null;
|
||||
const getLayout = useGetLayout();
|
||||
|
||||
const { updateLayout } = useUpdateLayout();
|
||||
|
@ -145,7 +148,9 @@ export function ToolbarOssGraph({
|
|||
title='Исправить позиции узлов'
|
||||
icon={<IconFixLayout size='1.25rem' className='icon-primary' />}
|
||||
onClick={handleFixLayout}
|
||||
disabled={selected.length > 1 || selected[0] > 0}
|
||||
disabled={
|
||||
selectedItems.length > 1 || (selectedItems.length > 0 && selectedItems[0].nodeType === NodeType.OPERATION)
|
||||
}
|
||||
/>
|
||||
<MiniButton
|
||||
title='Настройки отображения'
|
||||
|
@ -181,14 +186,14 @@ export function ToolbarOssGraph({
|
|||
title='Активировать операцию'
|
||||
icon={<IconExecute size='1.25rem' className='icon-green' />}
|
||||
onClick={handleOperationExecute}
|
||||
disabled={isProcessing || selected.length !== 1 || !readyForSynthesis}
|
||||
disabled={isProcessing || selectedItems.length !== 1 || !readyForSynthesis}
|
||||
/>
|
||||
<MiniButton
|
||||
titleHtml={prepareTooltip('Редактировать выбранную', 'Двойной клик')}
|
||||
aria-label='Редактировать выбранную'
|
||||
icon={<IconEdit2 size='1.25rem' className='icon-primary' />}
|
||||
onClick={handleEditItem}
|
||||
disabled={selected.length !== 1 || isProcessing}
|
||||
disabled={selectedItems.length !== 1 || isProcessing}
|
||||
/>
|
||||
<MiniButton
|
||||
titleHtml={prepareTooltip('Удалить выбранную', 'Delete')}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { type Node } from 'reactflow';
|
||||
|
||||
import { useMoveItems } from '@/features/oss/backend/use-move-items';
|
||||
|
||||
import { useThrottleCallback } from '@/hooks/use-throttle-callback';
|
||||
import { useDraggingStore } from '@/stores/dragging';
|
||||
|
||||
import { useMoveItems } from '../../../backend/use-move-items';
|
||||
import { NodeType } from '../../../models/oss';
|
||||
import { useOssEdit } from '../oss-edit-context';
|
||||
|
||||
import { useOssFlow } from './oss-flow-context';
|
||||
|
@ -21,6 +21,7 @@ interface DraggingProps {
|
|||
export function useDragging({ hideContextMenu }: DraggingProps) {
|
||||
const { setContainMovement, containMovement, setNodes } = useOssFlow();
|
||||
const setIsDragging = useDraggingStore(state => state.setIsDragging);
|
||||
const isDragging = useDraggingStore(state => state.isDragging);
|
||||
const getLayout = useGetLayout();
|
||||
const { selected, schema } = useOssEdit();
|
||||
const dropTarget = useDropTarget();
|
||||
|
@ -43,7 +44,7 @@ export function useDragging({ hideContextMenu }: DraggingProps) {
|
|||
function handleDragStart(event: React.MouseEvent, target: Node) {
|
||||
if (event.shiftKey) {
|
||||
setContainMovement(true);
|
||||
applyContainMovement([target.id, ...selected.map(id => String(id))], true);
|
||||
applyContainMovement([target.id, ...selected], true);
|
||||
} else {
|
||||
setContainMovement(false);
|
||||
dropTarget.update(event);
|
||||
|
@ -61,41 +62,37 @@ export function useDragging({ hideContextMenu }: DraggingProps) {
|
|||
|
||||
function handleDragStop(event: React.MouseEvent, target: Node) {
|
||||
if (containMovement) {
|
||||
applyContainMovement([target.id, ...selected.map(id => String(id))], false);
|
||||
applyContainMovement([target.id, ...selected], false);
|
||||
} else {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
const new_parent = dropTarget.evaluate(event);
|
||||
const allSelected = [...selected.filter(id => id != Number(target.id)), Number(target.id)];
|
||||
const operations = allSelected
|
||||
.filter(id => id > 0)
|
||||
.map(id => schema.operationByID.get(id))
|
||||
.filter(operation => !!operation);
|
||||
const blocks = allSelected
|
||||
.filter(id => id < 0)
|
||||
.map(id => schema.blockByID.get(-id))
|
||||
.filter(operation => !!operation);
|
||||
const parents = new Set(
|
||||
[...blocks.map(block => block.parent), ...operations.map(operation => operation.parent)].filter(id => !!id)
|
||||
);
|
||||
if (
|
||||
(parents.size !== 1 || parents.values().next().value !== new_parent) &&
|
||||
(parents.size !== 0 || new_parent !== null)
|
||||
) {
|
||||
void moveItems({
|
||||
itemID: schema.id,
|
||||
data: {
|
||||
layout: getLayout(),
|
||||
operations: operations.map(operation => operation.id),
|
||||
blocks: blocks.map(block => block.id),
|
||||
destination: new_parent
|
||||
}
|
||||
});
|
||||
if (isDragging) {
|
||||
setIsDragging(false);
|
||||
const new_parent = dropTarget.evaluate(event);
|
||||
const allSelected = [...selected.filter(id => id != target.id), target.id].map(id =>
|
||||
schema.itemByNodeID.get(id)
|
||||
);
|
||||
const parents = new Set(allSelected.map(item => item?.parent).filter(id => !!id));
|
||||
const operations = allSelected.filter(item => item?.nodeType === NodeType.OPERATION);
|
||||
const blocks = allSelected.filter(item => item?.nodeType === NodeType.BLOCK);
|
||||
if (
|
||||
(parents.size !== 1 || parents.values().next().value !== new_parent) &&
|
||||
(parents.size !== 0 || new_parent !== null)
|
||||
) {
|
||||
void moveItems({
|
||||
itemID: schema.id,
|
||||
data: {
|
||||
layout: getLayout(),
|
||||
operations: operations.map(operation => operation.id),
|
||||
blocks: blocks.map(block => block.id),
|
||||
destination: new_parent
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setIsDragging(false);
|
||||
setContainMovement(false);
|
||||
dropTarget.reset();
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import { useReactFlow } from 'reactflow';
|
||||
|
||||
import { NodeType } from '@/features/oss/models/oss';
|
||||
|
||||
import { useDraggingStore } from '@/stores/dragging';
|
||||
|
||||
import { useOssEdit } from '../oss-edit-context';
|
||||
|
@ -7,7 +9,7 @@ import { useOssEdit } from '../oss-edit-context';
|
|||
/** Hook to encapsulate drop target logic. */
|
||||
export function useDropTarget() {
|
||||
const { getIntersectingNodes, screenToFlowPosition } = useReactFlow();
|
||||
const { selected, schema } = useOssEdit();
|
||||
const { selectedItems, selected, schema } = useOssEdit();
|
||||
const dropTarget = useDraggingStore(state => state.dropTarget);
|
||||
const setDropTarget = useDraggingStore(state => state.setDropTarget);
|
||||
|
||||
|
@ -19,17 +21,16 @@ export function useDropTarget() {
|
|||
width: 1,
|
||||
height: 1
|
||||
})
|
||||
.map(node => Number(node.id))
|
||||
.filter(id => id < 0 && !selected.includes(id))
|
||||
.map(id => schema.blockByID.get(-id))
|
||||
.filter(block => !!block);
|
||||
.filter(node => !selected.includes(node.id))
|
||||
.map(node => schema.itemByNodeID.get(node.id))
|
||||
.filter(item => item?.nodeType === NodeType.BLOCK);
|
||||
|
||||
if (blocks.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const successors = schema.hierarchy.expandAllOutputs([...selected]).filter(id => id < 0);
|
||||
blocks = blocks.filter(block => !successors.includes(-block.id));
|
||||
const successors = schema.hierarchy.expandAllOutputs(selectedItems.map(item => item.nodeID));
|
||||
blocks = blocks.filter(block => !successors.includes(block.nodeID));
|
||||
if (blocks.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -18,13 +18,13 @@ export function useGetLayout() {
|
|||
operations: nodes
|
||||
.filter(node => node.type !== 'block')
|
||||
.map(node => ({
|
||||
id: Number(node.id),
|
||||
id: schema.itemByNodeID.get(node.id)!.id,
|
||||
...computeAbsolutePosition(node, schema, nodeById)
|
||||
})),
|
||||
blocks: nodes
|
||||
.filter(node => node.type === 'block')
|
||||
.map(node => ({
|
||||
id: -Number(node.id),
|
||||
id: schema.itemByNodeID.get(node.id)!.id,
|
||||
...computeAbsolutePosition(node, schema, nodeById),
|
||||
width: node.width ?? BLOCK_NODE_MIN_WIDTH,
|
||||
height: node.height ?? BLOCK_NODE_MIN_HEIGHT
|
||||
|
@ -35,7 +35,7 @@ export function useGetLayout() {
|
|||
|
||||
// ------- Internals -------
|
||||
function computeAbsolutePosition(target: Node, schema: IOperationSchema, nodeById: Map<string, Node>): Position2D {
|
||||
const nodes = schema.hierarchy.expandAllInputs([Number(target.id)]);
|
||||
const nodes = schema.hierarchy.expandAllInputs([target.id]);
|
||||
let x = target.position.x;
|
||||
let y = target.position.y;
|
||||
for (const nodeID of nodes) {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import { createContext, use } from 'react';
|
||||
|
||||
import { type IOperation, type IOperationSchema } from '../../models/oss';
|
||||
import { type IOperation, type IOperationSchema, type IOssItem } from '../../models/oss';
|
||||
|
||||
export const OssTabID = {
|
||||
CARD: 0,
|
||||
|
@ -12,7 +12,8 @@ export type OssTabID = (typeof OssTabID)[keyof typeof OssTabID];
|
|||
|
||||
interface IOssEditContext {
|
||||
schema: IOperationSchema;
|
||||
selected: number[];
|
||||
selected: string[];
|
||||
selectedItems: IOssItem[];
|
||||
|
||||
isOwned: boolean;
|
||||
isMutable: boolean;
|
||||
|
@ -22,7 +23,7 @@ interface IOssEditContext {
|
|||
|
||||
canDeleteOperation: (target: IOperation) => boolean;
|
||||
deleteSchema: () => void;
|
||||
setSelected: React.Dispatch<React.SetStateAction<number[]>>;
|
||||
setSelected: React.Dispatch<React.SetStateAction<string[]>>;
|
||||
}
|
||||
|
||||
export const OssEditContext = createContext<IOssEditContext | null>(null);
|
||||
|
|
|
@ -38,7 +38,8 @@ export const OssEditState = ({ itemID, children }: React.PropsWithChildren<OssEd
|
|||
const isMutable = role > UserRole.READER && !schema.read_only;
|
||||
const isEditor = !!user.id && schema.editors.includes(user.id);
|
||||
|
||||
const [selected, setSelected] = useState<number[]>([]);
|
||||
const [selected, setSelected] = useState<string[]>([]);
|
||||
const selectedItems = selected.map(id => schema.itemByNodeID.get(id)).filter(item => !!item);
|
||||
|
||||
const { deleteItem } = useDeleteItem();
|
||||
|
||||
|
@ -92,6 +93,7 @@ export const OssEditState = ({ itemID, children }: React.PropsWithChildren<OssEd
|
|||
value={{
|
||||
schema,
|
||||
selected,
|
||||
selectedItems,
|
||||
|
||||
isOwned,
|
||||
isMutable,
|
||||
|
|
|
@ -13,11 +13,11 @@ export const useCreateConstituenta = () => {
|
|||
const mutation = useMutation({
|
||||
mutationKey: [KEYS.global_mutation, rsformsApi.baseKey, 'create-constituenta'],
|
||||
mutationFn: rsformsApi.createConstituenta,
|
||||
onSuccess: data => {
|
||||
onSuccess: async data => {
|
||||
client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.schema.id }).queryKey, data.schema);
|
||||
updateTimestamp(data.schema.id);
|
||||
|
||||
return Promise.allSettled([
|
||||
await Promise.allSettled([
|
||||
client.invalidateQueries({ queryKey: [KEYS.oss] }),
|
||||
client.invalidateQueries({
|
||||
queryKey: [rsformsApi.baseKey],
|
||||
|
|
|
@ -13,11 +13,11 @@ export const useDeleteConstituents = () => {
|
|||
const mutation = useMutation({
|
||||
mutationKey: [KEYS.global_mutation, rsformsApi.baseKey, 'delete-constituents'],
|
||||
mutationFn: rsformsApi.deleteConstituents,
|
||||
onSuccess: data => {
|
||||
onSuccess: async data => {
|
||||
client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey, data);
|
||||
updateTimestamp(data.id);
|
||||
|
||||
return Promise.allSettled([
|
||||
await Promise.allSettled([
|
||||
client.invalidateQueries({ queryKey: [KEYS.oss] }),
|
||||
client.invalidateQueries({
|
||||
queryKey: [rsformsApi.baseKey],
|
||||
|
|
|
@ -13,11 +13,11 @@ export const useInlineSynthesis = () => {
|
|||
const mutation = useMutation({
|
||||
mutationKey: [KEYS.global_mutation, rsformsApi.baseKey, 'inline-synthesis'],
|
||||
mutationFn: rsformsApi.inlineSynthesis,
|
||||
onSuccess: data => {
|
||||
onSuccess: async data => {
|
||||
client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey, data);
|
||||
updateTimestamp(data.id);
|
||||
|
||||
return Promise.allSettled([
|
||||
await Promise.allSettled([
|
||||
client.invalidateQueries({ queryKey: [KEYS.oss] }),
|
||||
client.invalidateQueries({
|
||||
queryKey: [rsformsApi.baseKey],
|
||||
|
|
|
@ -12,11 +12,11 @@ export const useProduceStructure = () => {
|
|||
const mutation = useMutation({
|
||||
mutationKey: [KEYS.global_mutation, rsformsApi.baseKey, 'produce-structure'],
|
||||
mutationFn: rsformsApi.produceStructure,
|
||||
onSuccess: data => {
|
||||
onSuccess: async data => {
|
||||
client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.schema.id }).queryKey, data.schema);
|
||||
updateTimestamp(data.schema.id);
|
||||
|
||||
return Promise.allSettled([
|
||||
await Promise.allSettled([
|
||||
client.invalidateQueries({ queryKey: [KEYS.oss] }),
|
||||
client.invalidateQueries({
|
||||
queryKey: [rsformsApi.baseKey],
|
||||
|
|
|
@ -13,11 +13,11 @@ export const useRenameConstituenta = () => {
|
|||
const mutation = useMutation({
|
||||
mutationKey: [KEYS.global_mutation, rsformsApi.baseKey, 'rename-constituenta'],
|
||||
mutationFn: rsformsApi.renameConstituenta,
|
||||
onSuccess: data => {
|
||||
onSuccess: async data => {
|
||||
client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.schema.id }).queryKey, data.schema);
|
||||
updateTimestamp(data.schema.id);
|
||||
|
||||
return Promise.allSettled([
|
||||
await Promise.allSettled([
|
||||
client.invalidateQueries({ queryKey: [KEYS.oss] }),
|
||||
client.invalidateQueries({
|
||||
queryKey: [rsformsApi.baseKey],
|
||||
|
|
|
@ -12,11 +12,11 @@ export const useResetAliases = () => {
|
|||
const mutation = useMutation({
|
||||
mutationKey: [KEYS.global_mutation, rsformsApi.baseKey, 'reset-aliases'],
|
||||
mutationFn: rsformsApi.resetAliases,
|
||||
onSuccess: data => {
|
||||
onSuccess: async data => {
|
||||
client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey, data);
|
||||
updateTimestamp(data.id);
|
||||
|
||||
return Promise.allSettled([
|
||||
await Promise.allSettled([
|
||||
client.invalidateQueries({ queryKey: [KEYS.oss] }),
|
||||
client.invalidateQueries({
|
||||
queryKey: [rsformsApi.baseKey],
|
||||
|
|
|
@ -13,11 +13,11 @@ export const useSubstituteConstituents = () => {
|
|||
const mutation = useMutation({
|
||||
mutationKey: [KEYS.global_mutation, rsformsApi.baseKey, 'substitute-constituents'],
|
||||
mutationFn: rsformsApi.substituteConstituents,
|
||||
onSuccess: data => {
|
||||
onSuccess: async data => {
|
||||
client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey, data);
|
||||
updateTimestamp(data.id);
|
||||
|
||||
return Promise.allSettled([
|
||||
await Promise.allSettled([
|
||||
client.invalidateQueries({ queryKey: [KEYS.oss] }),
|
||||
client.invalidateQueries({
|
||||
queryKey: [rsformsApi.baseKey],
|
||||
|
|
|
@ -13,10 +13,10 @@ export const useUpdateConstituenta = () => {
|
|||
const mutation = useMutation({
|
||||
mutationKey: [KEYS.global_mutation, rsformsApi.baseKey, 'update-constituenta'],
|
||||
mutationFn: rsformsApi.updateConstituenta,
|
||||
onSuccess: (_, variables) => {
|
||||
onSuccess: async (_, variables) => {
|
||||
updateTimestamp(variables.itemID);
|
||||
|
||||
return Promise.allSettled([
|
||||
await Promise.allSettled([
|
||||
client.invalidateQueries({ queryKey: [KEYS.oss] }),
|
||||
client.invalidateQueries({ queryKey: [rsformsApi.baseKey] })
|
||||
]);
|
||||
|
|
|
@ -12,13 +12,13 @@ export const useUploadTRS = () => {
|
|||
const mutation = useMutation({
|
||||
mutationKey: [KEYS.global_mutation, rsformsApi.baseKey, 'load-trs'],
|
||||
mutationFn: rsformsApi.upload,
|
||||
onSuccess: data => {
|
||||
onSuccess: async data => {
|
||||
client.setQueryData(KEYS.composite.rsItem({ itemID: data.id }), data);
|
||||
client.setQueryData(KEYS.composite.libraryList, (prev: ILibraryItem[] | undefined) =>
|
||||
prev?.map(item => (item.id === data.id ? data : item))
|
||||
);
|
||||
|
||||
return Promise.allSettled([
|
||||
await Promise.allSettled([
|
||||
client.invalidateQueries({ queryKey: [KEYS.oss] }),
|
||||
client.invalidateQueries({
|
||||
queryKey: [rsformsApi.baseKey],
|
||||
|
|
|
@ -1,20 +1,18 @@
|
|||
// This file was generated by lezer-generator. You probably shouldn't edit it.
|
||||
import { LRParser } from '@lezer/lr';
|
||||
import { highlighting } from './highlight';
|
||||
import {LRParser} from "@lezer/lr"
|
||||
import {highlighting} from "./highlight"
|
||||
export const parser = LRParser.deserialize({
|
||||
version: 14,
|
||||
states:
|
||||
"$nQVQPOOObQQO'#C^OOQO'#Cl'#ClOjQPO'#CuOxQPO'#CdOOQO'#Ce'#CeOOQO'#Ck'#CkOOQO'#Cf'#CfQVQPOOO}QPO,58xO!SQPO,58{OOQO,59a,59aO!XQPO,59OOOQO-E6d-E6dO!^QSO1G.dO!cQPO1G.gOOQO1G.j1G.jO!hQQO'#CpOOQO'#C`'#C`O!pQPO7+$OOOQO'#Cg'#CgO!uQPO'#CcO!}QPO7+$RO!^QSO,59[OOQO<<Gj<<GjOOQO-E6e-E6eOOQO<<Gm<<GmOOQO1G.v1G.v",
|
||||
stateData: '#V~O^OS~ObPOgROhSO~ORXOUYO~OgRO[iXbiXhiX~OgRO~Oc^O~Oc_O~Oh`O~OeaO~OgdO~OfgOadX~OahO~OgdOaVX~OajO~Og^~',
|
||||
goto: '!kjPPkPokPruuy!PPPP!VuPPP!ZPPPP!aTQOWRc^Rf_TUOWQWOR]WQe_RieTVOWQb^RkgSTOWQZRR[S',
|
||||
nodeNames: '⚠ Text RefEntity Global Grams RefSyntactic Offset Nominal Error Filler',
|
||||
states: "$nQVQPOOObQQO'#C^OOQO'#Cl'#ClOjQPO'#CuOxQPO'#CdOOQO'#Ce'#CeOOQO'#Ck'#CkOOQO'#Cf'#CfQVQPOOO}QPO,58xO!SQPO,58{OOQO,59a,59aO!XQPO,59OOOQO-E6d-E6dO!^QSO1G.dO!cQPO1G.gOOQO1G.j1G.jO!hQQO'#CpOOQO'#C`'#C`O!pQPO7+$OOOQO'#Cg'#CgO!uQPO'#CcO!}QPO7+$RO!^QSO,59[OOQO<<Gj<<GjOOQO-E6e-E6eOOQO<<Gm<<GmOOQO1G.v1G.v",
|
||||
stateData: "#V~O^OS~ObPOgROhSO~ORXOUYO~OgRO[iXbiXhiX~OgRO~Oc^O~Oc_O~Oh`O~OeaO~OgdO~OfgOadX~OahO~OgdOaVX~OajO~Og^~",
|
||||
goto: "!kjPPkPokPruuy!PPPP!VuPPP!ZPPPP!aTQOWRc^Rf_TUOWQWOR]WQe_RieTVOWQb^RkgSTOWQZRR[S",
|
||||
nodeNames: "⚠ Text RefEntity Global Grams RefSyntactic Offset Nominal Error Filler",
|
||||
maxTerm: 25,
|
||||
propSources: [highlighting],
|
||||
skippedNodes: [0],
|
||||
repeatNodeCount: 2,
|
||||
tokenData:
|
||||
".V~R!UOX$eXZ%VZ^%z^p$epq%Vqr'sr|$e|}'x}!O(f!O!Q$e!Q!R)i!R![*i![!b$e!b!c+k!c!d+v!d!e)i!e!f+v!f!g+v!g!h)i!h!i+v!i!r)i!r!s+v!s!t)i!t!u+v!u!v+v!v!w+v!w!z)i!z!{+v!{!})i!}#T$e#T#o)i#p#q-{#q#r.Q#r#y$e#y#z%z#z$f$e$f$g%z$g#BY$e#BY#BZ%z#BZ$IS$e$IS$I_%z$I_$I|$e$I|$JO%z$JO$JT$e$JT$JU%z$JU$KV$e$KV$KW%z$KW&FU$e&FU&FV%z&FV;'S$e;'S;=`%P<%lO$eP$jVgPOX$eZp$er!b$e!c#o$e#r;'S$e;'S;=`%P<%lO$eP%SP;=`<%l$e~%[Y^~X^%Vpq%V#y#z%V$f$g%V#BY#BZ%V$IS$I_%V$I|$JO%V$JT$JU%V$KV$KW%V&FU&FV%V~&RjgP^~OX$eXZ%VZ^%z^p$epq%Vr!b$e!c#o$e#r#y$e#y#z%z#z$f$e$f$g%z$g#BY$e#BY#BZ%z#BZ$IS$e$IS$I_%z$I_$I|$e$I|$JO%z$JO$JT$e$JT$JU%z$JU$KV$e$KV$KW%z$KW&FU$e&FU&FV%z&FV;'S$e;'S;=`%P<%lO$e~'xOh~R(PVfQgPOX$eZp$er!b$e!c#o$e#r;'S$e;'S;=`%P<%lO$eV(m^eSgPOX$eZp$er}$e}!O)i!O!Q$e!Q!R)i!R![*i![!b$e!c!})i!}#T$e#T#o)i#r;'S$e;'S;=`%P<%lO$eT)p]eSgPOX$eZp$er}$e}!O)i!O!Q$e!Q![)i![!b$e!c!})i!}#T$e#T#o)i#r;'S$e;'S;=`%P<%lO$eV*r]UQeSgPOX$eZp$er}$e}!O)i!O!Q$e!Q![*i![!b$e!c!})i!}#T$e#T#o)i#r;'S$e;'S;=`%P<%lO$e~+nP#o#p+q~+vOb~V+}^eSgPOX$eZp$er}$e}!O)i!O!Q$e!Q!R)i!R![,y![!b$e!c!})i!}#T$e#T#o)i#r;'S$e;'S;=`%P<%lO$eV-S]RQeSgPOX$eZp$er}$e}!O)i!O!Q$e!Q![,y![!b$e!c!})i!}#T$e#T#o)i#r;'S$e;'S;=`%P<%lO$e~.QOc~~.VOa~",
|
||||
tokenData: ".V~R!UOX$eXZ%VZ^%z^p$epq%Vqr'sr|$e|}'x}!O(f!O!Q$e!Q!R)i!R![*i![!b$e!b!c+k!c!d+v!d!e)i!e!f+v!f!g+v!g!h)i!h!i+v!i!r)i!r!s+v!s!t)i!t!u+v!u!v+v!v!w+v!w!z)i!z!{+v!{!})i!}#T$e#T#o)i#p#q-{#q#r.Q#r#y$e#y#z%z#z$f$e$f$g%z$g#BY$e#BY#BZ%z#BZ$IS$e$IS$I_%z$I_$I|$e$I|$JO%z$JO$JT$e$JT$JU%z$JU$KV$e$KV$KW%z$KW&FU$e&FU&FV%z&FV;'S$e;'S;=`%P<%lO$eP$jVgPOX$eZp$er!b$e!c#o$e#r;'S$e;'S;=`%P<%lO$eP%SP;=`<%l$e~%[Y^~X^%Vpq%V#y#z%V$f$g%V#BY#BZ%V$IS$I_%V$I|$JO%V$JT$JU%V$KV$KW%V&FU&FV%V~&RjgP^~OX$eXZ%VZ^%z^p$epq%Vr!b$e!c#o$e#r#y$e#y#z%z#z$f$e$f$g%z$g#BY$e#BY#BZ%z#BZ$IS$e$IS$I_%z$I_$I|$e$I|$JO%z$JO$JT$e$JT$JU%z$JU$KV$e$KV$KW%z$KW&FU$e&FU&FV%z&FV;'S$e;'S;=`%P<%lO$e~'xOh~R(PVfQgPOX$eZp$er!b$e!c#o$e#r;'S$e;'S;=`%P<%lO$eV(m^eSgPOX$eZp$er}$e}!O)i!O!Q$e!Q!R)i!R![*i![!b$e!c!})i!}#T$e#T#o)i#r;'S$e;'S;=`%P<%lO$eT)p]eSgPOX$eZp$er}$e}!O)i!O!Q$e!Q![)i![!b$e!c!})i!}#T$e#T#o)i#r;'S$e;'S;=`%P<%lO$eV*r]UQeSgPOX$eZp$er}$e}!O)i!O!Q$e!Q![*i![!b$e!c!})i!}#T$e#T#o)i#r;'S$e;'S;=`%P<%lO$e~+nP#o#p+q~+vOb~V+}^eSgPOX$eZp$er}$e}!O)i!O!Q$e!Q!R)i!R![,y![!b$e!c!})i!}#T$e#T#o)i#r;'S$e;'S;=`%P<%lO$eV-S]RQeSgPOX$eZp$er}$e}!O)i!O!Q$e!Q![,y![!b$e!c!})i!}#T$e#T#o)i#r;'S$e;'S;=`%P<%lO$e~.QOc~~.VOa~",
|
||||
tokenizers: [0, 1, 2],
|
||||
topRules: { Text: [0, 1] },
|
||||
topRules: {"Text":[0,1]},
|
||||
tokenPrec: 96
|
||||
});
|
||||
})
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
// This file was generated by lezer-generator. You probably shouldn't edit it.
|
||||
export const
|
||||
Expression = 1,
|
||||
export const Expression = 1,
|
||||
Logic = 2,
|
||||
Logic_predicates = 3,
|
||||
Variable = 4,
|
||||
|
@ -43,4 +42,4 @@ export const
|
|||
Logic_binary = 76,
|
||||
Function_decl = 81,
|
||||
Arguments = 82,
|
||||
Declaration = 83
|
||||
Declaration = 83;
|
||||
|
|
|
@ -1,20 +1,24 @@
|
|||
// This file was generated by lezer-generator. You probably shouldn't edit it.
|
||||
import {LRParser} from "@lezer/lr"
|
||||
import { LRParser } from '@lezer/lr';
|
||||
export const parser = LRParser.deserialize({
|
||||
version: 14,
|
||||
states: "2xO!sQPOOOOQO'#Ch'#ChO#lQPO'#EWOOQO'#EW'#EWO%yQPO'#EVOVQPO'#CnO'_QPO'#CzO'fQPO'#C{O'nQPO'#C}O'sQPO'#DRO'xQPO'#DWO'}QPO'#D[OOQO'#Cw'#CwO(SQPO'#CwOOQO'#D_'#D_OOQO'#Cg'#CgO)pQPO'#CgO)uQPO'#CgO)zQPO'#C_OVQPO'#DsO*SQPO'#DvOOQO'#Dr'#DrO*[QPO'#DrO*aQPO'#EVOOQO'#C^'#C^O*rQPO'#EPQOQPOOO*wQPO,59YO*wQPO,59YO*wQPO,59YO*wQPO,59YO*wQPO,59YO*wQPO,59YO*wQPO,59YO*wQPO,59YO*wQPO,58yO+OQPO'#CfO+VQPO,59YO,jQPO,59OO,rQPO'#CeO,wQPO,58xO-YQPO,59mO-{QPO'#CfO*wQPO'#CnO.YQPO,59fOOQO'#Cf'#CfO*wQPO,59gOOQO,59g,59gO*wQPO,59iO*SQPO,59mO*wQPO,59rO*SQPO,59vO*wQPO,59RO*wQPO,59RO$lQPO'#C_OOQO,5:_,5:_O*wQPO'#CdOOQO'#C`'#C`OOQO'#Dx'#DxO.bQPO,5:bO*wQPO,5:^OVQPO,5:fOVQPO,5:fOVQPO,5:fOVQPO,5:fO.jQPO'#EROOQO'#EQ'#EQO.oQPO,5:kOOQO1G.t1G.tO2`QPO1G.tO2gQPO1G.tO6VQPO1G.tO9uQPO1G.tO9|QPO1G.tO:TQPO1G.tO:[QPO1G.tO=vQPO1G.eOOQO1G.j1G.jO*wQPO,59POOQO1G.d1G.dO*wQPO1G/XOOQO1G/Q1G/QO>gQPO1G/RO>nQPO1G/TO>vQPO1G/XO>{QPO1G/^O?SQPO1G/bO?XQPO1G.mO?aQPO1G.mO*SQPO,5:dO*wQPO1G/|O?hQPO1G/xOOQO1G0Q1G0QO@[QPO1G0QO@cQPO1G0QO@jQPO1G0QO*wQPO,5:mO*rQPO,5:lOVQPO1G0VOAXQPO1G.kOAiQPO7+$sOOQO7+$m7+$mOApQPO7+$oO*wQPO7+$sOVQPO7+$xO*wQPO7+$|OOQO7+$X7+$XOOQO1G0O1G0OOAuQPO7+%hOOQO7+%d7+%dOBcQPO1G0XOOQO1G0W1G0WOOQO7+%q7+%qOVQPO<<H_O*wQPO<<HZOBmQPO<<H_OBtQPO'#DXOCYQPO<<HdOCbQPO<<HhOCiQPO<<ISODWQPOAN=yODiQPOAN=uOVQPOAN=yOVQPO,59sOOQOAN>OAN>OOVQPOAN>SOOQOG23eG23eOOQOG23aG23aODpQPOG23eOERQPO1G/_OEgQPOG23nOEnQPOG23nOOQOLD)PLD)POOQOLD)YLD)YO*wQPOLD)YOFPQPO!$'LtOOQO!)9B`!)9B`",
|
||||
stateData: "Fi~O!xOS~OTQOVTO]PO^PO_PO`ROaROmUOpVOrWOxXOyYO}ZO!Q`O!S^O!T^O!U^O!V^O!W^O!X^O!hcO!ifO!kdO!mdO~OsiO~PVOc!zXd!zXe!zXf!zXg!zXh!zXi!zXj!zX!Y!zXl!zX~Ov!zX!PSX!ZSX![!zX!]!zX!^!zX!_!zX!`!zX!a!zX!b!zX!c!zX!d!zX!e!zX!v!zX~P!zOckOdlOemOfnOgoOhpOiqOjrOvsO![sO!]sO!^sO!_sO!`sO!asO!bsO!csO!dsO!esO~O!v!yX~P$lOV{O]PO^PO_PO`ROaROmUOpVOrWOxXOyYO}ZO!Q`O!S^O!T^O!U^O!V^O!W^O!X^O~OTyO~P&QOV!OOpVO~Os!QO~Om!RO~Om!SO~Om!TO~OckXdkXekXfkXgkXhkXikXjkXvkX!PSX!ZSX![kX!]kX!^kX!_kX!`kX!akX!bkX!ckX!dkX!ekX!vkX!YkXlkX~Os!UO~OV!VO~O!PsO!ZsO~OT!ZOV!YO~Os!^O~O!o!_O!p!`O!q!aO!r!bO!v!yX~OT!cO~OTRO~P&QO!YYX~P$lOU!fOcZXdZXeZXfZXgZXhZXiZXjZXvZX!YZX![ZX!]ZX!^ZX!_ZX!`ZX!aZX!bZX!cZX!dZX!eZX~OU!oO!YYX~O!Y!pO~OU!qO!o!_O!p!`O!q!aO!r!bO~Ov!rO~P!zOckOdlOemOfnOgoOhpOiqOjrO~OlYX!YYXtYX~P-aOl!sO!Y!pO~Ov!|O!Y!{O~Ov#SO~Ot#UO!Y#TO~OckOebifbigbihbiibijbivbi![bi!]bi!^bi!_bi!`bi!abi!bbi!cbi!dbi!ebi!vbiUbi!Ybilbi!obi!pbi!qbi!rbitbiwbiTbiVbi]bi^bi_bi`biabimbipbirbixbiybi}bi!Qbi!Sbi!Tbi!Ubi!Vbi!Wbi!Xbi!hbi!ibi!kbi!mbi|bi~Odbi~P.wOdlO~P.wOckOdlOemOjrOgbihbiibivbi![bi!]bi!^bi!_bi!`bi!abi!bbi!cbi!dbi!ebi!vbiUbi!Ybilbi!obi!pbi!qbi!rbitbiwbiTbiVbi]bi^bi_bi`biabimbipbirbixbiybi}bi!Qbi!Sbi!Tbi!Ubi!Vbi!Wbi!Xbi!hbi!ibi!kbi!mbi|bi~Ofbi~P2nOckOdlOemOfnOiqOjrOhbivbi![bi!]bi!^bi!_bi!`bi!abi!bbi!cbi!dbi!ebi!vbiUbi!Ybilbi!obi!pbi!qbi!rbitbiwbiTbiVbi]bi^bi_bi`biabimbipbirbixbiybi}bi!Qbi!Sbi!Tbi!Ubi!Vbi!Wbi!Xbi!hbi!ibi!kbi!mbi|bi~Ogbi~P6^OgoO~P6^OfnO~P2nOckOdlOemOfbigbihbiibijbivbi![bi!]bi!^bi!_bi!`bi!abi!bbi!cbi!dbi!ebi!vbiUbi!Ybilbi!obi!pbi!qbi!rbitbiwbiTbiVbi]bi^bi_bi`biabimbipbirbixbiybi}bi!Qbi!Sbi!Tbi!Ubi!Vbi!Wbi!Xbi!hbi!ibi!kbi!mbi|bi~O!oRi!pRi!qRi!rRi!vRiURilRi|RiwRi~P-aOU#XO~P-aOt#YO!Y!pO~Ov#ZO~Ow#[O~P-aO!P#]O~Ot#^O!Y!pO~OU#^O~P-aOt#aO!Y!pO~O!o!_O!q!ni!r!ni!v!niU!nil!ni|!niw!ni~O!p!ni~P?pO!p!`O~P?pO!o!_O!p!`O!q!aO!r!ni!v!niU!nil!ni|!niw!ni~OUXi!YXilXitXi~P-aOw#eO~P-aOV#fO~OckOdlOemOfnOgoOhpOiqOjrO~PVOt!ui!Y!ui~P-aOw#nO~P-aO!o!_O!p!`O!q!aO!r!bOl{X|{X~Ol#pO|#oO~Ow#qO~P-aO!o!_O!p!`O!q!aO!r!bO!v!jyU!jyl!jy|!jyw!jy~Ol#rO!o!_O!p!`O!q!aO!r!bO~OU#sO~P-aOl#xO!o!_O!p!`O!q!aO!r!bO~O!o!_O!p!`O!q!aO!r!bOl{i|{i~Ol#yO~P$lOw#zO!o!_O!p!`O!q!aO!r!bO~Ol#|O~P-aOr!S!i!Q`a!U!V!W!X!TT}yxy~",
|
||||
goto: "-}!{PP!|#v$WPPP$t%x&T&f(dPPPPP)`PPPPPPPP(dPP*^+YP*^PPP*^PPPP*^,XPP*^PP,[PPPPPPPPPPPPPPPPPP#v-WPP-WP-hP#vPPPP-k-n-qPPP-w(dSgO#UQxTQ!XcQ#O!_Q#P!`Q#Q!aQ#R!bQ#h#[Q#k#`Q#l#eQ#t#nQ#u#oR#w#qmhOTc!_!`!a!b#U#[#`#e#n#o#qlbOTc!_!`!a!b#U#[#`#e#n#o#qQ![dQ!v!RQ!x!TR#_!{!U[Uklmnopqrs{!O!Q!S!U!V!Y!^!p!r!|#S#Z#]#f#zl]OTc!_!`!a!b#U#[#`#e#n#o#qX!Zd!R!T!{UvT{!YX}U!Q!U!^UwT{!YQ|UQ!u!QQ!y!UR!}!^SSO#UQtT[zU{!Q!U!Y!^d!Wc!_!`!a!b#[#`#e#n#oQ!fkQ!glQ!hmQ!inQ!joQ!kpQ!lqQ!mrQ!nsQ!t!OQ!w!SQ!z!VQ#V!pQ#W!rQ#`!|Q#b#SQ#g#ZQ#j#]Q#m#fQ#v#qR#{#z!s_OTUcklmnopqrs{!O!Q!S!U!V!Y!^!_!`!a!b!p!r!|#S#U#Z#[#]#`#e#f#n#o#q#z!n_OUcklmnopqrs!O!Q!S!U!V!Y!^!_!`!a!b!p!r!|#S#U#Z#[#]#`#e#f#n#o#q#zTuT{!s[OTUcklmnopqrs{!O!Q!S!U!V!Y!^!_!`!a!b!p!r!|#S#U#Z#[#]#`#e#f#n#o#q#z!r[OTUcklmnopqrs{!O!Q!S!U!V!Y!^!_!`!a!b!p!r!|#S#U#Z#[#]#`#e#f#n#o#q#zR!PVR#i#[!saOTUcklmnopqrs{!O!Q!S!U!V!Y!^!_!`!a!b!p!r!|#S#U#Z#[#]#`#e#f#n#o#q#zmeOTc!_!`!a!b#U#[#`#e#n#o#qR!]dRjOR!eiQ!diR#c#TQjOR#d#U",
|
||||
nodeNames: "⚠ Expression Logic Logic_predicates Variable Local ) ( Tuple Setexpr_enum_min2 Setexpr_enum Setexpr Literal Integer EmptySet IntegerSet Global Radical Setexpr_binary + - * ∪ \\ ∆ ∩ × Setexpr_generators } { Enumeration Boolean ℬ Filter_expression Filter [ ] Declarative ∈ | PrefixD PrefixI Imperative Imp_blocks ; PrefixR Recursion := Function TextFunction BigPr SmallPr Card Bool Debool Red , :∈ ∉ ⊆ ⊄ ⊂ > ≥ < ≤ ≠ = Logic_unary Negation ¬ Predicate Logic_quantor ∀ Variable_pack ∃ Logic_binary ⇔ ⇒ ∨ & Function_decl Arguments Declaration",
|
||||
states:
|
||||
"2xO!sQPOOOOQO'#Ch'#ChO#lQPO'#EWOOQO'#EW'#EWO%yQPO'#EVOVQPO'#CnO'_QPO'#CzO'fQPO'#C{O'nQPO'#C}O'sQPO'#DRO'xQPO'#DWO'}QPO'#D[OOQO'#Cw'#CwO(SQPO'#CwOOQO'#D_'#D_OOQO'#Cg'#CgO)pQPO'#CgO)uQPO'#CgO)zQPO'#C_OVQPO'#DsO*SQPO'#DvOOQO'#Dr'#DrO*[QPO'#DrO*aQPO'#EVOOQO'#C^'#C^O*rQPO'#EPQOQPOOO*wQPO,59YO*wQPO,59YO*wQPO,59YO*wQPO,59YO*wQPO,59YO*wQPO,59YO*wQPO,59YO*wQPO,59YO*wQPO,58yO+OQPO'#CfO+VQPO,59YO,jQPO,59OO,rQPO'#CeO,wQPO,58xO-YQPO,59mO-{QPO'#CfO*wQPO'#CnO.YQPO,59fOOQO'#Cf'#CfO*wQPO,59gOOQO,59g,59gO*wQPO,59iO*SQPO,59mO*wQPO,59rO*SQPO,59vO*wQPO,59RO*wQPO,59RO$lQPO'#C_O.bQPO,5:_O*wQPO'#CdOOQO'#C`'#C`OOQO'#Dx'#DxO/PQPO,5:bO*wQPO,5:^OVQPO,5:fOVQPO,5:fOVQPO,5:fOVQPO,5:fO/XQPO'#EROOQO'#EQ'#EQO/^QPO,5:kOOQO1G.t1G.tO2}QPO1G.tO3UQPO1G.tO6tQPO1G.tO:dQPO1G.tO:kQPO1G.tO:rQPO1G.tO:yQPO1G.tO>eQPO1G.eOOQO1G.j1G.jO*wQPO,59POOQO1G.d1G.dO*wQPO1G/XOOQO1G/Q1G/QO?UQPO1G/RO?]QPO1G/TO?eQPO1G/XO?jQPO1G/^O?qQPO1G/bO?vQPO1G.mO@OQPO1G.mO*SQPO,5:dO*wQPO1G/|O@VQPO1G/xO@yQPO1G0QOAQQPO1G0QOAXQPO1G0QOOQO1G0Q1G0QO*wQPO,5:mO*rQPO,5:lOVQPO1G0VOAvQPO1G.kOBWQPO7+$sOOQO7+$m7+$mOB_QPO7+$oO*wQPO7+$sOVQPO7+$xO*wQPO7+$|OOQO7+$X7+$XOOQO1G0O1G0OOBdQPO7+%hOOQO7+%d7+%dOCQQPO1G0XOOQO1G0W1G0WOOQO7+%q7+%qOVQPO<<H_O*wQPO<<HZOC[QPO<<H_OCcQPO'#DXOCwQPO<<HdODPQPO<<HhODWQPO<<ISODuQPOAN=yOEWQPOAN=uOVQPOAN=yOVQPO,59sOOQOAN>OAN>OOVQPOAN>SOOQOG23eG23eOOQOG23aG23aOE_QPOG23eOEpQPO1G/_OFUQPOG23nOF]QPOG23nOOQOLD)PLD)POOQOLD)YLD)YO*wQPOLD)YOFnQPO!$'LtOOQO!)9B`!)9B`",
|
||||
stateData:
|
||||
'GW~O!xOS~OTQOVTO]PO^PO_PO`ROaROmUOpVOrWOxXOyYO}ZO!Q`O!S^O!T^O!U^O!V^O!W^O!X^O!hcO!ifO!kdO!mdO~OsiO~PVOc!zXd!zXe!zXf!zXg!zXh!zXi!zXj!zX!Y!zXl!zX~Ov!zX!PSX!ZSX![!zX!]!zX!^!zX!_!zX!`!zX!a!zX!b!zX!c!zX!d!zX!e!zX!v!zX~P!zOckOdlOemOfnOgoOhpOiqOjrOvsO![sO!]sO!^sO!_sO!`sO!asO!bsO!csO!dsO!esO~O!v!yX~P$lOV{O]PO^PO_PO`ROaROmUOpVOrWOxXOyYO}ZO!Q`O!S^O!T^O!U^O!V^O!W^O!X^O~OTyO~P&QOV!OOpVO~Os!QO~Om!RO~Om!SO~Om!TO~OckXdkXekXfkXgkXhkXikXjkXvkX!PSX!ZSX![kX!]kX!^kX!_kX!`kX!akX!bkX!ckX!dkX!ekX!vkX!YkXlkX~Os!UO~OV!VO~O!PsO!ZsO~OT!ZOV!YO~Os!^O~O!o!_O!p!`O!q!aO!r!bO!v!yX~OT!cO~OTRO~P&QO!YYX~P$lOU!fOcZXdZXeZXfZXgZXhZXiZXjZXvZX!YZX![ZX!]ZX!^ZX!_ZX!`ZX!aZX!bZX!cZX!dZX!eZX~OU!oO!YYX~O!Y!pO~OU!qO!o!_O!p!`O!q!aO!r!bO~Ov!rO~P!zOckOdlOemOfnOgoOhpOiqOjrO~OlYX!YYXtYX~P-aOl!sO!Y!pO~O!o!_O!p!`O!q!aO!r!bO!v!gaU!gal!ga|!gaw!ga~Ov!|O!Y!{O~Ov#SO~Ot#UO!Y#TO~OckOebifbigbihbiibijbivbi![bi!]bi!^bi!_bi!`bi!abi!bbi!cbi!dbi!ebi!vbiUbi!Ybilbi!obi!pbi!qbi!rbitbiwbiTbiVbi]bi^bi_bi`biabimbipbirbixbiybi}bi!Qbi!Sbi!Tbi!Ubi!Vbi!Wbi!Xbi!hbi!ibi!kbi!mbi|bi~Odbi~P/fOdlO~P/fOckOdlOemOjrOgbihbiibivbi![bi!]bi!^bi!_bi!`bi!abi!bbi!cbi!dbi!ebi!vbiUbi!Ybilbi!obi!pbi!qbi!rbitbiwbiTbiVbi]bi^bi_bi`biabimbipbirbixbiybi}bi!Qbi!Sbi!Tbi!Ubi!Vbi!Wbi!Xbi!hbi!ibi!kbi!mbi|bi~Ofbi~P3]OckOdlOemOfnOiqOjrOhbivbi![bi!]bi!^bi!_bi!`bi!abi!bbi!cbi!dbi!ebi!vbiUbi!Ybilbi!obi!pbi!qbi!rbitbiwbiTbiVbi]bi^bi_bi`biabimbipbirbixbiybi}bi!Qbi!Sbi!Tbi!Ubi!Vbi!Wbi!Xbi!hbi!ibi!kbi!mbi|bi~Ogbi~P6{OgoO~P6{OfnO~P3]OckOdlOemOfbigbihbiibijbivbi![bi!]bi!^bi!_bi!`bi!abi!bbi!cbi!dbi!ebi!vbiUbi!Ybilbi!obi!pbi!qbi!rbitbiwbiTbiVbi]bi^bi_bi`biabimbipbirbixbiybi}bi!Qbi!Sbi!Tbi!Ubi!Vbi!Wbi!Xbi!hbi!ibi!kbi!mbi|bi~O!oRi!pRi!qRi!rRi!vRiURilRi|RiwRi~P-aOU#XO~P-aOt#YO!Y!pO~Ov#ZO~Ow#[O~P-aO!P#]O~Ot#^O!Y!pO~OU#^O~P-aOt#aO!Y!pO~O!q!aO!r!bO!o!ni!v!niU!nil!ni|!niw!ni~O!p!`O~P@_O!p!ni~P@_O!r!bO!o!ni!p!ni!q!ni!v!niU!nil!ni|!niw!ni~OUXi!YXilXitXi~P-aOw#eO~P-aOV#fO~OckOdlOemOfnOgoOhpOiqOjrO~PVOt!ui!Y!ui~P-aOw#nO~P-aO!o!_O!p!`O!q!aO!r!bOl{X|{X~Ol#pO|#oO~Ow#qO~P-aO!o!_O!p!`O!q!aO!r!bO!v!jyU!jyl!jy|!jyw!jy~Ol#rO!o!_O!p!`O!q!aO!r!bO~OU#sO~P-aOl#xO!o!_O!p!`O!q!aO!r!bO~O!o!_O!p!`O!q!aO!r!bOl{i|{i~Ol#yO~P$lOw#zO!o!_O!p!`O!q!aO!r!bO~Ol#|O~P-aOr!S!i!Q`a!U!V!W!X!TT}yxy~',
|
||||
goto: '-}!{PP!|#v$WPPP$t%x&T&f(dPPPPP)`PPPPPPPP(dPP*^+YP*^PPP*^PPPP*^,XPP*^PP,[PPPPPPPPPPPPPPPPPP#v-WPP-WP-hP#vPPPP-k-n-qPPP-w(dSgO#UQxTQ!XcQ#O!_Q#P!`Q#Q!aQ#R!bQ#h#[Q#k#`Q#l#eQ#t#nQ#u#oR#w#qmhOTc!_!`!a!b#U#[#`#e#n#o#qlbOTc!_!`!a!b#U#[#`#e#n#o#qQ![dQ!v!RQ!x!TR#_!{!U[Uklmnopqrs{!O!Q!S!U!V!Y!^!p!r!|#S#Z#]#f#zl]OTc!_!`!a!b#U#[#`#e#n#o#qX!Zd!R!T!{UvT{!YX}U!Q!U!^UwT{!YQ|UQ!u!QQ!y!UR!}!^SSO#UQtT[zU{!Q!U!Y!^d!Wc!_!`!a!b#[#`#e#n#oQ!fkQ!glQ!hmQ!inQ!joQ!kpQ!lqQ!mrQ!nsQ!t!OQ!w!SQ!z!VQ#V!pQ#W!rQ#`!|Q#b#SQ#g#ZQ#j#]Q#m#fQ#v#qR#{#z!s_OTUcklmnopqrs{!O!Q!S!U!V!Y!^!_!`!a!b!p!r!|#S#U#Z#[#]#`#e#f#n#o#q#z!n_OUcklmnopqrs!O!Q!S!U!V!Y!^!_!`!a!b!p!r!|#S#U#Z#[#]#`#e#f#n#o#q#zTuT{!s[OTUcklmnopqrs{!O!Q!S!U!V!Y!^!_!`!a!b!p!r!|#S#U#Z#[#]#`#e#f#n#o#q#z!r[OTUcklmnopqrs{!O!Q!S!U!V!Y!^!_!`!a!b!p!r!|#S#U#Z#[#]#`#e#f#n#o#q#zR!PVR#i#[!saOTUcklmnopqrs{!O!Q!S!U!V!Y!^!_!`!a!b!p!r!|#S#U#Z#[#]#`#e#f#n#o#q#zmeOTc!_!`!a!b#U#[#`#e#n#o#qR!]dRjOR!eiQ!diR#c#TQjOR#d#U',
|
||||
nodeNames:
|
||||
'⚠ Expression Logic Logic_predicates Variable Local ) ( Tuple Setexpr_enum_min2 Setexpr_enum Setexpr Literal Integer EmptySet IntegerSet Global Radical Setexpr_binary * + - ∪ \\ ∆ ∩ × Setexpr_generators } { Enumeration Boolean ℬ Filter_expression Filter [ ] Declarative ∈ | PrefixD PrefixI Imperative Imp_blocks ; PrefixR Recursion := Function TextFunction BigPr SmallPr Card Bool Debool Red , :∈ ∉ ⊆ ⊄ ⊂ > ≥ < ≤ ≠ = Logic_unary Negation ¬ Predicate Logic_quantor ∀ Variable_pack ∃ Logic_binary ⇔ ⇒ ∨ & Function_decl Arguments Declaration',
|
||||
maxTerm: 88,
|
||||
nodeProps: [
|
||||
["openedBy", 6,"(",28,"{"],
|
||||
["closedBy", 7,")",29,"}"]
|
||||
['openedBy', 6, '(', 28, '{'],
|
||||
['closedBy', 7, ')', 29, '}']
|
||||
],
|
||||
skippedNodes: [0],
|
||||
repeatNodeCount: 0,
|
||||
tokenData: "6g~R!iX^%ppq%pvw&exy&jyz&oz{&t{|&y|}'O}!O'T!Q!['Y![!]'b!]!^'u!^!_'z!_!`(P!`!a(U!c!d(Z!e!f(Z!f!g(i!h!i(q!k!l)e!r!s)j!t!u*^!u!v(Z!v!w(Z!z!{(Z!|!}*n!}#O*s#O#P*x#P#Q*}#R#S+S#T#U+S#U#V+j#V#W-Y#W#X.u#X#d+S#d#e1_#e#f+S#f#g2t#g#o+S#o#p4O#p#q4T#q#r4Y#y#z%p$f$g%p$r$s4_%o%p4d5i6S+S#BY#BZ%p$IS$I_%p$I|$JO%p$JT$JU%p$KV$KW%p% l% m4i%%Y%%Z4n%%[%%]4s%&Y%&Z4x%&]%&^4}%&_%&`5S%&`%&a5X%&b%&c5^%&c%&d5c%'S%'T5h%'T%'U5m%'U%'V5r%(^%(_5w%(b%(c5|%(c%(d6R%)Q%)R6W%)S%)T6]%)U%)V6b&FU&FV%p~%uY!x~X^%ppq%p#y#z%p$f$g%p#BY#BZ%p$IS$I_%p$I|$JO%p$JT$JU%p$KV$KW%p&FU&FV%p~&jO!r~~&oOV~~&tOU~~&yOe~~'OOc~~'TO!Y~~'YOd~~'_P]~!Q!['Y~'eQ!_!`'k%&b%&c'p~'pO!P~~'uO!Z~~'zO|~~(PO!b~~(UO!e~~(ZO!`~~(^P!Q![(a~(fP`~!Q![(a~(nPx~!Q![(a~(tQ!Q![(z#]#^)S~)PP!Q~!Q![(z~)VP!R![)Y~)_Qr~|})S!Q![)Y~)jOy~~)mQ!Q![)s#f#g){~)xP!i~!Q![)s~*OP!R![*R~*WQ!S~|}){!Q![*R~*cP}~!Q![*f~*kPa~!Q![*f~*sO_~~*xOs~~*}Og~~+SOt~~+XRT~!Q![+b#T#o+S5i6S+S~+gPT~!Q![+b~+oTT~!Q![+b#T#c+S#c#d,O#d#o+S5i6S+S~,TTT~!Q![+b#T#c+S#c#d,d#d#o+S5i6S+S~,iTT~!Q![+b#T#`+S#`#a,x#a#o+S5i6S+S~-PR!V~T~!Q![+b#T#o+S5i6S+S~-_ST~!Q![+b#T#U-k#U#o+S5i6S+S~-pTT~!Q![+b#T#f+S#f#g.P#g#o+S5i6S+S~.UTT~!Q![+b#T#W+S#W#X.e#X#o+S5i6S+S~.lR!U~T~!Q![+b#T#o+S5i6S+S~.zTT~!Q![+b#T#X+S#X#Y/Z#Y#o+S5i6S+S~/`TT~!Q![+b#T#U+S#U#V/o#V#o+S5i6S+S~/tTT~!Q![+b#T#c+S#c#d0T#d#o+S5i6S+S~0YTT~!Q![+b#T#c+S#c#d0i#d#o+S5i6S+S~0nTT~!Q![+b#T#`+S#`#a0}#a#o+S5i6S+S~1UR!W~T~!Q![+b#T#o+S5i6S+S~1dTT~!Q![+b#T#f+S#f#g1s#g#o+S5i6S+S~1xST~!Q!R+b!R![2U#T#o+S5i6S+S~2]Q!T~T~|}2c!Q![2U~2fP!R![2i~2nQ!T~|}2c!Q![2i~2yTT~!Q![+b#T#X+S#X#Y3Y#Y#o+S5i6S+S~3_TT~!Q![+b#T#W+S#W#X3n#X#o+S5i6S+S~3uR!X~T~!Q![+b#T#o+S5i6S+S~4TOm~~4YOw~~4_Ol~~4dO!h~~4iOj~~4nOp~~4sO!p~~4xO!o~~4}O!k~~5SO!m~~5XO^~~5^Oh~~5cOv~~5hO![~~5mO!q~~5rOi~~5wOf~~5|O!d~~6RO!c~~6WO!a~~6]O!_~~6bO!^~~6gO!]~",
|
||||
tokenData:
|
||||
"6g~R!iX^%ppq%pvw&exy&jyz&oz{&t{|&y|}'O}!O'T!Q!['Y![!]'b!]!^'u!^!_'z!_!`(P!`!a(U!c!d(Z!e!f(Z!f!g(i!h!i(q!k!l)e!r!s)j!t!u*^!u!v(Z!v!w(Z!z!{(Z!|!}*n!}#O*s#O#P*x#P#Q*}#R#S+S#T#U+S#U#V+j#V#W-Y#W#X.u#X#d+S#d#e1_#e#f+S#f#g2t#g#o+S#o#p4O#p#q4T#q#r4Y#y#z%p$f$g%p$r$s4_%o%p4d5i6S+S#BY#BZ%p$IS$I_%p$I|$JO%p$JT$JU%p$KV$KW%p% l% m4i%%Y%%Z4n%%[%%]4s%&Y%&Z4x%&]%&^4}%&_%&`5S%&`%&a5X%&b%&c5^%&c%&d5c%'S%'T5h%'T%'U5m%'U%'V5r%(^%(_5w%(b%(c5|%(c%(d6R%)Q%)R6W%)S%)T6]%)U%)V6b&FU&FV%p~%uY!x~X^%ppq%p#y#z%p$f$g%p#BY#BZ%p$IS$I_%p$I|$JO%p$JT$JU%p$KV$KW%p&FU&FV%p~&jO!r~~&oOV~~&tOU~~&yOc~~'OOd~~'TO!Y~~'YOe~~'_P]~!Q!['Y~'eQ!_!`'k%&b%&c'p~'pO!P~~'uO!Z~~'zO|~~(PO!b~~(UO!e~~(ZO!`~~(^P!Q![(a~(fP`~!Q![(a~(nPx~!Q![(a~(tQ!Q![(z#]#^)S~)PP!Q~!Q![(z~)VP!R![)Y~)_Qr~|})S!Q![)Y~)jOy~~)mQ!Q![)s#f#g){~)xP!i~!Q![)s~*OP!R![*R~*WQ!S~|}){!Q![*R~*cP}~!Q![*f~*kPa~!Q![*f~*sO_~~*xOs~~*}Og~~+SOt~~+XRT~!Q![+b#T#o+S5i6S+S~+gPT~!Q![+b~+oTT~!Q![+b#T#c+S#c#d,O#d#o+S5i6S+S~,TTT~!Q![+b#T#c+S#c#d,d#d#o+S5i6S+S~,iTT~!Q![+b#T#`+S#`#a,x#a#o+S5i6S+S~-PR!V~T~!Q![+b#T#o+S5i6S+S~-_ST~!Q![+b#T#U-k#U#o+S5i6S+S~-pTT~!Q![+b#T#f+S#f#g.P#g#o+S5i6S+S~.UTT~!Q![+b#T#W+S#W#X.e#X#o+S5i6S+S~.lR!U~T~!Q![+b#T#o+S5i6S+S~.zTT~!Q![+b#T#X+S#X#Y/Z#Y#o+S5i6S+S~/`TT~!Q![+b#T#U+S#U#V/o#V#o+S5i6S+S~/tTT~!Q![+b#T#c+S#c#d0T#d#o+S5i6S+S~0YTT~!Q![+b#T#c+S#c#d0i#d#o+S5i6S+S~0nTT~!Q![+b#T#`+S#`#a0}#a#o+S5i6S+S~1UR!W~T~!Q![+b#T#o+S5i6S+S~1dTT~!Q![+b#T#f+S#f#g1s#g#o+S5i6S+S~1xST~!Q!R+b!R![2U#T#o+S5i6S+S~2]Q!T~T~|}2c!Q![2U~2fP!R![2i~2nQ!T~|}2c!Q![2i~2yTT~!Q![+b#T#X+S#X#Y3Y#Y#o+S5i6S+S~3_TT~!Q![+b#T#W+S#W#X3n#X#o+S5i6S+S~3uR!X~T~!Q![+b#T#o+S5i6S+S~4TOm~~4YOw~~4_Ol~~4dO!h~~4iOj~~4nOp~~4sO!p~~4xO!o~~4}O!k~~5SO!m~~5XO^~~5^Oh~~5cOv~~5hO![~~5mO!q~~5rOi~~5wOf~~5|O!d~~6RO!c~~6WO!a~~6]O!_~~6bO!^~~6gO!]~",
|
||||
tokenizers: [0],
|
||||
topRules: {"Expression":[0,1]},
|
||||
tokenPrec: 1710
|
||||
})
|
||||
topRules: { Expression: [0, 1] },
|
||||
tokenPrec: 1739
|
||||
});
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
// This file was generated by lezer-generator. You probably shouldn't edit it.
|
||||
export const
|
||||
Expression = 1,
|
||||
export const Expression = 1,
|
||||
Local = 2,
|
||||
Literal = 4,
|
||||
Global = 5,
|
||||
|
@ -12,4 +11,4 @@ export const
|
|||
PrefixR = 28,
|
||||
Function = 30,
|
||||
TextFunction = 31,
|
||||
Predicate = 44
|
||||
Predicate = 44;
|
||||
|
|
|
@ -4,19 +4,19 @@ import { highlighting } from './highlight';
|
|||
export const parser = LRParser.deserialize({
|
||||
version: 14,
|
||||
states:
|
||||
"3UO!sQPOOOOQO'#C`'#C`O#lQPO'#DoOOQO'#Do'#DoO%yQPO'#DdOVQPO'#DpO'_QPO'#DrO'fQPO'#DsO'nQQO'#DtO'sQPO'#DuO'xQPO'#DvO'}QPO'#DxOOQO'#Dq'#DqO(SQPO'#DqO)pQQO'#C{OOQO'#C{'#C{OOQO'#Dk'#DkO)uQPO'#DkO)zQPO'#DkO*PQPO'#DfOVQPO'#EQO*XQPO'#EQO*^QPO'#EQO*fQPO'#DdOOQO'#De'#DeO*wQPO'#ETQOQPOOO*|QPO,5:[O*|QPO,5:[O*|QPO,5:[O*|QPO,5:[O*|QPO,5:[O*|QPO,5:[O*|QPO,5:[O*|QPO,5:[O*|QPO,5:QO+TQPO'#DjO+[QPO,5:[O,oQPO,5:SO,wQPO'#DiO,|QPO,5:PO-_QPO,5:aO.QQPO'#DjO*|QPO'#DpO._QPO,5:^OOQO'#Dj'#DjO*|QPO,5:_OOQO,5:_,5:_O.gQPO,5:`O*^QPO,5:aO*|QPO,5:bO*^QPO,5:dOOQO,59g,59gO*|QPO,5:VO*|QPO,5:VO$lQPO'#DfOOQO,5:l,5:lO*|QPO,5:lO*|QPO'#DhOOQO'#Dg'#DgOOQO'#ER'#ERO.lQPO,5:lOVQPO,5:nOVQPO,5:nOVQPO,5:nOVQPO,5:nO.tQPO'#EVOOQO'#EU'#EUO.yQPO,5:oOOQO1G/v1G/vO2jQPO1G/vO2qQPO1G/vO6aQPO1G/vO:PQPO1G/vO:WQPO1G/vO:_QPO1G/vO:fQPO1G/vO>QQPO1G/lOOQO1G/n1G/nO*|QPO,5:TOOQO1G/k1G/kO*|QPO1G/{OOQO1G/x1G/xO>qQPO1G/yO*|QPO1G/zO>xQPO1G/{O>}QPO1G/|O?UQPO1G0OO?ZQPO1G/qO?cQPO1G/qO?jQPO1G0WO*^QPO,5:mO*|QPO1G0WOOQO1G0Y1G0YO@^QPO1G0YO@eQPO1G0YO@lQPO1G0YO*|QPO,5:qO*wQPO,5:pOVQPO1G0ZOAZQPO1G/oOAkQPO7+%gOOQO7+%e7+%eOArQPO7+%fO*|QPO7+%gOVQPO7+%hO*|QPO7+%jOOQO7+%]7+%]OOQO7+%r7+%rOOQO1G0X1G0XOAzQPO7+%rOBhQPO1G0]OOQO1G0[1G0[OOQO7+%u7+%uOVQPO<<IROBrQPO<<IQOBwQPO<<IROCOQPO'#DwOCdQPO<<ISOClQPO<<IUOCsQPO<<I^ODbQPOAN>mO*|QPOAN>lOVQPOAN>mOVQPO,5:cOOQOAN>nAN>nOVQPOAN>pOOQOG24XG24XODsQPOG24WODzQPOG24XOE]QPO1G/}OEqQPOG24[OExQPOG24[OOQOLD)rLD)rOOQOLD)sLD)sOOQOLD)vLD)vO*|QPOLD)vOFZQPO!$'MbOOQO!)9B|!)9B|",
|
||||
"3UO!sQPOOOOQO'#C`'#C`O#lQPO'#DoOOQO'#Do'#DoO%yQPO'#DdOVQPO'#DpO'_QPO'#DrO'fQPO'#DsO'nQQO'#DtO'sQPO'#DuO'xQPO'#DvO'}QPO'#DxOOQO'#Dq'#DqO(SQPO'#DqO)pQQO'#C{OOQO'#C{'#C{OOQO'#Dk'#DkO)uQPO'#DkO)zQPO'#DkO*PQPO'#DfOVQPO'#EQO*XQPO'#EQO*^QPO'#EQO*fQPO'#DdOOQO'#De'#DeO*wQPO'#ETQOQPOOO*|QPO,5:[O*|QPO,5:[O*|QPO,5:[O*|QPO,5:[O*|QPO,5:[O*|QPO,5:[O*|QPO,5:[O*|QPO,5:[O*|QPO,5:QO+TQPO'#DjO+[QPO,5:[O,oQPO,5:SO,wQPO'#DiO,|QPO,5:PO-_QPO,5:aO.QQPO'#DjO*|QPO'#DpO._QPO,5:^OOQO'#Dj'#DjO*|QPO,5:_OOQO,5:_,5:_O.gQPO,5:`O*^QPO,5:aO*|QPO,5:bO*^QPO,5:dOOQO,59g,59gO*|QPO,5:VO*|QPO,5:VO$lQPO'#DfO.lQPO,5:lO*|QPO,5:lO*|QPO'#DhOOQO'#Dg'#DgOOQO'#ER'#ERO/ZQPO,5:lOVQPO,5:nOVQPO,5:nOVQPO,5:nOVQPO,5:nO/cQPO'#EVOOQO'#EU'#EUO/hQPO,5:oOOQO1G/v1G/vO3XQPO1G/vO3`QPO1G/vO7OQPO1G/vO:nQPO1G/vO:uQPO1G/vO:|QPO1G/vO;TQPO1G/vO>oQPO1G/lOOQO1G/n1G/nO*|QPO,5:TOOQO1G/k1G/kO*|QPO1G/{OOQO1G/x1G/xO?`QPO1G/yO*|QPO1G/zO?gQPO1G/{O?lQPO1G/|O?sQPO1G0OO?xQPO1G/qO@QQPO1G/qO@XQPO1G0WO*^QPO,5:mO*|QPO1G0WO@{QPO1G0YOASQPO1G0YOAZQPO1G0YOOQO1G0Y1G0YO*|QPO,5:qO*wQPO,5:pOVQPO1G0ZOAxQPO1G/oOBYQPO7+%gOOQO7+%e7+%eOBaQPO7+%fO*|QPO7+%gOVQPO7+%hO*|QPO7+%jOOQO7+%]7+%]OOQO7+%r7+%rOOQO1G0X1G0XOBiQPO7+%rOCVQPO1G0]OOQO1G0[1G0[OOQO7+%u7+%uOVQPO<<IROCaQPO<<IQOCfQPO<<IROCmQPO'#DwODRQPO<<ISODZQPO<<IUODbQPO<<I^OEPQPOAN>mO*|QPOAN>lOVQPOAN>mOVQPO,5:cOOQOAN>nAN>nOVQPOAN>pOOQOG24XG24XOEbQPOG24WOEiQPOG24XOEzQPO1G/}OF`QPOG24[OFgQPOG24[OOQOLD)rLD)rOOQOLD)sLD)sOOQOLD)vLD)vO*|QPOLD)vOFxQPO!$'MbOOQO!)9B|!)9B|",
|
||||
stateData:
|
||||
'Fs~O!VOS~OQQORTOTROURO`UObVOcWOiXOjYOlZOnaO{dO|eO}fO!OfO!`PO!aPO!bPO!m^O!n^O!o_O!p_O!q_O!r_O~OeiO~PVOV!cXW!cXX!cXY!cXZ!cX[!cX]!cX^!cX!s!cXa!cX~Og!cXm!ZXp!ZXq!cXr!cXs!cXt!cXu!cXv!cXw!cXx!cXy!cXz!cX!T!cX~P!zOVkOWlOXmOYnOZoO[pO]qO^rOgsOqsOrsOssOtsOusOvsOwsOxsOysOzsO~O!T!WX~P$lOR{OTROURO`UObVOcWOiXOjYOlZOnaO!`PO!aPO!bPO!m^O!n^O!o_O!p_O!q_O!r_O~OQyO~P&QOR!OObVO~Od!QO~O`!RO~O`!SO~O`!TO~OV!eXW!eXX!eXY!eXZ!eX[!eX]!eX^!eXg!eXm!ZXp!ZXq!eXr!eXs!eXt!eXu!eXv!eXw!eXx!eXy!eXz!eX!T!eX!s!eXa!eX~Od!UO~Oe!VO~OR!WO~OmsOpsO~Oe!ZO~OQ!]OR![O~O!P!`O!Q!aO!R!bO!S!cO!T!WX~OQ!dO~OQRO~P&QO!s!^X~P$lO_!gOV!_XW!_XX!_XY!_XZ!_X[!_X]!_X^!_Xg!_Xq!_Xr!_Xs!_Xt!_Xu!_Xv!_Xw!_Xx!_Xy!_Xz!_X!s!_X~O_!pO!s!^X~O!s!qO~O_!rO!P!`O!Q!aO!R!bO!S!cO~Og!sO~P!zOVkOWlOXmOYnOZoO[pO]qO^rO~Oa!^X!s!^Xf!^X~P-fOa!tO!s!qO~Oe!vO~Og#OO!s!}O~Og#TO~Of#VO!s#UO~OVkOX!diY!diZ!di[!di]!di^!dig!diq!dir!dis!dit!diu!div!diw!dix!diy!diz!di!T!di_!di!s!dia!di!P!di!Q!di!R!di!S!dih!dif!diQ!diR!diT!diU!di`!dib!dic!dii!dij!dil!din!di{!di|!di}!di!O!di!`!di!a!di!b!di!m!di!n!di!o!di!p!di!q!di!r!dik!di~OW!di~P/ROWlO~P/ROVkOWlOXmO^rOZ!di[!di]!dig!diq!dir!dis!dit!diu!div!diw!dix!diy!diz!di!T!di_!di!s!dia!di!P!di!Q!di!R!di!S!dih!dif!diQ!diR!diT!diU!di`!dib!dic!dii!dij!dil!din!di{!di|!di}!di!O!di!`!di!a!di!b!di!m!di!n!di!o!di!p!di!q!di!r!dik!di~OY!di~P2xOVkOWlOXmOYnO]qO^rO[!dig!diq!dir!dis!dit!diu!div!diw!dix!diy!diz!di!T!di_!di!s!dia!di!P!di!Q!di!R!di!S!dih!dif!diQ!diR!diT!diU!di`!dib!dic!dii!dij!dil!din!di{!di|!di}!di!O!di!`!di!a!di!b!di!m!di!n!di!o!di!p!di!q!di!r!dik!di~OZ!di~P6hOZoO~P6hOYnO~P2xOVkOWlOXmOY!diZ!di[!di]!di^!dig!diq!dir!dis!dit!diu!div!diw!dix!diy!diz!di!T!di_!di!s!dia!di!P!di!Q!di!R!di!S!dih!dif!diQ!diR!diT!diU!di`!dib!dic!dii!dij!dil!din!di{!di|!di}!di!O!di!`!di!a!di!b!di!m!di!n!di!o!di!p!di!q!di!r!dik!di~O!P!Yi!Q!Yi!R!Yi!S!Yi!T!Yi_!Yia!Yik!Yih!Yi~P-fO_#YO~P-fOg#[O~Oh#]O~P-fOm#^O~Of#_O!s!qO~O_#_O~P-fOf#`O!s!qO~O!P!`O!R!vi!S!vi!T!vi_!via!vik!vih!vi~O!Q!vi~P?rO!Q!aO~P?rO!P!`O!Q!aO!R!bO!S!vi!T!vi_!via!vik!vih!vi~O_!]i!s!]ia!]if!]i~P-fOh#fO~P-fOf#gO!s!qO~OVkOWlOXmOYnOZoO[pO]qO^rO~PVOf!yi!s!yi~P-fOR#nO~Oh#oO~P-fO!P!`O!Q!aO!R!bO!S!cOa!kXk!kX~Oa#qOk#pO~Oh#rO~P-fO!P!`O!Q!aO!R!bO!S!cO!T!ty_!tya!tyk!tyh!ty~Oa#sO!P!`O!Q!aO!R!bO!S!cO~O_#yO~P-fOa#zO!P!`O!Q!aO!R!bO!S!cO~O!P!`O!Q!aO!R!bO!S!cOa!kik!ki~Oa#{O~P$lOh#|O!P!`O!Q!aO!R!bO!S!cO~Oa$OO~P-fOc!m|nTU!o!p!q!r!nQljij~',
|
||||
'Gb~O!VOS~OQQORTOTROURO`UObVOcWOiXOjYOlZOnaO{dO|eO}fO!OfO!`PO!aPO!bPO!m^O!n^O!o_O!p_O!q_O!r_O~OeiO~PVOV!cXW!cXX!cXY!cXZ!cX[!cX]!cX^!cX!s!cXa!cX~Og!cXm!ZXp!ZXq!cXr!cXs!cXt!cXu!cXv!cXw!cXx!cXy!cXz!cX!T!cX~P!zOVkOWlOXmOYnOZoO[pO]qO^rOgsOqsOrsOssOtsOusOvsOwsOxsOysOzsO~O!T!WX~P$lOR{OTROURO`UObVOcWOiXOjYOlZOnaO!`PO!aPO!bPO!m^O!n^O!o_O!p_O!q_O!r_O~OQyO~P&QOR!OObVO~Od!QO~O`!RO~O`!SO~O`!TO~OV!eXW!eXX!eXY!eXZ!eX[!eX]!eX^!eXg!eXm!ZXp!ZXq!eXr!eXs!eXt!eXu!eXv!eXw!eXx!eXy!eXz!eX!T!eX!s!eXa!eX~Od!UO~Oe!VO~OR!WO~OmsOpsO~Oe!ZO~OQ!]OR![O~O!P!`O!Q!aO!R!bO!S!cO!T!WX~OQ!dO~OQRO~P&QO!s!^X~P$lO_!gOV!_XW!_XX!_XY!_XZ!_X[!_X]!_X^!_Xg!_Xq!_Xr!_Xs!_Xt!_Xu!_Xv!_Xw!_Xx!_Xy!_Xz!_X!s!_X~O_!pO!s!^X~O!s!qO~O_!rO!P!`O!Q!aO!R!bO!S!cO~Og!sO~P!zOVkOWlOXmOYnOZoO[pO]qO^rO~Oa!^X!s!^Xf!^X~P-fOa!tO!s!qO~Oe!vO~O!P!`O!Q!aO!R!bO!S!cO!T!ta_!taa!tak!tah!ta~Og#OO!s!}O~Og#TO~Of#VO!s#UO~OVkOX!diY!diZ!di[!di]!di^!dig!diq!dir!dis!dit!diu!div!diw!dix!diy!diz!di!T!di_!di!s!dia!di!P!di!Q!di!R!di!S!dih!dif!diQ!diR!diT!diU!di`!dib!dic!dii!dij!dil!din!di{!di|!di}!di!O!di!`!di!a!di!b!di!m!di!n!di!o!di!p!di!q!di!r!dik!di~OW!di~P/pOWlO~P/pOVkOWlOXmO^rOZ!di[!di]!dig!diq!dir!dis!dit!diu!div!diw!dix!diy!diz!di!T!di_!di!s!dia!di!P!di!Q!di!R!di!S!dih!dif!diQ!diR!diT!diU!di`!dib!dic!dii!dij!dil!din!di{!di|!di}!di!O!di!`!di!a!di!b!di!m!di!n!di!o!di!p!di!q!di!r!dik!di~OY!di~P3gOVkOWlOXmOYnO]qO^rO[!dig!diq!dir!dis!dit!diu!div!diw!dix!diy!diz!di!T!di_!di!s!dia!di!P!di!Q!di!R!di!S!dih!dif!diQ!diR!diT!diU!di`!dib!dic!dii!dij!dil!din!di{!di|!di}!di!O!di!`!di!a!di!b!di!m!di!n!di!o!di!p!di!q!di!r!dik!di~OZ!di~P7VOZoO~P7VOYnO~P3gOVkOWlOXmOY!diZ!di[!di]!di^!dig!diq!dir!dis!dit!diu!div!diw!dix!diy!diz!di!T!di_!di!s!dia!di!P!di!Q!di!R!di!S!dih!dif!diQ!diR!diT!diU!di`!dib!dic!dii!dij!dil!din!di{!di|!di}!di!O!di!`!di!a!di!b!di!m!di!n!di!o!di!p!di!q!di!r!dik!di~O!P!Yi!Q!Yi!R!Yi!S!Yi!T!Yi_!Yia!Yik!Yih!Yi~P-fO_#YO~P-fOg#[O~Oh#]O~P-fOm#^O~Of#_O!s!qO~O_#_O~P-fOf#`O!s!qO~O!R!bO!S!cO!P!vi!T!vi_!via!vik!vih!vi~O!Q!aO~P@aO!Q!vi~P@aO!S!cO!P!vi!Q!vi!R!vi!T!vi_!via!vik!vih!vi~O_!]i!s!]ia!]if!]i~P-fOh#fO~P-fOf#gO!s!qO~OVkOWlOXmOYnOZoO[pO]qO^rO~PVOf!yi!s!yi~P-fOR#nO~Oh#oO~P-fO!P!`O!Q!aO!R!bO!S!cOa!kXk!kX~Oa#qOk#pO~Oh#rO~P-fO!P!`O!Q!aO!R!bO!S!cO!T!ty_!tya!tyk!tyh!ty~Oa#sO!P!`O!Q!aO!R!bO!S!cO~O_#yO~P-fOa#zO!P!`O!Q!aO!R!bO!S!cO~O!P!`O!Q!aO!R!bO!S!cOa!kik!ki~Oa#{O~P$lOh#|O!P!`O!Q!aO!R!bO!S!cO~Oa$OO~P-fOc!m|nTU!o!p!q!r!nQljij~',
|
||||
goto: "-l!zPPPP!{PPPPPPPPPPPPPPPPPPPPPPPPPP#wPPPPPPPPPPPPPPPPPPPPPP$s$y%s&T&q'u(Q(cPPP!{*a!{+_,Z+_+_+_-Y+_PPPPPPP%s-]%s-`-c-f!s`OTUdklmnopqrs{!O!S!V!W!Z![!`!a!b!c!q!s!v#O#T#V#[#]#^#b#f#n#o#p#r#|!sbOTUdklmnopqrs{!O!S!V!W!Z![!`!a!b!c!q!s!v#O#T#V#[#]#^#b#f#n#o#p#r#|QjOR#e#VSgO#VQxTQ!YdQ#P!`Q#Q!aQ#R!bQ#S!cQ#i#]Q#l#bQ#m#fQ#u#oQ#v#pR#x#rmhOTd!`!a!b!c#V#]#b#f#o#p#rlcOTd!`!a!b!c#V#]#b#f#o#p#rQ!^fQ!w!RQ!y!TR#a!}!U[Uklmnopqrs{!O!S!V!W!Z![!q!s!v#O#T#[#^#n#|l]OTd!`!a!b!c#V#]#b#f#o#p#rX!]f!R!T!}UvT{![X}U!V!Z!vUwT{![Q|UQ!z!VQ!|!ZR#Z!vSSO#VQtT[zU{!V!Z![!vd!Xd!`!a!b!c#]#b#f#o#pQ!gkQ!hlQ!imQ!jnQ!koQ!lpQ!mqQ!nrQ!osQ!u!OQ!x!SQ!{!WQ#W!qQ#X!sQ#b#OQ#c#TQ#h#[Q#k#^Q#t#nQ#w#rR#}#|!n`OUdklmnopqrs!O!S!V!W!Z![!`!a!b!c!q!s!v#O#T#V#[#]#^#b#f#n#o#p#r#|TuT{!s[OTUdklmnopqrs{!O!S!V!W!Z![!`!a!b!c!q!s!v#O#T#V#[#]#^#b#f#n#o#p#r#|!r[OTUdklmnopqrs{!O!S!V!W!Z![!`!a!b!c!q!s!v#O#T#V#[#]#^#b#f#n#o#p#r#|R!PVR#j#]R!_fRjOR!fiQ!eiR#d#U",
|
||||
nodeNames:
|
||||
'⚠ Expression Local ( Literal Global Radical + - * ∪ \\ ∆ ∩ × ) { } ℬ Filter ComplexIndex [ ] ∈ | PrefixD PrefixI ; PrefixR := Function TextFunction :∈ ∉ ⊆ ⊄ ⊂ > ≥ < ≤ ≠ = ¬ Predicate ∀ ∃ ⇔ ⇒ ∨ &',
|
||||
'⚠ Expression Local ( Literal Global Radical * + - ∪ \\ ∆ ∩ × ) { } ℬ Filter ComplexIndex [ ] ∈ | PrefixD PrefixI ; PrefixR := Function TextFunction :∈ ∉ ⊆ ⊄ ⊂ > ≥ < ≤ ≠ = ¬ Predicate ∀ ∃ ⇔ ⇒ ∨ &',
|
||||
maxTerm: 87,
|
||||
propSources: [highlighting],
|
||||
skippedNodes: [0],
|
||||
repeatNodeCount: 0,
|
||||
tokenData:
|
||||
"6P~R!jX^%spq%svw&hxy&myz&rz{&w{|&||}'R}!O'W!Q!R']!R[!h!i)d!k!l)z!r!s*P!t!u*g!u!v(|!v!w(|!z!{(|!|!}*w!}#O*|#O#P+R#P#Q+W#R#S+]#T#U+]#U#V+s#V#W-c#W#X/O#X#d+]#d#e1h#e#f+]#f#g2^#g#o+]#o#p3h#p#q3m#q#r3r#y#z%s$f$g%s$r$s3w%o%p3|5i6S+]#BY#BZ%s$IS$I_%s$I|$JO%s$JT$JU%s$KV$KW%s% l% m4R%%Y%%Z4W%%[%%]4]%&Y%&Z4b%&]%&^4g%&_%&`4l%&`%&a4q%&b%&c4v%&c%&d4{%'S%'T5Q%'T%'U5V%'U%'V5[%(^%(_5a%(b%(c5f%(c%(d5k%)Q%)R5p%)S%)T5u%)U%)V5z&FU&FV%s~%xY!V~X^%spq%s#y#z%s$f$g%s#BY#BZ%s$IS$I_%s$I|$JO%s$JT$JU%s$KV$KW%s&FU&FV%s~&mO!S~~&rOR~~&wO_~~&|OX~~'ROV~~'WO!s~~']OW~P'bP!`P!Q![']R'lQdQ!`P|}'r!Q!['eQ'uP!R!['xQ'}QdQ|}'r!Q!['x~(WQ!_!`(^%&b%&c(c~(cOm~~(hOp~~(mOk~~(rOw~~(wOz~~(|Ou~~)PP!Q![)S~)XPT~!Q![)S~)aPi~!Q![)S~)gQ!Q![)m#]#^)u~)rPn~!Q![)m~)zOc~~*POj~~*SQ!Q![*Y#f#g*b~*_P|~!Q![*Y~*gO!m~~*lPl~!Q![*o~*tPU~!Q![*o~*|O!b~~+ROe~~+WOZ~~+]Of~~+bRQ~!Q![+k#T#o+]5i6S+]~+pPQ~!Q![+k~+xTQ~!Q![+k#T#c+]#c#d,X#d#o+]5i6S+]~,^TQ~!Q![+k#T#c+]#c#d,m#d#o+]5i6S+]~,rTQ~!Q![+k#T#`+]#`#a-R#a#o+]5i6S+]~-YR!p~Q~!Q![+k#T#o+]5i6S+]~-hSQ~!Q![+k#T#U-t#U#o+]5i6S+]~-yTQ~!Q![+k#T#f+]#f#g.Y#g#o+]5i6S+]~._TQ~!Q![+k#T#W+]#W#X.n#X#o+]5i6S+]~.uR!o~Q~!Q![+k#T#o+]5i6S+]~/TTQ~!Q![+k#T#X+]#X#Y/d#Y#o+]5i6S+]~/iTQ~!Q![+k#T#U+]#U#V/x#V#o+]5i6S+]~/}TQ~!Q![+k#T#c+]#c#d0^#d#o+]5i6S+]~0cTQ~!Q![+k#T#c+]#c#d0r#d#o+]5i6S+]~0wTQ~!Q![+k#T#`+]#`#a1W#a#o+]5i6S+]~1_R!q~Q~!Q![+k#T#o+]5i6S+]~1mTQ~!Q![+k#T#f+]#f#g1|#g#o+]5i6S+]~2TR!n~Q~!Q![+k#T#o+]5i6S+]~2cTQ~!Q![+k#T#X+]#X#Y2r#Y#o+]5i6S+]~2wTQ~!Q![+k#T#W+]#W#X3W#X#o+]5i6S+]~3_R!r~Q~!Q![+k#T#o+]5i6S+]~3mO`~~3rOh~~3wOa~~3|O{~~4RO^~~4WOb~~4]O!Q~~4bO!P~~4gO}~~4lO!O~~4qO!a~~4vO[~~4{Og~~5QOq~~5VO!R~~5[O]~~5aOY~~5fOy~~5kOx~~5pOv~~5uOt~~5zOs~~6POr~",
|
||||
"6P~R!jX^%spq%svw&hxy&myz&rz{&w{|&||}'R}!O'W!Q!R']!R[!h!i)d!k!l)z!r!s*P!t!u*g!u!v(|!v!w(|!z!{(|!|!}*w!}#O*|#O#P+R#P#Q+W#R#S+]#T#U+]#U#V+s#V#W-c#W#X/O#X#d+]#d#e1h#e#f+]#f#g2^#g#o+]#o#p3h#p#q3m#q#r3r#y#z%s$f$g%s$r$s3w%o%p3|5i6S+]#BY#BZ%s$IS$I_%s$I|$JO%s$JT$JU%s$KV$KW%s% l% m4R%%Y%%Z4W%%[%%]4]%&Y%&Z4b%&]%&^4g%&_%&`4l%&`%&a4q%&b%&c4v%&c%&d4{%'S%'T5Q%'T%'U5V%'U%'V5[%(^%(_5a%(b%(c5f%(c%(d5k%)Q%)R5p%)S%)T5u%)U%)V5z&FU&FV%s~%xY!V~X^%spq%s#y#z%s$f$g%s#BY#BZ%s$IS$I_%s$I|$JO%s$JT$JU%s$KV$KW%s&FU&FV%s~&mO!S~~&rOR~~&wO_~~&|OV~~'ROW~~'WO!s~~']OX~P'bP!`P!Q![']R'lQdQ!`P|}'r!Q!['eQ'uP!R!['xQ'}QdQ|}'r!Q!['x~(WQ!_!`(^%&b%&c(c~(cOm~~(hOp~~(mOk~~(rOw~~(wOz~~(|Ou~~)PP!Q![)S~)XPT~!Q![)S~)aPi~!Q![)S~)gQ!Q![)m#]#^)u~)rPn~!Q![)m~)zOc~~*POj~~*SQ!Q![*Y#f#g*b~*_P|~!Q![*Y~*gO!m~~*lPl~!Q![*o~*tPU~!Q![*o~*|O!b~~+ROe~~+WOZ~~+]Of~~+bRQ~!Q![+k#T#o+]5i6S+]~+pPQ~!Q![+k~+xTQ~!Q![+k#T#c+]#c#d,X#d#o+]5i6S+]~,^TQ~!Q![+k#T#c+]#c#d,m#d#o+]5i6S+]~,rTQ~!Q![+k#T#`+]#`#a-R#a#o+]5i6S+]~-YR!p~Q~!Q![+k#T#o+]5i6S+]~-hSQ~!Q![+k#T#U-t#U#o+]5i6S+]~-yTQ~!Q![+k#T#f+]#f#g.Y#g#o+]5i6S+]~._TQ~!Q![+k#T#W+]#W#X.n#X#o+]5i6S+]~.uR!o~Q~!Q![+k#T#o+]5i6S+]~/TTQ~!Q![+k#T#X+]#X#Y/d#Y#o+]5i6S+]~/iTQ~!Q![+k#T#U+]#U#V/x#V#o+]5i6S+]~/}TQ~!Q![+k#T#c+]#c#d0^#d#o+]5i6S+]~0cTQ~!Q![+k#T#c+]#c#d0r#d#o+]5i6S+]~0wTQ~!Q![+k#T#`+]#`#a1W#a#o+]5i6S+]~1_R!q~Q~!Q![+k#T#o+]5i6S+]~1mTQ~!Q![+k#T#f+]#f#g1|#g#o+]5i6S+]~2TR!n~Q~!Q![+k#T#o+]5i6S+]~2cTQ~!Q![+k#T#X+]#X#Y2r#Y#o+]5i6S+]~2wTQ~!Q![+k#T#W+]#W#X3W#X#o+]5i6S+]~3_R!r~Q~!Q![+k#T#o+]5i6S+]~3mO`~~3rOh~~3wOa~~3|O{~~4RO^~~4WOb~~4]O!Q~~4bO!P~~4gO}~~4lO!O~~4qO!a~~4vO[~~4{Og~~5QOq~~5VO!R~~5[O]~~5aOY~~5fOy~~5kOx~~5pOv~~5uOt~~5zOs~~6POr~",
|
||||
tokenizers: [0, 1],
|
||||
topRules: { Expression: [0, 1] },
|
||||
tokenPrec: 1720
|
||||
tokenPrec: 1749
|
||||
});
|
||||
|
|
|
@ -7,16 +7,20 @@
|
|||
// ------------- Precedence Definitions --------------------
|
||||
///////////////////////////////////////////////////////////
|
||||
@precedence {
|
||||
plus @left minus @left,
|
||||
times @left,
|
||||
not @right,
|
||||
log_equiv @left,
|
||||
log_impl @left,
|
||||
log_or @left,
|
||||
log_and @left,
|
||||
log_or @left,
|
||||
log_impl @left,
|
||||
log_equiv @left,
|
||||
|
||||
times @left,
|
||||
plus @left minus @left,
|
||||
|
||||
set_decart @left set_union @left set_intersect @left set_minus @left set_symminus @left,
|
||||
set_bool @right,
|
||||
quant @right
|
||||
|
||||
quant @right,
|
||||
|
||||
not @right
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
|
@ -197,9 +201,9 @@ identifier {
|
|||
}
|
||||
|
||||
Setexpr_binary {
|
||||
Setexpr !times "*" Setexpr |
|
||||
Setexpr !plus "+" Setexpr |
|
||||
Setexpr !minus "-" Setexpr |
|
||||
Setexpr !times "*" Setexpr |
|
||||
Setexpr !set_union "∪" Setexpr |
|
||||
Setexpr !set_minus "\\" Setexpr |
|
||||
Setexpr !set_symminus "∆" Setexpr |
|
||||
|
|
|
@ -8,16 +8,20 @@
|
|||
// ------------- Precedence Definitions --------------------
|
||||
///////////////////////////////////////////////////////////
|
||||
@precedence {
|
||||
plus @left minus @left,
|
||||
times @left,
|
||||
not @right,
|
||||
log_equiv @left,
|
||||
log_impl @left,
|
||||
log_or @left,
|
||||
log_and @left,
|
||||
log_or @left,
|
||||
log_impl @left,
|
||||
log_equiv @left,
|
||||
|
||||
times @left,
|
||||
plus @left minus @left,
|
||||
|
||||
set_decart @left set_union @left set_intersect @left set_minus @left set_symminus @left,
|
||||
set_bool @right,
|
||||
quant @right
|
||||
|
||||
quant @right,
|
||||
|
||||
not @right
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
|
@ -195,9 +199,9 @@ identifier {
|
|||
}
|
||||
|
||||
setexpr_binary {
|
||||
setexpr !times "*" setexpr |
|
||||
setexpr !plus "+" setexpr |
|
||||
setexpr !minus "-" setexpr |
|
||||
setexpr !times "*" setexpr |
|
||||
setexpr !set_union "∪" setexpr |
|
||||
setexpr !set_minus "\\" setexpr |
|
||||
setexpr !set_symminus "∆" setexpr |
|
||||
|
|
|
@ -145,6 +145,15 @@ export class RSTextWrapper extends CodeMirrorWrapper {
|
|||
return true;
|
||||
}
|
||||
case TokenID.BOOLEAN: {
|
||||
if (!hasSelection && selection.from >= 2) {
|
||||
const enclosingText = this.ref.view.state.sliceDoc(selection.from - 2, selection.from + 1);
|
||||
if (enclosingText === 'ℬ()') {
|
||||
this.ref.view.dispatch({
|
||||
changes: [{ from: selection.from - 2, insert: 'ℬ' }]
|
||||
});
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (hasSelection && this.startsWithBoolean(selection)) {
|
||||
this.envelopeWith('ℬ', '');
|
||||
} else {
|
||||
|
@ -290,7 +299,7 @@ export class RSTextWrapper extends CodeMirrorWrapper {
|
|||
if (!text.startsWith('ℬ') || !text.endsWith(')')) {
|
||||
return false;
|
||||
}
|
||||
const openParenIndex = text.indexOf('(', 1); // start after 'ℬ'
|
||||
const openParenIndex = text.indexOf('(', 1);
|
||||
if (openParenIndex === -1) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
'use no memo'; // TODO: remove when react hook forms are compliant with react compiler
|
||||
'use client';
|
||||
|
||||
import { useMemo, useRef, useState } from 'react';
|
||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { Controller, useForm } from 'react-hook-form';
|
||||
import { toast } from 'react-toastify';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
|
@ -121,10 +121,12 @@ export function FormConstituenta({ disabled, id, toggleReset, schema, activeCst,
|
|||
}
|
||||
|
||||
const prevDirty = useRef(isDirty);
|
||||
if (prevDirty.current !== isDirty) {
|
||||
prevDirty.current = isDirty;
|
||||
setIsModified(isDirty);
|
||||
}
|
||||
useEffect(() => {
|
||||
if (prevDirty.current !== isDirty) {
|
||||
prevDirty.current = isDirty;
|
||||
setIsModified(isDirty);
|
||||
}
|
||||
}, [isDirty, setIsModified]);
|
||||
|
||||
function onSubmit(data: IUpdateConstituentaDTO) {
|
||||
return cstUpdate({ itemID: schema.id, data }).then(() => {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
'use no memo'; // TODO: remove when react hook forms are compliant with react compiler
|
||||
'use client';
|
||||
|
||||
import { useRef } from 'react';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { useForm, useWatch } from 'react-hook-form';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
|
||||
|
@ -70,10 +70,12 @@ export function FormRSForm() {
|
|||
}
|
||||
|
||||
const prevDirty = useRef(isDirty);
|
||||
if (prevDirty.current !== isDirty) {
|
||||
prevDirty.current = isDirty;
|
||||
setIsModified(isDirty);
|
||||
}
|
||||
useEffect(() => {
|
||||
if (prevDirty.current !== isDirty) {
|
||||
prevDirty.current = isDirty;
|
||||
setIsModified(isDirty);
|
||||
}
|
||||
}, [isDirty, setIsModified]);
|
||||
|
||||
function handleSelectVersion(version: CurrentVersion) {
|
||||
router.push({ path: urls.schema(schema.id, version === 'latest' ? undefined : version) });
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import { useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import clsx from 'clsx';
|
||||
import fileDownload from 'js-file-download';
|
||||
|
||||
import { MiniButton } from '@/components/control';
|
||||
|
@ -123,7 +124,7 @@ export function EditorRSList() {
|
|||
return false;
|
||||
}
|
||||
|
||||
const tableHeight = useFitHeight('4rem + 5px');
|
||||
const tableHeight = useFitHeight(isContentEditable ? '4rem + 5px' : '2rem');
|
||||
|
||||
return (
|
||||
<div tabIndex={-1} onKeyDown={handleKeyDown} className='relative pt-8'>
|
||||
|
@ -146,7 +147,7 @@ export function EditorRSList() {
|
|||
) : null}
|
||||
|
||||
<MiniButton
|
||||
className='absolute z-pop top-18 right-4 hidden sm:block'
|
||||
className={clsx('absolute z-pop right-4 hidden sm:block', isContentEditable ? 'top-18' : 'top-8')}
|
||||
title='Выгрузить в формате CSV'
|
||||
icon={<IconCSV size='1.25rem' className='icon-green' />}
|
||||
onClick={handleDownloadCSV}
|
||||
|
|
|
@ -5,41 +5,41 @@
|
|||
/**
|
||||
* Represents single node of a {@link Graph}, as implemented by storing outgoing and incoming connections.
|
||||
*/
|
||||
export class GraphNode {
|
||||
export class GraphNode<NodeID> {
|
||||
/** Unique identifier of the node. */
|
||||
id: number;
|
||||
id: NodeID;
|
||||
/** List of outgoing nodes. */
|
||||
outputs: number[];
|
||||
outputs: NodeID[];
|
||||
/** List of incoming nodes. */
|
||||
inputs: number[];
|
||||
inputs: NodeID[];
|
||||
|
||||
constructor(id: number) {
|
||||
constructor(id: NodeID) {
|
||||
this.id = id;
|
||||
this.outputs = [];
|
||||
this.inputs = [];
|
||||
}
|
||||
|
||||
clone(): GraphNode {
|
||||
clone(): GraphNode<NodeID> {
|
||||
const result = new GraphNode(this.id);
|
||||
result.outputs = [...this.outputs];
|
||||
result.inputs = [...this.inputs];
|
||||
return result;
|
||||
}
|
||||
|
||||
addOutput(node: number): void {
|
||||
addOutput(node: NodeID): void {
|
||||
this.outputs.push(node);
|
||||
}
|
||||
|
||||
addInput(node: number): void {
|
||||
addInput(node: NodeID): void {
|
||||
this.inputs.push(node);
|
||||
}
|
||||
|
||||
removeInput(target: number): number | null {
|
||||
removeInput(target: NodeID): NodeID | null {
|
||||
const index = this.inputs.findIndex(node => node === target);
|
||||
return index > -1 ? this.inputs.splice(index, 1)[0] : null;
|
||||
}
|
||||
|
||||
removeOutput(target: number): number | null {
|
||||
removeOutput(target: NodeID): NodeID | null {
|
||||
const index = this.outputs.findIndex(node => node === target);
|
||||
return index > -1 ? this.outputs.splice(index, 1)[0] : null;
|
||||
}
|
||||
|
@ -50,11 +50,11 @@ export class GraphNode {
|
|||
*
|
||||
* This class is optimized for TermGraph use case and not supposed to be used as generic graph implementation.
|
||||
*/
|
||||
export class Graph {
|
||||
export class Graph<NodeID = number> {
|
||||
/** Map of nodes. */
|
||||
nodes = new Map<number, GraphNode>();
|
||||
nodes = new Map<NodeID, GraphNode<NodeID>>();
|
||||
|
||||
constructor(arr?: number[][]) {
|
||||
constructor(arr?: NodeID[][]) {
|
||||
if (!arr) {
|
||||
return;
|
||||
}
|
||||
|
@ -67,17 +67,17 @@ export class Graph {
|
|||
});
|
||||
}
|
||||
|
||||
clone(): Graph {
|
||||
const result = new Graph();
|
||||
clone(): Graph<NodeID> {
|
||||
const result = new Graph<NodeID>();
|
||||
this.nodes.forEach(node => result.nodes.set(node.id, node.clone()));
|
||||
return result;
|
||||
}
|
||||
|
||||
at(target: number): GraphNode | undefined {
|
||||
at(target: NodeID): GraphNode<NodeID> | undefined {
|
||||
return this.nodes.get(target);
|
||||
}
|
||||
|
||||
addNode(target: number): GraphNode {
|
||||
addNode(target: NodeID): GraphNode<NodeID> {
|
||||
let node = this.nodes.get(target);
|
||||
if (!node) {
|
||||
node = new GraphNode(target);
|
||||
|
@ -86,11 +86,11 @@ export class Graph {
|
|||
return node;
|
||||
}
|
||||
|
||||
hasNode(target: number): boolean {
|
||||
hasNode(target: NodeID): boolean {
|
||||
return !!this.nodes.get(target);
|
||||
}
|
||||
|
||||
removeNode(target: number): void {
|
||||
removeNode(target: NodeID): void {
|
||||
this.nodes.forEach(node => {
|
||||
node.removeInput(target);
|
||||
node.removeOutput(target);
|
||||
|
@ -98,7 +98,7 @@ export class Graph {
|
|||
this.nodes.delete(target);
|
||||
}
|
||||
|
||||
foldNode(target: number): void {
|
||||
foldNode(target: NodeID): void {
|
||||
const nodeToRemove = this.nodes.get(target);
|
||||
if (!nodeToRemove) {
|
||||
return;
|
||||
|
@ -111,8 +111,8 @@ export class Graph {
|
|||
this.removeNode(target);
|
||||
}
|
||||
|
||||
removeIsolated(): GraphNode[] {
|
||||
const result: GraphNode[] = [];
|
||||
removeIsolated(): GraphNode<NodeID>[] {
|
||||
const result: GraphNode<NodeID>[] = [];
|
||||
this.nodes.forEach(node => {
|
||||
if (node.outputs.length === 0 && node.inputs.length === 0) {
|
||||
result.push(node);
|
||||
|
@ -122,7 +122,7 @@ export class Graph {
|
|||
return result;
|
||||
}
|
||||
|
||||
addEdge(source: number, destination: number): void {
|
||||
addEdge(source: NodeID, destination: NodeID): void {
|
||||
if (this.hasEdge(source, destination)) {
|
||||
return;
|
||||
}
|
||||
|
@ -132,7 +132,7 @@ export class Graph {
|
|||
destinationNode.addInput(sourceNode.id);
|
||||
}
|
||||
|
||||
removeEdge(source: number, destination: number): void {
|
||||
removeEdge(source: NodeID, destination: NodeID): void {
|
||||
const sourceNode = this.nodes.get(source);
|
||||
const destinationNode = this.nodes.get(destination);
|
||||
if (sourceNode && destinationNode) {
|
||||
|
@ -141,7 +141,7 @@ export class Graph {
|
|||
}
|
||||
}
|
||||
|
||||
hasEdge(source: number, destination: number): boolean {
|
||||
hasEdge(source: NodeID, destination: NodeID): boolean {
|
||||
const sourceNode = this.nodes.get(source);
|
||||
if (!sourceNode) {
|
||||
return false;
|
||||
|
@ -149,8 +149,8 @@ export class Graph {
|
|||
return !!sourceNode.outputs.find(id => id === destination);
|
||||
}
|
||||
|
||||
expandOutputs(origin: number[]): number[] {
|
||||
const result: number[] = [];
|
||||
expandOutputs(origin: NodeID[]): NodeID[] {
|
||||
const result: NodeID[] = [];
|
||||
origin.forEach(id => {
|
||||
const node = this.nodes.get(id);
|
||||
if (node) {
|
||||
|
@ -164,8 +164,8 @@ export class Graph {
|
|||
return result;
|
||||
}
|
||||
|
||||
expandInputs(origin: number[]): number[] {
|
||||
const result: number[] = [];
|
||||
expandInputs(origin: NodeID[]): NodeID[] {
|
||||
const result: NodeID[] = [];
|
||||
origin.forEach(id => {
|
||||
const node = this.nodes.get(id);
|
||||
if (node) {
|
||||
|
@ -179,13 +179,13 @@ export class Graph {
|
|||
return result;
|
||||
}
|
||||
|
||||
expandAllOutputs(origin: number[]): number[] {
|
||||
const result: number[] = this.expandOutputs(origin);
|
||||
expandAllOutputs(origin: NodeID[]): NodeID[] {
|
||||
const result: NodeID[] = this.expandOutputs(origin);
|
||||
if (result.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const marked = new Map<number, boolean>();
|
||||
const marked = new Map<NodeID, boolean>();
|
||||
origin.forEach(id => marked.set(id, true));
|
||||
let position = 0;
|
||||
while (position < result.length) {
|
||||
|
@ -203,13 +203,13 @@ export class Graph {
|
|||
return result;
|
||||
}
|
||||
|
||||
expandAllInputs(origin: number[]): number[] {
|
||||
const result: number[] = this.expandInputs(origin);
|
||||
expandAllInputs(origin: NodeID[]): NodeID[] {
|
||||
const result: NodeID[] = this.expandInputs(origin);
|
||||
if (result.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const marked = new Map<number, boolean>();
|
||||
const marked = new Map<NodeID, boolean>();
|
||||
origin.forEach(id => marked.set(id, true));
|
||||
let position = 0;
|
||||
while (position < result.length) {
|
||||
|
@ -227,8 +227,8 @@ export class Graph {
|
|||
return result;
|
||||
}
|
||||
|
||||
maximizePart(origin: number[]): number[] {
|
||||
const outputs: number[] = this.expandAllOutputs(origin);
|
||||
maximizePart(origin: NodeID[]): NodeID[] {
|
||||
const outputs: NodeID[] = this.expandAllOutputs(origin);
|
||||
const result = [...origin];
|
||||
this.topologicalOrder()
|
||||
.filter(id => outputs.includes(id))
|
||||
|
@ -241,10 +241,10 @@ export class Graph {
|
|||
return result;
|
||||
}
|
||||
|
||||
topologicalOrder(): number[] {
|
||||
const result: number[] = [];
|
||||
const marked = new Set<number>();
|
||||
const nodeStack: number[] = [];
|
||||
topologicalOrder(): NodeID[] {
|
||||
const result: NodeID[] = [];
|
||||
const marked = new Set<NodeID>();
|
||||
const nodeStack: NodeID[] = [];
|
||||
this.nodes.forEach(node => {
|
||||
if (marked.has(node.id)) {
|
||||
return;
|
||||
|
@ -275,12 +275,12 @@ export class Graph {
|
|||
|
||||
transitiveReduction() {
|
||||
const order = this.topologicalOrder();
|
||||
const marked = new Map<number, boolean>();
|
||||
const marked = new Map<NodeID, boolean>();
|
||||
order.forEach(nodeID => {
|
||||
if (marked.get(nodeID)) {
|
||||
return;
|
||||
}
|
||||
const stack: { id: number; parents: number[] }[] = [];
|
||||
const stack: { id: NodeID; parents: NodeID[] }[] = [];
|
||||
stack.push({ id: nodeID, parents: [] });
|
||||
while (stack.length > 0) {
|
||||
const item = stack.splice(0, 1)[0];
|
||||
|
@ -299,20 +299,20 @@ export class Graph {
|
|||
/**
|
||||
* Finds a cycle in the graph.
|
||||
*
|
||||
* @returns {number[] | null} The cycle if found, otherwise `null`.
|
||||
* @returns {NodeID[] | null} The cycle if found, otherwise `null`.
|
||||
* Uses non-recursive DFS.
|
||||
*/
|
||||
findCycle(): number[] | null {
|
||||
const visited = new Set<number>();
|
||||
const nodeStack = new Set<number>();
|
||||
const parents = new Map<number, number>();
|
||||
findCycle(): NodeID[] | null {
|
||||
const visited = new Set<NodeID>();
|
||||
const nodeStack = new Set<NodeID>();
|
||||
const parents = new Map<NodeID, NodeID>();
|
||||
|
||||
for (const nodeId of this.nodes.keys()) {
|
||||
if (visited.has(nodeId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const callStack: { nodeId: number; parentId: number | null }[] = [];
|
||||
const callStack: { nodeId: NodeID; parentId: NodeID | null }[] = [];
|
||||
callStack.push({ nodeId: nodeId, parentId: null });
|
||||
while (callStack.length > 0) {
|
||||
const { nodeId, parentId } = callStack[callStack.length - 1];
|
||||
|
@ -336,7 +336,7 @@ export class Graph {
|
|||
if (!nodeStack.has(child)) {
|
||||
continue;
|
||||
}
|
||||
const cycle: number[] = [];
|
||||
const cycle: NodeID[] = [];
|
||||
let current = nodeId;
|
||||
cycle.push(child);
|
||||
while (current !== child) {
|
||||
|
|
Loading…
Reference in New Issue
Block a user