Compare commits
11 Commits
168ff94c42
...
d3e9e0eadd
Author | SHA1 | Date | |
---|---|---|---|
![]() |
d3e9e0eadd | ||
![]() |
296e740cc7 | ||
![]() |
5d00a10a58 | ||
![]() |
fe678de8e4 | ||
![]() |
6d84b89b14 | ||
![]() |
5ca7f4d057 | ||
![]() |
6e40aa06de | ||
![]() |
1b662ad39f | ||
![]() |
0302a99344 | ||
![]() |
1078a271fb | ||
![]() |
7100738706 |
|
@ -9,6 +9,7 @@
|
|||
|
||||
[](https://github.com/IRBorisov/ConceptPortal/actions/workflows/backend.yml)
|
||||
[](https://github.com/IRBorisov/ConceptPortal/actions/workflows/frontend.yml)
|
||||
[](https://portal.acconcept.ru)
|
||||
|
||||
React + Django based web portal for editing RSForm schemas.
|
||||
This readme file is used mostly to document project dependencies and conventions.
|
||||
|
|
8
TODO.txt
8
TODO.txt
|
@ -9,7 +9,9 @@ For more specific TODOs see comments in code
|
|||
- Design first user experience
|
||||
- Demo sandbox for anonymous users
|
||||
|
||||
- User profile: Settings + settings persistency
|
||||
- User profile: Settings + settings server persistency
|
||||
- Profile pictures
|
||||
- Integrate socials and feedback
|
||||
- Custom LibraryItem lists
|
||||
- Custom user filters and sharing filters
|
||||
|
||||
|
@ -19,6 +21,8 @@ For more specific TODOs see comments in code
|
|||
- Focus on codemirror editor when label is clicked (need React 19 ref for clean code solution)
|
||||
- Draggable rows in constituents table
|
||||
|
||||
- M-graph visualization for typification and RSForm in general
|
||||
|
||||
- replace reagraph with react-flow in TermGraph and FormulaGraph
|
||||
- Search functionality for Help Manuals
|
||||
- Export PDF (Items list, Graph)
|
||||
|
@ -31,6 +35,8 @@ For more specific TODOs see comments in code
|
|||
- Content based search in Library
|
||||
- Home page (user specific)
|
||||
- Private projects. Consider cooperative editing
|
||||
- OSS: synthesis table: auto substitution for diamond synthesis
|
||||
|
||||
|
||||
[Tech]
|
||||
- duplicate syntax parsing and type info calculations to client. Consider moving backend to Nodejs or embedding c++ lib
|
||||
|
|
|
@ -84,7 +84,7 @@ class TestVersionViews(EndpointTester):
|
|||
alias='A1',
|
||||
cst_type='axiom',
|
||||
definition_formal='X1=X1',
|
||||
order=2
|
||||
order=1
|
||||
)
|
||||
version_id = self._create_version({'version': '1.0.0', 'description': 'test'})
|
||||
a1.definition_formal = 'X1=X2'
|
||||
|
@ -163,7 +163,7 @@ class TestVersionViews(EndpointTester):
|
|||
x1.convention = 'Test2'
|
||||
x1.term_raw = 'Test'
|
||||
x1.save()
|
||||
x3.order = 1
|
||||
x3.order = 0
|
||||
x3.save()
|
||||
|
||||
self.executeNotFound(version=invalid_id)
|
||||
|
@ -172,10 +172,10 @@ class TestVersionViews(EndpointTester):
|
|||
x1.refresh_from_db()
|
||||
x2.refresh_from_db()
|
||||
self.assertEqual(len(response.data['items']), 3)
|
||||
self.assertEqual(x1.order, 1)
|
||||
self.assertEqual(x1.order, 0)
|
||||
self.assertEqual(x1.convention, 'testStart')
|
||||
self.assertEqual(x1.term_raw, '')
|
||||
self.assertEqual(x2.order, 2)
|
||||
self.assertEqual(x2.order, 1)
|
||||
self.assertEqual(response.data['items'][2]['alias'], 'D1')
|
||||
self.assertEqual(response.data['items'][2]['term_raw'], 'TestTerm')
|
||||
|
||||
|
|
|
@ -28,8 +28,6 @@ from .. import serializers as s
|
|||
class LibraryViewSet(viewsets.ModelViewSet):
|
||||
''' Endpoint: Library operations. '''
|
||||
queryset = m.LibraryItem.objects.all()
|
||||
# TODO: consider using .only() for performance
|
||||
|
||||
ordering = '-time_update'
|
||||
|
||||
def get_serializer_class(self):
|
||||
|
|
|
@ -244,7 +244,6 @@ class OperationSchema:
|
|||
original = children[sub.original.pk]
|
||||
replacement = children[sub.substitution.pk]
|
||||
translated_substitutions.append((original, replacement))
|
||||
# TODO: add auto substitutes for diamond
|
||||
receiver.substitute(translated_substitutions)
|
||||
|
||||
for cst in receiver.constituents().order_by('order'):
|
||||
|
@ -258,6 +257,7 @@ class OperationSchema:
|
|||
|
||||
receiver.restore_order()
|
||||
receiver.reset_aliases()
|
||||
receiver.resolve_all_text()
|
||||
|
||||
if len(self.cache.graph.outputs[operation.pk]) > 0:
|
||||
self.after_create_cst(receiver, list(receiver.constituents().order_by('order')))
|
||||
|
@ -366,12 +366,10 @@ class OperationSchema:
|
|||
if destination is None:
|
||||
return
|
||||
|
||||
# TODO: update substitutions for diamond synthesis (if needed)
|
||||
|
||||
self.cache.ensure_loaded()
|
||||
new_mapping = self._transform_mapping(mapping, operation, destination)
|
||||
alias_mapping = OperationSchema._produce_alias_mapping(new_mapping)
|
||||
insert_where = self._determine_insert_position(items[0], operation, source, destination)
|
||||
insert_where = self._determine_insert_position(items[0].pk, operation, source, destination)
|
||||
new_cst_list = destination.insert_copy(items, insert_where, alias_mapping)
|
||||
for index, cst in enumerate(new_cst_list):
|
||||
new_inheritance = Inheritance.objects.create(
|
||||
|
@ -529,20 +527,23 @@ class OperationSchema:
|
|||
return result
|
||||
|
||||
def _determine_insert_position(
|
||||
self, prototype: Constituenta,
|
||||
self, prototype_id: int,
|
||||
operation: Operation,
|
||||
source: RSForm,
|
||||
destination: RSForm
|
||||
) -> int:
|
||||
''' Determine insert_after for new constituenta. '''
|
||||
if prototype.order == 1:
|
||||
return 1
|
||||
prev_cst = source.cache.constituents[prototype.order - 2]
|
||||
prototype = source.cache.by_id[prototype_id]
|
||||
prototype_index = source.cache.constituents.index(prototype)
|
||||
if prototype_index == 0:
|
||||
return 0
|
||||
prev_cst = source.cache.constituents[prototype_index - 1]
|
||||
inherited_prev_id = self.cache.get_successor(prev_cst.pk, operation.pk)
|
||||
if inherited_prev_id is None:
|
||||
return INSERT_LAST
|
||||
prev_cst = destination.cache.by_id[inherited_prev_id]
|
||||
return cast(int, prev_cst.order) + 1
|
||||
prev_index = destination.cache.constituents.index(prev_cst)
|
||||
return prev_index + 1
|
||||
|
||||
def _extract_data_references(self, data: dict, old_data: dict) -> set[str]:
|
||||
result: set[str] = set()
|
||||
|
|
|
@ -70,7 +70,7 @@ class TestChangeConstituents(EndpointTester):
|
|||
self.assertEqual(self.ks1.constituents().count(), 3)
|
||||
self.assertEqual(self.ks3.constituents().count(), 5)
|
||||
self.assertEqual(inherited_cst.alias, 'X4')
|
||||
self.assertEqual(inherited_cst.order, 3)
|
||||
self.assertEqual(inherited_cst.order, 2)
|
||||
self.assertEqual(inherited_cst.definition_formal, 'X1 = X2')
|
||||
|
||||
@decl_endpoint('/api/rsforms/{schema}/rename-cst', method='patch')
|
||||
|
@ -133,5 +133,5 @@ class TestChangeConstituents(EndpointTester):
|
|||
d2.refresh_from_db()
|
||||
self.assertEqual(self.ks1.constituents().count(), 1)
|
||||
self.assertEqual(self.ks3.constituents().count(), 4)
|
||||
self.assertEqual(self.ks1X2.order, 1)
|
||||
self.assertEqual(self.ks1X2.order, 0)
|
||||
self.assertEqual(d2.definition_formal, r'X2\X2\X3')
|
||||
|
|
|
@ -367,7 +367,6 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
|
|||
@action(detail=False, methods=['post'], url_path='get-predecessor')
|
||||
def get_predecessor(self, request: Request) -> HttpResponse:
|
||||
''' Get predecessor. '''
|
||||
# TODO: add tests for this method
|
||||
serializer = CstTargetSerializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
cst = cast(Constituenta, serializer.validated_data['target'])
|
||||
|
|
|
@ -7,7 +7,8 @@ from . import models
|
|||
class ConstituentaAdmin(admin.ModelAdmin):
|
||||
''' Admin model: Constituenta. '''
|
||||
ordering = ['schema', 'order']
|
||||
list_display = ['schema', 'alias', 'term_resolved', 'definition_resolved']
|
||||
list_display = ['schema', 'order', 'alias', 'term_resolved', 'definition_resolved']
|
||||
search_fields = ['term_resolved', 'definition_resolved']
|
||||
|
||||
|
||||
admin.site.register(models.Constituenta, ConstituentaAdmin)
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
# Generated by Django 5.1 on 2024-09-11 16:37
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
def migrate_order_from_1_to_0(apps, schema_editor):
|
||||
Constituenta = apps.get_model('rsform', 'Constituenta')
|
||||
for cst in Constituenta.objects.all():
|
||||
cst.order = max(0, cst.order - 1)
|
||||
cst.save()
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('rsform', '0002_alter_constituenta_order'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='constituenta',
|
||||
name='order',
|
||||
field=models.PositiveIntegerField(default=0, verbose_name='Позиция'),
|
||||
),
|
||||
migrations.RunPython(migrate_order_from_1_to_0),
|
||||
]
|
|
@ -2,7 +2,6 @@
|
|||
import re
|
||||
|
||||
from cctext import extract_entities
|
||||
from django.core.validators import MinValueValidator
|
||||
from django.db.models import (
|
||||
CASCADE,
|
||||
CharField,
|
||||
|
@ -57,8 +56,7 @@ class Constituenta(Model):
|
|||
)
|
||||
order: PositiveIntegerField = PositiveIntegerField(
|
||||
verbose_name='Позиция',
|
||||
validators=[MinValueValidator(1)],
|
||||
default=1,
|
||||
default=0,
|
||||
)
|
||||
alias: CharField = CharField(
|
||||
verbose_name='Имя',
|
||||
|
|
|
@ -140,7 +140,8 @@ class RSForm:
|
|||
if insert_after is None:
|
||||
position = INSERT_LAST
|
||||
else:
|
||||
position = insert_after.order + 1
|
||||
self.cache.ensure_loaded()
|
||||
position = self.cache.constituents.index(self.cache.by_id[insert_after.pk]) + 1
|
||||
result = self.insert_new(data['alias'], data['cst_type'], position)
|
||||
result.convention = data.get('convention', '')
|
||||
result.definition_formal = data.get('definition_formal', '')
|
||||
|
@ -170,8 +171,7 @@ class RSForm:
|
|||
position: int = INSERT_LAST,
|
||||
**kwargs
|
||||
) -> Constituenta:
|
||||
''' Insert new constituenta at given position.
|
||||
All following constituents order is shifted by 1 position. '''
|
||||
''' Insert new constituenta at given position. '''
|
||||
if self.constituents().filter(alias=alias).exists():
|
||||
raise ValidationError(msg.aliasTaken(alias))
|
||||
position = self._get_insert_position(position)
|
||||
|
@ -298,8 +298,8 @@ class RSForm:
|
|||
if cst in target:
|
||||
cst.order = destination + count_moved
|
||||
count_moved += 1
|
||||
elif count_top + 1 < destination:
|
||||
cst.order = count_top + 1
|
||||
elif count_top < destination:
|
||||
cst.order = count_top
|
||||
count_top += 1
|
||||
else:
|
||||
cst.order = destination + size + count_bot
|
||||
|
@ -417,8 +417,8 @@ class RSForm:
|
|||
if count_new == 0:
|
||||
return []
|
||||
|
||||
position = target.order + 1
|
||||
self.cache.ensure_loaded()
|
||||
position = self.cache.constituents.index(self.cache.by_id[target.id]) + 1
|
||||
self._shift_positions(position, count_new)
|
||||
result = []
|
||||
cst_type = CstType.TERM if len(parse['args']) == 0 else CstType.FUNCTION
|
||||
|
@ -456,29 +456,23 @@ class RSForm:
|
|||
def _shift_positions(self, start: int, shift: int) -> None:
|
||||
if shift == 0:
|
||||
return
|
||||
update_list: Iterable[Constituenta] = []
|
||||
if not self.cache.is_loaded:
|
||||
update_list = Constituenta.objects \
|
||||
.only('order') \
|
||||
.filter(schema=self.model, order__gte=start)
|
||||
else:
|
||||
update_list = [cst for cst in self.cache.constituents if cst.order >= start]
|
||||
self.cache.ensure_loaded()
|
||||
update_list = self.cache.constituents[start:]
|
||||
for cst in update_list:
|
||||
cst.order += shift
|
||||
Constituenta.objects.bulk_update(update_list, ['order'])
|
||||
|
||||
def _get_insert_position(self, position: int) -> int:
|
||||
if position <= 0 and position != INSERT_LAST:
|
||||
if position < 0 and position != INSERT_LAST:
|
||||
raise ValidationError(msg.invalidPosition())
|
||||
lastPosition = self.constituents().count()
|
||||
if position == INSERT_LAST:
|
||||
position = lastPosition + 1
|
||||
return lastPosition
|
||||
else:
|
||||
position = max(1, min(position, lastPosition + 1))
|
||||
return position
|
||||
return max(0, min(position, lastPosition))
|
||||
|
||||
def _reset_order(self) -> None:
|
||||
order = 1
|
||||
order = 0
|
||||
changed: list[Constituenta] = []
|
||||
cst_list: Iterable[Constituenta] = []
|
||||
if not self.cache.is_loaded:
|
||||
|
@ -569,14 +563,14 @@ class RSFormCache:
|
|||
|
||||
def insert(self, cst: Constituenta) -> None:
|
||||
if self.is_loaded:
|
||||
self.constituents.insert(cst.order - 1, cst)
|
||||
self.constituents.insert(cst.order, cst)
|
||||
self.by_id[cst.pk] = cst
|
||||
self.by_alias[cst.alias] = cst
|
||||
|
||||
def insert_multi(self, items: Iterable[Constituenta]) -> None:
|
||||
if self.is_loaded:
|
||||
for cst in items:
|
||||
self.constituents.insert(cst.order - 1, cst)
|
||||
self.constituents.insert(cst.order, cst)
|
||||
self.by_id[cst.pk] = cst
|
||||
self.by_alias[cst.alias] = cst
|
||||
|
||||
|
@ -770,7 +764,7 @@ class _OrderManager:
|
|||
self._items = result
|
||||
|
||||
def _save_order(self) -> None:
|
||||
order = 1
|
||||
order = 0
|
||||
for cst in self._items:
|
||||
cst.order = order
|
||||
order += 1
|
||||
|
|
|
@ -26,7 +26,7 @@ class CstBaseSerializer(serializers.ModelSerializer):
|
|||
class Meta:
|
||||
''' serializer metadata. '''
|
||||
model = Constituenta
|
||||
fields = '__all__'
|
||||
exclude = ('order',)
|
||||
read_only_fields = ('id',)
|
||||
|
||||
|
||||
|
@ -35,8 +35,8 @@ class CstSerializer(serializers.ModelSerializer):
|
|||
class Meta:
|
||||
''' serializer metadata. '''
|
||||
model = Constituenta
|
||||
fields = '__all__'
|
||||
read_only_fields = ('id', 'schema', 'order', 'alias', 'cst_type', 'definition_resolved', 'term_resolved')
|
||||
exclude = ('order',)
|
||||
read_only_fields = ('id', 'schema', 'alias', 'cst_type', 'definition_resolved', 'term_resolved')
|
||||
|
||||
|
||||
class CstUpdateSerializer(serializers.Serializer):
|
||||
|
@ -71,7 +71,7 @@ class CstDetailsSerializer(serializers.ModelSerializer):
|
|||
class Meta:
|
||||
''' serializer metadata. '''
|
||||
model = Constituenta
|
||||
fields = '__all__'
|
||||
exclude = ('order',)
|
||||
|
||||
|
||||
class CstCreateSerializer(serializers.ModelSerializer):
|
||||
|
@ -126,7 +126,7 @@ class RSFormSerializer(serializers.ModelSerializer):
|
|||
result['items'] = []
|
||||
result['oss'] = []
|
||||
result['inheritance'] = []
|
||||
for cst in RSForm(instance).constituents().order_by('order'):
|
||||
for cst in RSForm(instance).constituents().defer('order').order_by('order'):
|
||||
result['items'].append(CstSerializer(cst).data)
|
||||
for oss in LibraryItem.objects.filter(operations__result=instance).only('alias'):
|
||||
result['oss'].append({
|
||||
|
@ -171,6 +171,7 @@ class RSFormSerializer(serializers.ModelSerializer):
|
|||
cst_data = next(x for x in items if x['id'] == cst.pk)
|
||||
new_cst = CstBaseSerializer(data=cst_data)
|
||||
new_cst.is_valid(raise_exception=True)
|
||||
new_cst.validated_data['order'] = ids.index(cst.pk)
|
||||
new_cst.update(
|
||||
instance=cst,
|
||||
validated_data=new_cst.validated_data
|
||||
|
@ -180,9 +181,11 @@ class RSFormSerializer(serializers.ModelSerializer):
|
|||
for cst_data in items:
|
||||
if cst_data['id'] not in processed:
|
||||
cst = schema.insert_new(cst_data['alias'])
|
||||
old_id = cst_data['id']
|
||||
cst_data['id'] = cst.pk
|
||||
new_cst = CstBaseSerializer(data=cst_data)
|
||||
new_cst.is_valid(raise_exception=True)
|
||||
new_cst.validated_data['order'] = ids.index(old_id)
|
||||
new_cst.update(
|
||||
instance=cst,
|
||||
validated_data=new_cst.validated_data
|
||||
|
|
|
@ -151,7 +151,7 @@ class RSFormTRSSerializer(serializers.Serializer):
|
|||
location=validated_data['location']
|
||||
)
|
||||
self.instance.save()
|
||||
order = 1
|
||||
order = 0
|
||||
for cst_data in validated_data['items']:
|
||||
cst = Constituenta(
|
||||
alias=cst_data['alias'],
|
||||
|
@ -174,7 +174,7 @@ class RSFormTRSSerializer(serializers.Serializer):
|
|||
if 'comment' in validated_data:
|
||||
instance.model.comment = validated_data['comment']
|
||||
|
||||
order = 1
|
||||
order = 0
|
||||
prev_constituents = instance.constituents()
|
||||
loaded_ids = set()
|
||||
for cst_data in validated_data['items']:
|
||||
|
|
|
@ -16,7 +16,7 @@ class TestConstituenta(TestCase):
|
|||
|
||||
def test_str(self):
|
||||
testStr = 'X1'
|
||||
cst = Constituenta.objects.create(alias=testStr, schema=self.schema1.model, order=1, convention='Test')
|
||||
cst = Constituenta.objects.create(alias=testStr, schema=self.schema1.model, order=0, convention='Test')
|
||||
self.assertEqual(str(cst), testStr)
|
||||
|
||||
|
||||
|
@ -25,25 +25,18 @@ class TestConstituenta(TestCase):
|
|||
Constituenta.objects.create(alias='X1', schema=self.schema1.model, order=-1)
|
||||
|
||||
|
||||
def test_order_min_value(self):
|
||||
with self.assertRaises(ValidationError):
|
||||
cst = Constituenta.objects.create(alias='X1', schema=self.schema1.model, order=0)
|
||||
cst.full_clean()
|
||||
|
||||
|
||||
def test_schema_not_null(self):
|
||||
with self.assertRaises(IntegrityError):
|
||||
Constituenta.objects.create(alias='X1', order=1)
|
||||
Constituenta.objects.create(alias='X1', order=0)
|
||||
|
||||
|
||||
def test_create_default(self):
|
||||
cst = Constituenta.objects.create(
|
||||
alias='X1',
|
||||
schema=self.schema1.model,
|
||||
order=1
|
||||
schema=self.schema1.model
|
||||
)
|
||||
self.assertEqual(cst.schema, self.schema1.model)
|
||||
self.assertEqual(cst.order, 1)
|
||||
self.assertEqual(cst.order, 0)
|
||||
self.assertEqual(cst.alias, 'X1')
|
||||
self.assertEqual(cst.cst_type, CstType.BASE)
|
||||
self.assertEqual(cst.convention, '')
|
||||
|
@ -57,7 +50,6 @@ class TestConstituenta(TestCase):
|
|||
def test_extract_references(self):
|
||||
cst = Constituenta.objects.create(
|
||||
alias='X1',
|
||||
order=1,
|
||||
schema=self.schema1.model,
|
||||
definition_formal='X1 X2',
|
||||
term_raw='@{X3|sing} is a @{X4|sing}',
|
||||
|
@ -68,7 +60,6 @@ class TestConstituenta(TestCase):
|
|||
def text_apply_mapping(self):
|
||||
cst = Constituenta.objects.create(
|
||||
alias='X1',
|
||||
order=1,
|
||||
schema=self.schema1.model,
|
||||
definition_formal='X1 = X2',
|
||||
term_raw='@{X1|sing}',
|
||||
|
|
|
@ -23,8 +23,8 @@ class TestRSForm(DBTester):
|
|||
self.assertFalse(schema1.constituents().exists())
|
||||
self.assertFalse(schema2.constituents().exists())
|
||||
|
||||
Constituenta.objects.create(alias='X1', schema=schema1.model, order=1)
|
||||
Constituenta.objects.create(alias='X2', schema=schema1.model, order=2)
|
||||
Constituenta.objects.create(alias='X1', schema=schema1.model, order=0)
|
||||
Constituenta.objects.create(alias='X2', schema=schema1.model, order=1)
|
||||
self.assertTrue(schema1.constituents().exists())
|
||||
self.assertFalse(schema2.constituents().exists())
|
||||
self.assertEqual(schema1.constituents().count(), 2)
|
||||
|
@ -32,8 +32,8 @@ class TestRSForm(DBTester):
|
|||
|
||||
def test_get_max_index(self):
|
||||
schema1 = RSForm.create(title='Test1')
|
||||
Constituenta.objects.create(alias='X1', schema=schema1.model, order=1)
|
||||
Constituenta.objects.create(alias='D2', cst_type=CstType.TERM, schema=schema1.model, order=2)
|
||||
Constituenta.objects.create(alias='X1', schema=schema1.model, order=0)
|
||||
Constituenta.objects.create(alias='D2', cst_type=CstType.TERM, schema=schema1.model, order=1)
|
||||
self.assertEqual(schema1.get_max_index(CstType.BASE), 1)
|
||||
self.assertEqual(schema1.get_max_index(CstType.TERM), 2)
|
||||
self.assertEqual(schema1.get_max_index(CstType.AXIOM), 0)
|
||||
|
@ -42,37 +42,37 @@ class TestRSForm(DBTester):
|
|||
def test_insert_at(self):
|
||||
schema = RSForm.create(title='Test')
|
||||
x1 = schema.insert_new('X1')
|
||||
self.assertEqual(x1.order, 1)
|
||||
self.assertEqual(x1.order, 0)
|
||||
self.assertEqual(x1.schema, schema.model)
|
||||
|
||||
x2 = schema.insert_new('X2', position=1)
|
||||
x2 = schema.insert_new('X2', position=0)
|
||||
x1.refresh_from_db()
|
||||
self.assertEqual(x2.order, 1)
|
||||
self.assertEqual(x2.order, 0)
|
||||
self.assertEqual(x2.schema, schema.model)
|
||||
self.assertEqual(x1.order, 2)
|
||||
self.assertEqual(x1.order, 1)
|
||||
|
||||
x3 = schema.insert_new('X3', position=4)
|
||||
x3 = schema.insert_new('X3', position=3)
|
||||
x2.refresh_from_db()
|
||||
x1.refresh_from_db()
|
||||
self.assertEqual(x3.order, 3)
|
||||
self.assertEqual(x3.order, 2)
|
||||
self.assertEqual(x3.schema, schema.model)
|
||||
self.assertEqual(x2.order, 1)
|
||||
self.assertEqual(x1.order, 2)
|
||||
self.assertEqual(x2.order, 0)
|
||||
self.assertEqual(x1.order, 1)
|
||||
|
||||
x4 = schema.insert_new('X4', position=3)
|
||||
x4 = schema.insert_new('X4', position=2)
|
||||
x3.refresh_from_db()
|
||||
x2.refresh_from_db()
|
||||
x1.refresh_from_db()
|
||||
self.assertEqual(x4.order, 3)
|
||||
self.assertEqual(x4.order, 2)
|
||||
self.assertEqual(x4.schema, schema.model)
|
||||
self.assertEqual(x3.order, 4)
|
||||
self.assertEqual(x2.order, 1)
|
||||
self.assertEqual(x1.order, 2)
|
||||
self.assertEqual(x3.order, 3)
|
||||
self.assertEqual(x2.order, 0)
|
||||
self.assertEqual(x1.order, 1)
|
||||
|
||||
|
||||
def test_insert_at_invalid_position(self):
|
||||
with self.assertRaises(ValidationError):
|
||||
self.schema.insert_new('X5', position=0)
|
||||
self.schema.insert_new('X5', position=-2)
|
||||
|
||||
|
||||
def test_insert_at_invalid_alias(self):
|
||||
|
@ -84,24 +84,24 @@ class TestRSForm(DBTester):
|
|||
def test_insert_at_reorder(self):
|
||||
self.schema.insert_new('X1')
|
||||
d1 = self.schema.insert_new('D1')
|
||||
d2 = self.schema.insert_new('D2', position=1)
|
||||
d2 = self.schema.insert_new('D2', position=0)
|
||||
d1.refresh_from_db()
|
||||
self.assertEqual(d1.order, 3)
|
||||
self.assertEqual(d2.order, 1)
|
||||
self.assertEqual(d1.order, 2)
|
||||
self.assertEqual(d2.order, 0)
|
||||
|
||||
x2 = self.schema.insert_new('X2', position=4)
|
||||
self.assertEqual(x2.order, 4)
|
||||
x2 = self.schema.insert_new('X2', position=3)
|
||||
self.assertEqual(x2.order, 3)
|
||||
|
||||
|
||||
def test_insert_last(self):
|
||||
x1 = self.schema.insert_new('X1')
|
||||
self.assertEqual(x1.order, 1)
|
||||
self.assertEqual(x1.order, 0)
|
||||
self.assertEqual(x1.schema, self.schema.model)
|
||||
|
||||
x2 = self.schema.insert_new('X2')
|
||||
self.assertEqual(x2.order, 2)
|
||||
self.assertEqual(x2.order, 1)
|
||||
self.assertEqual(x2.schema, self.schema.model)
|
||||
self.assertEqual(x1.order, 1)
|
||||
self.assertEqual(x1.order, 0)
|
||||
|
||||
def test_create_cst(self):
|
||||
data = {
|
||||
|
@ -120,8 +120,8 @@ class TestRSForm(DBTester):
|
|||
self.assertEqual(x3.alias, data['alias'])
|
||||
self.assertEqual(x3.term_raw, data['term_raw'])
|
||||
self.assertEqual(x3.definition_raw, data['definition_raw'])
|
||||
self.assertEqual(x2.order, 3)
|
||||
self.assertEqual(x3.order, 2)
|
||||
self.assertEqual(x2.order, 2)
|
||||
self.assertEqual(x3.order, 1)
|
||||
|
||||
|
||||
def test_create_cst_resolve(self):
|
||||
|
@ -154,20 +154,20 @@ class TestRSForm(DBTester):
|
|||
definition_raw='@{X10|plur}'
|
||||
)
|
||||
|
||||
result = self.schema.insert_copy([s1, x1], 2)
|
||||
result = self.schema.insert_copy([s1, x1], 1)
|
||||
self.assertEqual(len(result), 2)
|
||||
|
||||
s1.refresh_from_db()
|
||||
self.assertEqual(s1.order, 4)
|
||||
self.assertEqual(s1.order, 3)
|
||||
|
||||
x2 = result[1]
|
||||
self.assertEqual(x2.order, 3)
|
||||
self.assertEqual(x2.order, 2)
|
||||
self.assertEqual(x2.alias, 'X11')
|
||||
self.assertEqual(x2.cst_type, CstType.BASE)
|
||||
self.assertEqual(x2.convention, x1.convention)
|
||||
|
||||
s2 = result[0]
|
||||
self.assertEqual(s2.order, 2)
|
||||
self.assertEqual(s2.order, 1)
|
||||
self.assertEqual(s2.alias, 'S12')
|
||||
self.assertEqual(s2.cst_type, CstType.STRUCTURED)
|
||||
self.assertEqual(s2.definition_formal, x2.alias)
|
||||
|
@ -188,8 +188,8 @@ class TestRSForm(DBTester):
|
|||
x2.refresh_from_db()
|
||||
d1.refresh_from_db()
|
||||
self.assertEqual(self.schema.constituents().count(), 2)
|
||||
self.assertEqual(x2.order, 1)
|
||||
self.assertEqual(d1.order, 2)
|
||||
self.assertEqual(x2.order, 0)
|
||||
self.assertEqual(d1.order, 1)
|
||||
self.assertEqual(d1.definition_formal, 'DEL = X2')
|
||||
self.assertEqual(d1.definition_raw, '@{DEL|sing}')
|
||||
self.assertEqual(d1.term_raw, '@{X2|plur}')
|
||||
|
@ -240,25 +240,25 @@ class TestRSForm(DBTester):
|
|||
x2 = self.schema.insert_new('X2')
|
||||
d1 = self.schema.insert_new('D1')
|
||||
d2 = self.schema.insert_new('D2')
|
||||
self.schema.move_cst([x2, d2], 1)
|
||||
self.schema.move_cst([x2, d2], 0)
|
||||
x1.refresh_from_db()
|
||||
x2.refresh_from_db()
|
||||
d1.refresh_from_db()
|
||||
d2.refresh_from_db()
|
||||
self.assertEqual(x1.order, 3)
|
||||
self.assertEqual(x2.order, 1)
|
||||
self.assertEqual(d1.order, 4)
|
||||
self.assertEqual(d2.order, 2)
|
||||
self.assertEqual(x1.order, 2)
|
||||
self.assertEqual(x2.order, 0)
|
||||
self.assertEqual(d1.order, 3)
|
||||
self.assertEqual(d2.order, 1)
|
||||
|
||||
|
||||
def test_move_cst_down(self):
|
||||
x1 = self.schema.insert_new('X1')
|
||||
x2 = self.schema.insert_new('X2')
|
||||
self.schema.move_cst([x1], 2)
|
||||
self.schema.move_cst([x1], 1)
|
||||
x1.refresh_from_db()
|
||||
x2.refresh_from_db()
|
||||
self.assertEqual(x1.order, 2)
|
||||
self.assertEqual(x2.order, 1)
|
||||
self.assertEqual(x1.order, 1)
|
||||
self.assertEqual(x2.order, 0)
|
||||
|
||||
|
||||
def test_restore_order(self):
|
||||
|
@ -316,18 +316,18 @@ class TestRSForm(DBTester):
|
|||
f2.refresh_from_db()
|
||||
a1.refresh_from_db()
|
||||
|
||||
self.assertEqual(x1.order, 1)
|
||||
self.assertEqual(x2.order, 2)
|
||||
self.assertEqual(c1.order, 3)
|
||||
self.assertEqual(s1.order, 4)
|
||||
self.assertEqual(d1.order, 5)
|
||||
self.assertEqual(s2.order, 6)
|
||||
self.assertEqual(d3.order, 7)
|
||||
self.assertEqual(a1.order, 8)
|
||||
self.assertEqual(d4.order, 9)
|
||||
self.assertEqual(d2.order, 10)
|
||||
self.assertEqual(f1.order, 11)
|
||||
self.assertEqual(f2.order, 12)
|
||||
self.assertEqual(x1.order, 0)
|
||||
self.assertEqual(x2.order, 1)
|
||||
self.assertEqual(c1.order, 2)
|
||||
self.assertEqual(s1.order, 3)
|
||||
self.assertEqual(d1.order, 4)
|
||||
self.assertEqual(s2.order, 5)
|
||||
self.assertEqual(d3.order, 6)
|
||||
self.assertEqual(a1.order, 7)
|
||||
self.assertEqual(d4.order, 8)
|
||||
self.assertEqual(d2.order, 9)
|
||||
self.assertEqual(f1.order, 10)
|
||||
self.assertEqual(f2.order, 11)
|
||||
|
||||
|
||||
def test_reset_aliases(self):
|
||||
|
|
|
@ -198,7 +198,7 @@ class TestRSFormViewset(EndpointTester):
|
|||
response = self.executeCreated(data=data, item=self.owned_id)
|
||||
self.assertEqual(response.data['new_cst']['alias'], 'X3')
|
||||
x3 = Constituenta.objects.get(alias=response.data['new_cst']['alias'])
|
||||
self.assertEqual(x3.order, 3)
|
||||
self.assertEqual(x3.order, 2)
|
||||
|
||||
data = {
|
||||
'alias': 'X4',
|
||||
|
@ -210,7 +210,7 @@ class TestRSFormViewset(EndpointTester):
|
|||
response = self.executeCreated(data=data, item=self.owned_id)
|
||||
self.assertEqual(response.data['new_cst']['alias'], data['alias'])
|
||||
x4 = Constituenta.objects.get(alias=response.data['new_cst']['alias'])
|
||||
self.assertEqual(x4.order, 3)
|
||||
self.assertEqual(x4.order, 2)
|
||||
self.assertEqual(x4.term_raw, data['term_raw'])
|
||||
self.assertEqual(x4.term_forms, data['term_forms'])
|
||||
|
||||
|
@ -257,7 +257,7 @@ class TestRSFormViewset(EndpointTester):
|
|||
term_raw='@{X1|plur}',
|
||||
definition_formal='X1'
|
||||
)
|
||||
self.assertEqual(x1.order, 1)
|
||||
self.assertEqual(x1.order, 0)
|
||||
self.assertEqual(x1.alias, 'X1')
|
||||
self.assertEqual(x1.cst_type, CstType.BASE)
|
||||
|
||||
|
@ -269,7 +269,7 @@ class TestRSFormViewset(EndpointTester):
|
|||
x1.refresh_from_db()
|
||||
self.assertEqual(d1.term_resolved, '')
|
||||
self.assertEqual(d1.term_raw, '@{D2|plur}')
|
||||
self.assertEqual(x1.order, 1)
|
||||
self.assertEqual(x1.order, 0)
|
||||
self.assertEqual(x1.alias, 'D2')
|
||||
self.assertEqual(x1.cst_type, CstType.TERM)
|
||||
|
||||
|
@ -354,7 +354,7 @@ class TestRSFormViewset(EndpointTester):
|
|||
self.assertEqual(len(response.data['items']), 1)
|
||||
self.assertEqual(self.owned.constituents().count(), 1)
|
||||
self.assertEqual(x2.alias, 'X2')
|
||||
self.assertEqual(x2.order, 1)
|
||||
self.assertEqual(x2.order, 0)
|
||||
|
||||
x3 = self.unowned.insert_new('X1')
|
||||
data = {'items': [x3.pk]}
|
||||
|
@ -365,22 +365,22 @@ class TestRSFormViewset(EndpointTester):
|
|||
def test_move_constituenta(self):
|
||||
self.set_params(item=self.owned_id)
|
||||
|
||||
data = {'items': [1337], 'move_to': 1}
|
||||
data = {'items': [1337], 'move_to': 0}
|
||||
self.executeBadData(data=data)
|
||||
|
||||
x1 = self.owned.insert_new('X1')
|
||||
x2 = self.owned.insert_new('X2')
|
||||
|
||||
data = {'items': [x2.pk], 'move_to': 1}
|
||||
data = {'items': [x2.pk], 'move_to': 0}
|
||||
response = self.executeOK(data=data)
|
||||
x1.refresh_from_db()
|
||||
x2.refresh_from_db()
|
||||
self.assertEqual(response.data['id'], self.owned_id)
|
||||
self.assertEqual(x1.order, 2)
|
||||
self.assertEqual(x2.order, 1)
|
||||
self.assertEqual(x1.order, 1)
|
||||
self.assertEqual(x2.order, 0)
|
||||
|
||||
x3 = self.unowned.insert_new('X1')
|
||||
data = {'items': [x3.pk], 'move_to': 1}
|
||||
data = {'items': [x3.pk], 'move_to': 0}
|
||||
self.executeBadData(data=data)
|
||||
|
||||
|
||||
|
@ -399,11 +399,11 @@ class TestRSFormViewset(EndpointTester):
|
|||
x1.refresh_from_db()
|
||||
x2.refresh_from_db()
|
||||
d11.refresh_from_db()
|
||||
self.assertEqual(x2.order, 1)
|
||||
self.assertEqual(x2.order, 0)
|
||||
self.assertEqual(x2.alias, 'X1')
|
||||
self.assertEqual(x1.order, 2)
|
||||
self.assertEqual(x1.order, 1)
|
||||
self.assertEqual(x1.alias, 'X2')
|
||||
self.assertEqual(d11.order, 3)
|
||||
self.assertEqual(d11.order, 2)
|
||||
self.assertEqual(d11.alias, 'D1')
|
||||
|
||||
self.executeOK()
|
||||
|
@ -462,9 +462,7 @@ class TestRSFormViewset(EndpointTester):
|
|||
result = response.data['schema']
|
||||
items = [item for item in result['items'] if item['id'] in response.data['cst_list']]
|
||||
self.assertEqual(len(items), 2)
|
||||
self.assertEqual(items[0]['order'], s1.order + 1)
|
||||
self.assertEqual(items[0]['definition_formal'], 'Pr1(S1)')
|
||||
self.assertEqual(items[1]['order'], s1.order + 2)
|
||||
self.assertEqual(items[1]['definition_formal'], 'Pr2(S1)')
|
||||
|
||||
# Testing complex structure
|
||||
|
@ -473,7 +471,6 @@ class TestRSFormViewset(EndpointTester):
|
|||
result = response.data['schema']
|
||||
items = [item for item in result['items'] if item['id'] in response.data['cst_list']]
|
||||
self.assertEqual(len(items), 8)
|
||||
self.assertEqual(items[0]['order'], s3.order + 1)
|
||||
self.assertEqual(items[0]['definition_formal'], 'pr1(S3)')
|
||||
|
||||
# Testing function
|
||||
|
@ -482,7 +479,6 @@ class TestRSFormViewset(EndpointTester):
|
|||
result = response.data['schema']
|
||||
items = [item for item in result['items'] if item['id'] in response.data['cst_list']]
|
||||
self.assertEqual(len(items), 2)
|
||||
self.assertEqual(items[0]['order'], f1.order + 1)
|
||||
self.assertEqual(items[0]['definition_formal'], '[α∈X1, β∈X1] Pr1(F10[α,β])')
|
||||
|
||||
|
||||
|
@ -497,7 +493,7 @@ class TestConstituentaAPI(EndpointTester):
|
|||
alias='X1',
|
||||
cst_type=CstType.BASE,
|
||||
schema=self.rsform_owned.model,
|
||||
order=1,
|
||||
order=0,
|
||||
convention='Test',
|
||||
term_raw='Test1',
|
||||
term_resolved='Test1R',
|
||||
|
@ -506,7 +502,7 @@ class TestConstituentaAPI(EndpointTester):
|
|||
alias='X2',
|
||||
cst_type=CstType.BASE,
|
||||
schema=self.rsform_unowned.model,
|
||||
order=1,
|
||||
order=0,
|
||||
convention='Test1',
|
||||
term_raw='Test2',
|
||||
term_resolved='Test2R'
|
||||
|
@ -514,7 +510,7 @@ class TestConstituentaAPI(EndpointTester):
|
|||
self.cst3 = Constituenta.objects.create(
|
||||
alias='X3',
|
||||
schema=self.rsform_owned.model,
|
||||
order=2,
|
||||
order=1,
|
||||
term_raw='Test3',
|
||||
term_resolved='Test3',
|
||||
definition_raw='Test1',
|
||||
|
@ -594,14 +590,12 @@ class TestConstituentaAPI(EndpointTester):
|
|||
data = {
|
||||
'target': self.cst1.pk,
|
||||
'item_data': {
|
||||
'alias': 'X33',
|
||||
'order': 10
|
||||
'alias': 'X33'
|
||||
}
|
||||
}
|
||||
response = self.executeOK(data=data, schema=self.rsform_owned.model.pk)
|
||||
self.assertEqual(response.data['alias'], 'X1')
|
||||
self.assertEqual(response.data['alias'], self.cst1.alias)
|
||||
self.assertEqual(response.data['order'], self.cst1.order)
|
||||
|
||||
|
||||
class TestInlineSynthesis(EndpointTester):
|
||||
|
@ -669,8 +663,6 @@ class TestInlineSynthesis(EndpointTester):
|
|||
response = self.executeOK(data=data)
|
||||
result = {item['alias']: item for item in response.data['items']}
|
||||
self.assertEqual(len(result), 6)
|
||||
self.assertEqual(result['X2']['order'], 1)
|
||||
self.assertEqual(result['X4']['order'], 2)
|
||||
self.assertEqual(result['S1']['definition_formal'], 'X2')
|
||||
self.assertEqual(result['S2']['definition_formal'], 'X4×X4')
|
||||
self.assertEqual(result['D1']['definition_formal'], r'S1\S2\X2')
|
||||
|
|
File diff suppressed because it is too large
Load Diff
273
rsconcept/frontend/package-lock.json
generated
273
rsconcept/frontend/package-lock.json
generated
|
@ -10,11 +10,11 @@
|
|||
"dependencies": {
|
||||
"@lezer/lr": "^1.4.2",
|
||||
"@tanstack/react-table": "^8.20.5",
|
||||
"@uiw/codemirror-themes": "^4.23.0",
|
||||
"@uiw/react-codemirror": "^4.23.0",
|
||||
"@uiw/codemirror-themes": "^4.23.2",
|
||||
"@uiw/react-codemirror": "^4.23.2",
|
||||
"axios": "^1.7.7",
|
||||
"clsx": "^2.1.1",
|
||||
"framer-motion": "^11.5.1",
|
||||
"framer-motion": "^11.5.4",
|
||||
"html-to-image": "^1.11.11",
|
||||
"js-file-download": "^0.4.12",
|
||||
"react": "^18.3.1",
|
||||
|
@ -23,7 +23,7 @@
|
|||
"react-icons": "^5.3.0",
|
||||
"react-intl": "^6.6.8",
|
||||
"react-loader-spinner": "^6.1.6",
|
||||
"react-router-dom": "^6.26.1",
|
||||
"react-router-dom": "^6.26.2",
|
||||
"react-select": "^5.8.0",
|
||||
"react-tabs": "^6.0.2",
|
||||
"react-toastify": "^10.0.5",
|
||||
|
@ -36,14 +36,14 @@
|
|||
"devDependencies": {
|
||||
"@lezer/generator": "^1.7.1",
|
||||
"@types/jest": "^29.5.12",
|
||||
"@types/node": "^22.5.3",
|
||||
"@types/node": "^22.5.4",
|
||||
"@types/react": "^18.3.5",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.0.1",
|
||||
"@typescript-eslint/parser": "^8.0.1",
|
||||
"@vitejs/plugin-react": "^4.3.1",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"eslint": "^9.9.1",
|
||||
"eslint": "^9.10.0",
|
||||
"eslint-plugin-react": "^7.35.2",
|
||||
"eslint-plugin-simple-import-sort": "^12.1.1",
|
||||
"globals": "^15.9.0",
|
||||
|
@ -51,9 +51,9 @@
|
|||
"postcss": "^8.4.45",
|
||||
"tailwindcss": "^3.4.10",
|
||||
"ts-jest": "^29.2.5",
|
||||
"typescript": "^5.5.4",
|
||||
"typescript-eslint": "^8.4.0",
|
||||
"vite": "^5.4.3"
|
||||
"typescript": "^5.6.2",
|
||||
"typescript-eslint": "^8.5.0",
|
||||
"vite": "^5.4.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@alloc/quick-lru": {
|
||||
|
@ -1431,9 +1431,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@eslint/js": {
|
||||
"version": "9.9.1",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.9.1.tgz",
|
||||
"integrity": "sha512-xIDQRsfg5hNBqHz04H1R3scSVwmI+KUbqjsQKHKQ1DAUSaUjYPReZZmS/5PNiKu1fUvzDd6H7DEDKACSEhu+TQ==",
|
||||
"version": "9.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.10.0.tgz",
|
||||
"integrity": "sha512-fuXtbiP5GWIn8Fz+LWoOMVf/Jxm+aajZYkhi6CuEm4SxymFM+eUWzbO9qXT+L0iCkL5+KGYMCSGxo686H19S1g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
|
@ -1450,6 +1450,19 @@
|
|||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/plugin-kit": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.1.0.tgz",
|
||||
"integrity": "sha512-autAXT203ixhqei9xt+qkYOvY8l6LAFIdT2UXc/RPNeUVfqRF1BV94GTJyVPFKT8nFM6MyVJhjLj9E8JWvf5zQ==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"levn": "^0.4.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/core": {
|
||||
"version": "1.6.7",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.7.tgz",
|
||||
|
@ -1616,9 +1629,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@isaacs/cliui/node_modules/ansi-regex": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
|
||||
"integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
|
||||
"integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
|
@ -2875,9 +2888,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@remix-run/router": {
|
||||
"version": "1.19.1",
|
||||
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.19.1.tgz",
|
||||
"integrity": "sha512-S45oynt/WH19bHbIXjtli6QmwNYvaz+vtnubvNpNDvUOoA/OWh6j1OikIP3G+v5GHdxyC6EXoChG3HgYGEUfcg==",
|
||||
"version": "1.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.19.2.tgz",
|
||||
"integrity": "sha512-baiMx18+IMuD1yyvOGaHM9QrVUPGGG0jC+z+IPHnRJWUAUvaKuWKyE8gjDj2rzv3sz9zOGoRSPgeBVHRhZnBlA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
|
@ -3549,9 +3562,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "22.5.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.3.tgz",
|
||||
"integrity": "sha512-njripolh85IA9SQGTAqbmnNZTdxv7X/4OYGPz8tgy5JDr8MP+uDBa921GpYEoDDnwm0Hmn5ZPeJgiiSTPoOzkQ==",
|
||||
"version": "22.5.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.4.tgz",
|
||||
"integrity": "sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
@ -3672,17 +3685,17 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "8.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.4.0.tgz",
|
||||
"integrity": "sha512-rg8LGdv7ri3oAlenMACk9e+AR4wUV0yrrG+XKsGKOK0EVgeEDqurkXMPILG2836fW4ibokTB5v4b6Z9+GYQDEw==",
|
||||
"version": "8.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.5.0.tgz",
|
||||
"integrity": "sha512-lHS5hvz33iUFQKuPFGheAB84LwcJ60G8vKnEhnfcK1l8kGVLro2SFYW6K0/tj8FUhRJ0VHyg1oAfg50QGbPPHw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/regexpp": "^4.10.0",
|
||||
"@typescript-eslint/scope-manager": "8.4.0",
|
||||
"@typescript-eslint/type-utils": "8.4.0",
|
||||
"@typescript-eslint/utils": "8.4.0",
|
||||
"@typescript-eslint/visitor-keys": "8.4.0",
|
||||
"@typescript-eslint/scope-manager": "8.5.0",
|
||||
"@typescript-eslint/type-utils": "8.5.0",
|
||||
"@typescript-eslint/utils": "8.5.0",
|
||||
"@typescript-eslint/visitor-keys": "8.5.0",
|
||||
"graphemer": "^1.4.0",
|
||||
"ignore": "^5.3.1",
|
||||
"natural-compare": "^1.4.0",
|
||||
|
@ -3706,16 +3719,16 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser": {
|
||||
"version": "8.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.4.0.tgz",
|
||||
"integrity": "sha512-NHgWmKSgJk5K9N16GIhQ4jSobBoJwrmURaLErad0qlLjrpP5bECYg+wxVTGlGZmJbU03jj/dfnb6V9bw+5icsA==",
|
||||
"version": "8.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.5.0.tgz",
|
||||
"integrity": "sha512-gF77eNv0Xz2UJg/NbpWJ0kqAm35UMsvZf1GHj8D9MRFTj/V3tAciIWXfmPLsAAF/vUlpWPvUDyH1jjsr0cMVWw==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "8.4.0",
|
||||
"@typescript-eslint/types": "8.4.0",
|
||||
"@typescript-eslint/typescript-estree": "8.4.0",
|
||||
"@typescript-eslint/visitor-keys": "8.4.0",
|
||||
"@typescript-eslint/scope-manager": "8.5.0",
|
||||
"@typescript-eslint/types": "8.5.0",
|
||||
"@typescript-eslint/typescript-estree": "8.5.0",
|
||||
"@typescript-eslint/visitor-keys": "8.5.0",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
|
@ -3735,14 +3748,14 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "8.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.4.0.tgz",
|
||||
"integrity": "sha512-n2jFxLeY0JmKfUqy3P70rs6vdoPjHK8P/w+zJcV3fk0b0BwRXC/zxRTEnAsgYT7MwdQDt/ZEbtdzdVC+hcpF0A==",
|
||||
"version": "8.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.5.0.tgz",
|
||||
"integrity": "sha512-06JOQ9Qgj33yvBEx6tpC8ecP9o860rsR22hWMEd12WcTRrfaFgHr2RB/CA/B+7BMhHkXT4chg2MyboGdFGawYg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.4.0",
|
||||
"@typescript-eslint/visitor-keys": "8.4.0"
|
||||
"@typescript-eslint/types": "8.5.0",
|
||||
"@typescript-eslint/visitor-keys": "8.5.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
|
@ -3753,14 +3766,14 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/type-utils": {
|
||||
"version": "8.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.4.0.tgz",
|
||||
"integrity": "sha512-pu2PAmNrl9KX6TtirVOrbLPLwDmASpZhK/XU7WvoKoCUkdtq9zF7qQ7gna0GBZFN0hci0vHaSusiL2WpsQk37A==",
|
||||
"version": "8.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.5.0.tgz",
|
||||
"integrity": "sha512-N1K8Ix+lUM+cIDhL2uekVn/ZD7TZW+9/rwz8DclQpcQ9rk4sIL5CAlBC0CugWKREmDjBzI/kQqU4wkg46jWLYA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/typescript-estree": "8.4.0",
|
||||
"@typescript-eslint/utils": "8.4.0",
|
||||
"@typescript-eslint/typescript-estree": "8.5.0",
|
||||
"@typescript-eslint/utils": "8.5.0",
|
||||
"debug": "^4.3.4",
|
||||
"ts-api-utils": "^1.3.0"
|
||||
},
|
||||
|
@ -3778,9 +3791,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/types": {
|
||||
"version": "8.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.4.0.tgz",
|
||||
"integrity": "sha512-T1RB3KQdskh9t3v/qv7niK6P8yvn7ja1mS7QK7XfRVL6wtZ8/mFs/FHf4fKvTA0rKnqnYxl/uHFNbnEt0phgbw==",
|
||||
"version": "8.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.5.0.tgz",
|
||||
"integrity": "sha512-qjkormnQS5wF9pjSi6q60bKUHH44j2APxfh9TQRXK8wbYVeDYYdYJGIROL87LGZZ2gz3Rbmjc736qyL8deVtdw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
|
@ -3792,14 +3805,14 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "8.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.4.0.tgz",
|
||||
"integrity": "sha512-kJ2OIP4dQw5gdI4uXsaxUZHRwWAGpREJ9Zq6D5L0BweyOrWsL6Sz0YcAZGWhvKnH7fm1J5YFE1JrQL0c9dd53A==",
|
||||
"version": "8.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.5.0.tgz",
|
||||
"integrity": "sha512-vEG2Sf9P8BPQ+d0pxdfndw3xIXaoSjliG0/Ejk7UggByZPKXmJmw3GW5jV2gHNQNawBUyfahoSiCFVov0Ruf7Q==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.4.0",
|
||||
"@typescript-eslint/visitor-keys": "8.4.0",
|
||||
"@typescript-eslint/types": "8.5.0",
|
||||
"@typescript-eslint/visitor-keys": "8.5.0",
|
||||
"debug": "^4.3.4",
|
||||
"fast-glob": "^3.3.2",
|
||||
"is-glob": "^4.0.3",
|
||||
|
@ -3821,16 +3834,16 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/utils": {
|
||||
"version": "8.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.4.0.tgz",
|
||||
"integrity": "sha512-swULW8n1IKLjRAgciCkTCafyTHHfwVQFt8DovmaF69sKbOxTSFMmIZaSHjqO9i/RV0wIblaawhzvtva8Nmm7lQ==",
|
||||
"version": "8.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.5.0.tgz",
|
||||
"integrity": "sha512-6yyGYVL0e+VzGYp60wvkBHiqDWOpT63pdMV2CVG4LVDd5uR6q1qQN/7LafBZtAtNIn/mqXjsSeS5ggv/P0iECw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.4.0",
|
||||
"@typescript-eslint/scope-manager": "8.4.0",
|
||||
"@typescript-eslint/types": "8.4.0",
|
||||
"@typescript-eslint/typescript-estree": "8.4.0"
|
||||
"@typescript-eslint/scope-manager": "8.5.0",
|
||||
"@typescript-eslint/types": "8.5.0",
|
||||
"@typescript-eslint/typescript-estree": "8.5.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
|
@ -3844,13 +3857,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/visitor-keys": {
|
||||
"version": "8.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.4.0.tgz",
|
||||
"integrity": "sha512-zTQD6WLNTre1hj5wp09nBIDiOc2U5r/qmzo7wxPn4ZgAjHql09EofqhF9WF+fZHzL5aCyaIpPcT2hyxl73kr9A==",
|
||||
"version": "8.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.5.0.tgz",
|
||||
"integrity": "sha512-yTPqMnbAZJNy2Xq2XU8AdtOW9tJIr+UQb64aXB9f3B1498Zx9JorVgFJcZpEc9UBuCCrdzKID2RGAMkYcDtZOw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.4.0",
|
||||
"@typescript-eslint/types": "8.5.0",
|
||||
"eslint-visitor-keys": "^3.4.3"
|
||||
},
|
||||
"engines": {
|
||||
|
@ -3862,9 +3875,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@uiw/codemirror-extensions-basic-setup": {
|
||||
"version": "4.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@uiw/codemirror-extensions-basic-setup/-/codemirror-extensions-basic-setup-4.23.0.tgz",
|
||||
"integrity": "sha512-+k5nkRpUWGaHr1JWT8jcKsVewlXw5qBgSopm9LW8fZ6KnSNZBycz8kHxh0+WSvckmXEESGptkIsb7dlkmJT/hQ==",
|
||||
"version": "4.23.2",
|
||||
"resolved": "https://registry.npmjs.org/@uiw/codemirror-extensions-basic-setup/-/codemirror-extensions-basic-setup-4.23.2.tgz",
|
||||
"integrity": "sha512-eacivkj7wzskl2HBYs4rfN0CbYlsSQh5ADtOYWTpc8Txm4ONw8RTi4/rxF6Ks2vdaovizewU5QaHximbxoNTrw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@codemirror/autocomplete": "^6.0.0",
|
||||
|
@ -3889,9 +3902,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@uiw/codemirror-themes": {
|
||||
"version": "4.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@uiw/codemirror-themes/-/codemirror-themes-4.23.0.tgz",
|
||||
"integrity": "sha512-9fiji9xooZyBQozR1i6iTr56YP7j/Dr/VgsNWbqf5Szv+g+4WM1iZuiDGwNXmFMWX8gbkDzp6ASE21VCPSofWw==",
|
||||
"version": "4.23.2",
|
||||
"resolved": "https://registry.npmjs.org/@uiw/codemirror-themes/-/codemirror-themes-4.23.2.tgz",
|
||||
"integrity": "sha512-g8x+oPqgbzxXSkHhRf7e1AM1mI9/Nl3URReS89pHitRKv8MZNrE+ey+HE8ycfNXRUatrb6zTSRV3M75uoZwNYw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@codemirror/language": "^6.0.0",
|
||||
|
@ -3908,16 +3921,16 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@uiw/react-codemirror": {
|
||||
"version": "4.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@uiw/react-codemirror/-/react-codemirror-4.23.0.tgz",
|
||||
"integrity": "sha512-MnqTXfgeLA3fsUUQjqjJgemEuNyoGALgsExVm0NQAllAAi1wfj+IoKFeK+h3XXMlTFRCFYOUh4AHDv0YXJLsOg==",
|
||||
"version": "4.23.2",
|
||||
"resolved": "https://registry.npmjs.org/@uiw/react-codemirror/-/react-codemirror-4.23.2.tgz",
|
||||
"integrity": "sha512-MmFL6P5V1Mr81JLkJyWNedfxENKdRhsvyU7Izji9wp337m8dqRAz7rCF5XWarGKx+iQ7q2H5ryl07nLqKLSvtQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.18.6",
|
||||
"@codemirror/commands": "^6.1.0",
|
||||
"@codemirror/state": "^6.1.1",
|
||||
"@codemirror/theme-one-dark": "^6.0.0",
|
||||
"@uiw/codemirror-extensions-basic-setup": "4.23.0",
|
||||
"@uiw/codemirror-extensions-basic-setup": "4.23.2",
|
||||
"codemirror": "^6.0.0"
|
||||
},
|
||||
"funding": {
|
||||
|
@ -4729,9 +4742,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001655",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001655.tgz",
|
||||
"integrity": "sha512-jRGVy3iSGO5Uutn2owlb5gR6qsGngTw9ZTb4ali9f3glshcNmJ2noam4Mo9zia5P9Dk3jNNydy7vQjuE5dQmfg==",
|
||||
"version": "1.0.30001660",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001660.tgz",
|
||||
"integrity": "sha512-GacvNTTuATm26qC74pt+ad1fW15mlQ/zuTzzY1ZoIzECTP8HURDfF43kNxPgf7H1jmelCBQTTbBNxdSXOA7Bqg==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
|
@ -4828,9 +4841,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/cjs-module-lexer": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.0.tgz",
|
||||
"integrity": "sha512-N1NGmowPlGBLsOZLPvm48StN04V4YvQRL0i6b7ctrVY3epjP/ct7hFLOItz6pDIvRjwpfPxi52a2UWV2ziir8g==",
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.1.tgz",
|
||||
"integrity": "sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
|
@ -5427,12 +5440,12 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.3.6",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz",
|
||||
"integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==",
|
||||
"version": "4.3.7",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
|
||||
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "2.1.2"
|
||||
"ms": "^2.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
|
@ -5521,9 +5534,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/detect-gpu": {
|
||||
"version": "5.0.46",
|
||||
"resolved": "https://registry.npmjs.org/detect-gpu/-/detect-gpu-5.0.46.tgz",
|
||||
"integrity": "sha512-aulQlEJDVAADo2j4ZkcEu/mtuX9dz104w7uIDa52/ntcKdOEM8aI+k91Wv4x0o+Gds4Nbd2Sds0Uaqp1ZuLLJw==",
|
||||
"version": "5.0.47",
|
||||
"resolved": "https://registry.npmjs.org/detect-gpu/-/detect-gpu-5.0.47.tgz",
|
||||
"integrity": "sha512-hxOjFbFN6/ToNzDs0SIt/P/Y1WxoxAEUXXlrw/HT2IPtDtIxSi57zP/TC6kTvWDWmwSnvfVHsPoDZihscx8OJQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"webgl-constants": "^1.1.1"
|
||||
|
@ -5616,9 +5629,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.5.13",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.13.tgz",
|
||||
"integrity": "sha512-lbBcvtIJ4J6sS4tb5TLp1b4LyfCdMkwStzXPyAgVgTRAsep4bvrAGaBOP7ZJtQMNJpSQ9SqG4brWOroNaQtm7Q==",
|
||||
"version": "1.5.19",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.19.tgz",
|
||||
"integrity": "sha512-kpLJJi3zxTR1U828P+LIUDZ5ohixyo68/IcYOHLqnbTPr/wdgn4i1ECvmALN9E16JPA6cvCG5UG79gVwVdEK5w==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
|
@ -5882,9 +5895,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/eslint": {
|
||||
"version": "9.9.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.9.1.tgz",
|
||||
"integrity": "sha512-dHvhrbfr4xFQ9/dq+jcVneZMyRYLjggWjk6RVsIiHsP8Rz6yZ8LvZ//iU4TrZF+SXWG+JkNF2OyiZRvzgRDqMg==",
|
||||
"version": "9.10.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.10.0.tgz",
|
||||
"integrity": "sha512-Y4D0IgtBZfOcOUAIQTSXBKoNGfY0REGqHJG6+Q81vNippW5YlKjHFj4soMxamKK1NXHUWuBZTLdU3Km+L/pcHw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
@ -5892,7 +5905,8 @@
|
|||
"@eslint-community/regexpp": "^4.11.0",
|
||||
"@eslint/config-array": "^0.18.0",
|
||||
"@eslint/eslintrc": "^3.1.0",
|
||||
"@eslint/js": "9.9.1",
|
||||
"@eslint/js": "9.10.0",
|
||||
"@eslint/plugin-kit": "^0.1.0",
|
||||
"@humanwhocodes/module-importer": "^1.0.1",
|
||||
"@humanwhocodes/retry": "^0.3.0",
|
||||
"@nodelib/fs.walk": "^1.2.8",
|
||||
|
@ -5915,7 +5929,6 @@
|
|||
"is-glob": "^4.0.0",
|
||||
"is-path-inside": "^3.0.3",
|
||||
"json-stable-stringify-without-jsonify": "^1.0.1",
|
||||
"levn": "^0.4.1",
|
||||
"lodash.merge": "^4.6.2",
|
||||
"minimatch": "^3.1.2",
|
||||
"natural-compare": "^1.4.0",
|
||||
|
@ -6495,9 +6508,9 @@
|
|||
"license": "ISC"
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.15.8",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.8.tgz",
|
||||
"integrity": "sha512-xgrmBhBToVKay1q2Tao5LI26B83UhrB/vM1avwVSDzt8rx3rO6AizBAaF46EgksTVr+rFTQaqZZ9MVBfUe4nig==",
|
||||
"version": "1.15.9",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
|
||||
"integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
|
@ -6583,9 +6596,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/framer-motion": {
|
||||
"version": "11.5.1",
|
||||
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.5.1.tgz",
|
||||
"integrity": "sha512-8QI17IxYiqWo+lIXmAopG+6NEi10CgTL1AlQRpcNcSxoyrrcRgXB/tc6pzmeloZ3y4xTdoFCN/s4mJAdTz6Clw==",
|
||||
"version": "11.5.4",
|
||||
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.5.4.tgz",
|
||||
"integrity": "sha512-E+tb3/G6SO69POkdJT+3EpdMuhmtCh9EWuK4I1DnIC23L7tFPrl8vxP+LSovwaw6uUr73rUbpb4FgK011wbRJQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
|
@ -10033,9 +10046,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/mz": {
|
||||
|
@ -11035,12 +11048,12 @@
|
|||
}
|
||||
},
|
||||
"node_modules/react-router": {
|
||||
"version": "6.26.1",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.26.1.tgz",
|
||||
"integrity": "sha512-kIwJveZNwp7teQRI5QmwWo39A5bXRyqpH0COKKmPnyD2vBvDwgFXSqDUYtt1h+FEyfnE8eXr7oe0MxRzVwCcvQ==",
|
||||
"version": "6.26.2",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.26.2.tgz",
|
||||
"integrity": "sha512-tvN1iuT03kHgOFnLPfLJ8V95eijteveqdOSk+srqfePtQvqCExB8eHOYnlilbOcyJyKnYkr1vJvf7YqotAJu1A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@remix-run/router": "1.19.1"
|
||||
"@remix-run/router": "1.19.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
|
@ -11050,13 +11063,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/react-router-dom": {
|
||||
"version": "6.26.1",
|
||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.26.1.tgz",
|
||||
"integrity": "sha512-veut7m41S1fLql4pLhxeSW3jlqs+4MtjRLj0xvuCEXsxusJCbs6I8yn9BxzzDX2XDgafrccY6hwjmd/bL54tFw==",
|
||||
"version": "6.26.2",
|
||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.26.2.tgz",
|
||||
"integrity": "sha512-z7YkaEW0Dy35T3/QKPYB1LjMK2R1fxnHO8kWpUMTBdfVzZrWOiY9a7CtN8HqdWtDUWd5FY6Dl8HFsqVwH4uOtQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@remix-run/router": "1.19.1",
|
||||
"react-router": "6.26.1"
|
||||
"@remix-run/router": "1.19.2",
|
||||
"react-router": "6.26.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
|
@ -11662,9 +11675,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/source-map-js": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
|
||||
"integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==",
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
|
@ -12229,9 +12242,9 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/three-mesh-bvh": {
|
||||
"version": "0.7.6",
|
||||
"resolved": "https://registry.npmjs.org/three-mesh-bvh/-/three-mesh-bvh-0.7.6.tgz",
|
||||
"integrity": "sha512-rCjsnxEqR9r1/C/lCqzGLS67NDty/S/eT6rAJfDvsanrIctTWdNoR4ZOGWewCB13h1QkVo2BpmC0wakj1+0m8A==",
|
||||
"version": "0.7.8",
|
||||
"resolved": "https://registry.npmjs.org/three-mesh-bvh/-/three-mesh-bvh-0.7.8.tgz",
|
||||
"integrity": "sha512-BGEZTOIC14U0XIRw3tO4jY7IjP7n7v24nv9JXS1CyeVRWOCkcOMhRnmENUjuV39gktAw4Ofhr0OvIAiTspQrrw==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"three": ">= 0.151.0"
|
||||
|
@ -12517,9 +12530,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.5.4",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz",
|
||||
"integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==",
|
||||
"version": "5.6.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz",
|
||||
"integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==",
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
|
@ -12531,15 +12544,15 @@
|
|||
}
|
||||
},
|
||||
"node_modules/typescript-eslint": {
|
||||
"version": "8.4.0",
|
||||
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.4.0.tgz",
|
||||
"integrity": "sha512-67qoc3zQZe3CAkO0ua17+7aCLI0dU+sSQd1eKPGq06QE4rfQjstVXR6woHO5qQvGUa550NfGckT4tzh3b3c8Pw==",
|
||||
"version": "8.5.0",
|
||||
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.5.0.tgz",
|
||||
"integrity": "sha512-uD+XxEoSIvqtm4KE97etm32Tn5MfaZWgWfMMREStLxR6JzvHkc2Tkj7zhTEK5XmtpTmKHNnG8Sot6qDfhHtR1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "8.4.0",
|
||||
"@typescript-eslint/parser": "8.4.0",
|
||||
"@typescript-eslint/utils": "8.4.0"
|
||||
"@typescript-eslint/eslint-plugin": "8.5.0",
|
||||
"@typescript-eslint/parser": "8.5.0",
|
||||
"@typescript-eslint/utils": "8.5.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
|
@ -12698,9 +12711,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "5.4.3",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.3.tgz",
|
||||
"integrity": "sha512-IH+nl64eq9lJjFqU+/yrRnrHPVTlgy42/+IzbOdaFDVlyLgI/wDlf+FCobXLX1cT0X5+7LMyH1mIy2xJdLfo8Q==",
|
||||
"version": "5.4.4",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.4.tgz",
|
||||
"integrity": "sha512-RHFCkULitycHVTtelJ6jQLd+KSAAzOgEYorV32R2q++M6COBjKJR6BxqClwp5sf0XaBDjVMuJ9wnNfyAJwjMkA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
|
|
@ -14,11 +14,11 @@
|
|||
"dependencies": {
|
||||
"@lezer/lr": "^1.4.2",
|
||||
"@tanstack/react-table": "^8.20.5",
|
||||
"@uiw/codemirror-themes": "^4.23.0",
|
||||
"@uiw/react-codemirror": "^4.23.0",
|
||||
"@uiw/codemirror-themes": "^4.23.2",
|
||||
"@uiw/react-codemirror": "^4.23.2",
|
||||
"axios": "^1.7.7",
|
||||
"clsx": "^2.1.1",
|
||||
"framer-motion": "^11.5.1",
|
||||
"framer-motion": "^11.5.4",
|
||||
"html-to-image": "^1.11.11",
|
||||
"js-file-download": "^0.4.12",
|
||||
"react": "^18.3.1",
|
||||
|
@ -27,7 +27,7 @@
|
|||
"react-icons": "^5.3.0",
|
||||
"react-intl": "^6.6.8",
|
||||
"react-loader-spinner": "^6.1.6",
|
||||
"react-router-dom": "^6.26.1",
|
||||
"react-router-dom": "^6.26.2",
|
||||
"react-select": "^5.8.0",
|
||||
"react-tabs": "^6.0.2",
|
||||
"react-toastify": "^10.0.5",
|
||||
|
@ -40,14 +40,14 @@
|
|||
"devDependencies": {
|
||||
"@lezer/generator": "^1.7.1",
|
||||
"@types/jest": "^29.5.12",
|
||||
"@types/node": "^22.5.3",
|
||||
"@types/node": "^22.5.4",
|
||||
"@types/react": "^18.3.5",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.0.1",
|
||||
"@typescript-eslint/parser": "^8.0.1",
|
||||
"@vitejs/plugin-react": "^4.3.1",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"eslint": "^9.9.1",
|
||||
"eslint": "^9.10.0",
|
||||
"eslint-plugin-react": "^7.35.2",
|
||||
"eslint-plugin-simple-import-sort": "^12.1.1",
|
||||
"globals": "^15.9.0",
|
||||
|
@ -55,9 +55,9 @@
|
|||
"postcss": "^8.4.45",
|
||||
"tailwindcss": "^3.4.10",
|
||||
"ts-jest": "^29.2.5",
|
||||
"typescript": "^5.5.4",
|
||||
"typescript-eslint": "^8.4.0",
|
||||
"vite": "^5.4.3"
|
||||
"typescript": "^5.6.2",
|
||||
"typescript-eslint": "^8.5.0",
|
||||
"vite": "^5.4.4"
|
||||
},
|
||||
"jest": {
|
||||
"preset": "ts-jest",
|
||||
|
|
|
@ -116,6 +116,22 @@ const RSInput = forwardRef<ReactCodeMirrorRef, RSInputProps>(
|
|||
if (!selection.empty || !schema) {
|
||||
return;
|
||||
}
|
||||
const wordRange = text.getWord(selection.from);
|
||||
if (wordRange) {
|
||||
const word = text.getText(wordRange.from, wordRange.to);
|
||||
if (word.length > 2 && (word.startsWith('Pr') || word.startsWith('pr'))) {
|
||||
text.setSelection(wordRange.from, wordRange.from + 2);
|
||||
if (word.startsWith('Pr')) {
|
||||
text.replaceWith('pr');
|
||||
} else {
|
||||
text.replaceWith('Pr');
|
||||
}
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const hint = text.getText(selection.from - 1, selection.from);
|
||||
const type = guessCstType(hint);
|
||||
if (hint === getCstTypePrefix(type)) {
|
||||
|
|
|
@ -70,7 +70,9 @@ function PickConstituenta({
|
|||
cell: props => <BadgeConstituenta theme={colors} value={props.row.original} prefixID={prefixID} />
|
||||
}),
|
||||
columnHelper.accessor(cst => describeFunc(cst), {
|
||||
id: 'description'
|
||||
id: 'description',
|
||||
size: 1000,
|
||||
minSize: 1000
|
||||
})
|
||||
],
|
||||
[colors, prefixID, describeFunc]
|
||||
|
|
|
@ -127,7 +127,6 @@ function DataTable<TData extends RowData>({
|
|||
|
||||
const isEmpty = tableImpl.getRowModel().rows.length === 0;
|
||||
|
||||
// TODO: refactor formula for different font sizes and pagination tools
|
||||
const fixedSize = useMemo(() => {
|
||||
if (!rows) {
|
||||
return undefined;
|
||||
|
|
|
@ -46,7 +46,7 @@ function FileInput({ id, label, acceptType, title, className, style, onChange, .
|
|||
{...restProps}
|
||||
/>
|
||||
<Button text={label} icon={<IconUpload size='1.25rem' />} onClick={handleUploadClick} title={title} />
|
||||
<Label text={fileName} htmlFor={id} />
|
||||
<Label className='text-wrap' text={fileName} htmlFor={id} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -103,23 +103,19 @@ function Modal({
|
|||
{children}
|
||||
</div>
|
||||
|
||||
<div className={clsx('z-modalControls', 'px-6 py-3 flex gap-12 justify-center')}>
|
||||
<div className='z-modalControls my-2 flex gap-12 justify-center text-sm'>
|
||||
{!readonly ? (
|
||||
<Button
|
||||
autoFocus
|
||||
text={submitText}
|
||||
title={!canSubmit ? submitInvalidTooltip : ''}
|
||||
className='min-w-[8rem] min-h-[2.6rem]'
|
||||
className='min-w-[7rem]'
|
||||
colors='clr-btn-primary'
|
||||
disabled={!canSubmit}
|
||||
onClick={handleSubmit}
|
||||
/>
|
||||
) : null}
|
||||
<Button
|
||||
text={readonly ? 'Закрыть' : 'Отмена'}
|
||||
className='min-w-[8rem] min-h-[2.6rem]'
|
||||
onClick={handleCancel}
|
||||
/>
|
||||
<Button text={readonly ? 'Закрыть' : 'Отмена'} className='min-w-[7rem]' onClick={handleCancel} />
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
|
|
|
@ -110,8 +110,14 @@ export const OptionsState = ({ children }: OptionsStateProps) => {
|
|||
}, [setDarkMode]);
|
||||
|
||||
const mainHeight = useMemo(() => {
|
||||
return !noNavigation ? 'calc(100dvh - 6.75rem)' : '100dvh';
|
||||
}, [noNavigation]);
|
||||
if (noNavigation) {
|
||||
return '100dvh';
|
||||
} else if (noFooter) {
|
||||
return 'calc(100dvh - 3rem)';
|
||||
} else {
|
||||
return 'calc(100dvh - 6.75rem)';
|
||||
}
|
||||
}, [noNavigation, noFooter]);
|
||||
|
||||
const viewportHeight = useMemo(() => {
|
||||
return !noNavigation ? 'calc(100dvh - 3rem)' : '100dvh';
|
||||
|
|
|
@ -160,13 +160,13 @@ function DlgConstituentaTemplate({ hideWindow, schema, onCreate, insertAfter }:
|
|||
<Modal
|
||||
header='Создание конституенты из шаблона'
|
||||
submitText='Создать'
|
||||
className='w-[43rem] h-[36.5rem] px-6'
|
||||
className='w-[43rem] h-[35rem] px-6'
|
||||
hideWindow={hideWindow}
|
||||
canSubmit={validated}
|
||||
beforeSubmit={handlePrompt}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
<Overlay position='top-0 right-[6rem]'>
|
||||
<Overlay position='top-0 right-[5.9rem]'>
|
||||
<BadgeHelp
|
||||
topic={HelpTopic.RSL_TEMPLATES}
|
||||
className={clsx(PARAMETER.TOOLTIP_WIDTH, 'sm:max-w-[40rem]')}
|
||||
|
|
|
@ -118,7 +118,7 @@ function TabTemplate({ state, partialUpdate, templateSchema }: TabTemplateProps)
|
|||
data={filteredData}
|
||||
onSelectValue={cst => partialUpdate({ prototype: cst })}
|
||||
prefixID={prefixes.cst_template_ist}
|
||||
rows={9}
|
||||
rows={8}
|
||||
/>
|
||||
<TextArea
|
||||
id='dlg_template_term'
|
||||
|
|
|
@ -147,7 +147,7 @@ function DlgCreateOperation({ hideWindow, oss, onCreate, initialInputs }: DlgCre
|
|||
hideWindow={hideWindow}
|
||||
canSubmit={isValid}
|
||||
onSubmit={handleSubmit}
|
||||
className='w-[40rem] px-6 min-h-[35rem]'
|
||||
className='w-[40rem] px-6 h-[32rem]'
|
||||
>
|
||||
<Overlay position='top-0 right-0'>
|
||||
<BadgeHelp topic={HelpTopic.CC_OSS} className={clsx(PARAMETER.TOOLTIP_WIDTH, 'sm:max-w-[40rem]')} offset={14} />
|
||||
|
@ -155,11 +155,11 @@ function DlgCreateOperation({ hideWindow, oss, onCreate, initialInputs }: DlgCre
|
|||
|
||||
<Tabs
|
||||
selectedTabClassName='clr-selected'
|
||||
className='flex flex-col'
|
||||
className='flex flex-col pt-2'
|
||||
selectedIndex={activeTab}
|
||||
onSelect={handleSelectTab}
|
||||
>
|
||||
<TabList className={clsx('mb-3 self-center', 'flex', 'border divide-x rounded-none')}>
|
||||
<TabList className={clsx('self-center absolute top-[2.4rem]', 'flex', 'border divide-x rounded-none')}>
|
||||
<TabLabel
|
||||
title={describeOperationType(OperationType.INPUT)}
|
||||
label={labelOperationType(OperationType.INPUT)}
|
||||
|
|
|
@ -169,7 +169,7 @@ function DlgEditOperation({ hideWindow, oss, target, onSubmit }: DlgEditOperatio
|
|||
hideWindow={hideWindow}
|
||||
canSubmit={canSubmit}
|
||||
onSubmit={handleSubmit}
|
||||
className='w-[40rem] px-6 min-h-[35rem]'
|
||||
className='w-[40rem] px-6 h-[32rem]'
|
||||
>
|
||||
<Overlay position='top-0 right-0'>
|
||||
<BadgeHelp topic={HelpTopic.CC_OSS} className={clsx(PARAMETER.TOOLTIP_WIDTH, 'sm:max-w-[40rem]')} offset={14} />
|
||||
|
|
|
@ -35,7 +35,7 @@ function TabSynthesis({
|
|||
<PickSubstitutions
|
||||
schemas={schemas}
|
||||
prefixID={prefixes.dlg_cst_substitutes_list}
|
||||
rows={10}
|
||||
rows={8}
|
||||
substitutions={substitutions}
|
||||
setSubstitutions={setSubstitutions}
|
||||
suggestions={suggestions}
|
||||
|
|
|
@ -70,7 +70,7 @@ function DlgEditReference({ hideWindow, schema, initial, onSave }: DlgEditRefere
|
|||
hideWindow={hideWindow}
|
||||
canSubmit={isValid}
|
||||
onSubmit={handleSubmit}
|
||||
className='w-[40rem] px-6 min-h-[35rem]'
|
||||
className='w-[40rem] px-6 h-[32rem]'
|
||||
>
|
||||
<Overlay position='top-0 right-0'>
|
||||
<BadgeHelp
|
||||
|
|
|
@ -70,7 +70,7 @@ function TabEntityReference({ initial, schema, setIsValid, setReference }: TabEn
|
|||
describeFunc={cst => cst.term_resolved}
|
||||
matchFunc={(cst, filter) => matchConstituenta(cst, filter, CstMatchMode.TERM)}
|
||||
onBeginFilter={cst => cst.term_resolved !== ''}
|
||||
rows={8}
|
||||
rows={7}
|
||||
/>
|
||||
|
||||
<div className='flex gap-3'>
|
||||
|
|
|
@ -96,7 +96,7 @@ function DlgInlineSynthesis({ hideWindow, receiver, onInlineSynthesis }: DlgInli
|
|||
<Modal
|
||||
header='Импорт концептуальной схем'
|
||||
submitText='Добавить конституенты'
|
||||
className='w-[40rem] h-[36rem] px-6'
|
||||
className='w-[40rem] h-[33rem] px-6'
|
||||
hideWindow={hideWindow}
|
||||
canSubmit={validated}
|
||||
onSubmit={handleSubmit}
|
||||
|
|
|
@ -19,7 +19,7 @@ function TabConstituents({ schema, error, loading, selected, setSelected }: TabC
|
|||
<DataLoader id='dlg-constituents-tab' isLoading={loading} error={error} hasNoData={!schema}>
|
||||
<PickMultiConstituenta
|
||||
schema={schema}
|
||||
rows={14}
|
||||
rows={13}
|
||||
prefixID={prefixes.cst_inline_synth_list}
|
||||
selected={selected}
|
||||
setSelected={setSelected}
|
||||
|
|
|
@ -35,7 +35,7 @@ function TabSchema({ selected, setSelected }: TabSchemaProps) {
|
|||
id='dlg_schema_picker' // prettier: split lines
|
||||
items={library.items}
|
||||
itemType={LibraryItemType.RSFORM}
|
||||
rows={15}
|
||||
rows={14}
|
||||
value={selected}
|
||||
onSelectValue={setSelected}
|
||||
/>
|
||||
|
|
|
@ -25,11 +25,6 @@ export enum CstType {
|
|||
// CstType constant for category dividers in TemplateSchemas
|
||||
export const CATEGORY_CST_TYPE = CstType.THEOREM;
|
||||
|
||||
/**
|
||||
* Represents position in linear order.
|
||||
*/
|
||||
export type Position = number;
|
||||
|
||||
/**
|
||||
* Represents {@link IConstituenta} identifier type.
|
||||
*/
|
||||
|
@ -71,7 +66,6 @@ export interface TermForm {
|
|||
export interface IConstituentaMeta {
|
||||
id: ConstituentaID;
|
||||
schema: LibraryItemID;
|
||||
order: Position;
|
||||
alias: string;
|
||||
convention: string;
|
||||
cst_type: CstType;
|
||||
|
@ -146,7 +140,7 @@ export interface ICstCreateData
|
|||
* Represents data, used in ordering a list of {@link IConstituenta}.
|
||||
*/
|
||||
export interface ICstMovetoData extends IConstituentaList {
|
||||
move_to: Position;
|
||||
move_to: number; // Note: 0-base index
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -120,7 +120,6 @@ export function createMockConstituenta(id: ConstituentaID, alias: string, commen
|
|||
children: [],
|
||||
children_alias: [],
|
||||
is_simple_expression: false,
|
||||
order: -1,
|
||||
schema: -1,
|
||||
alias: alias,
|
||||
convention: comment,
|
||||
|
@ -157,8 +156,17 @@ export function isMockCst(cst: IConstituenta) {
|
|||
* Apply filter based on start {@link IConstituenta} type.
|
||||
*/
|
||||
export function applyFilterCategory(start: IConstituenta, schema: IRSForm): IConstituenta[] {
|
||||
const nextCategory = schema.items.find(cst => cst.order > start.order && cst.cst_type === CATEGORY_CST_TYPE);
|
||||
return schema.items.filter(cst => cst.order >= start.order && (!nextCategory || cst.order < nextCategory.order));
|
||||
const startIndex = schema.items.indexOf(start);
|
||||
if (startIndex === -1) {
|
||||
return [];
|
||||
}
|
||||
const nextCategoryIndex = schema.items.findIndex(
|
||||
(cst, index) => index > startIndex && cst.cst_type === CATEGORY_CST_TYPE
|
||||
);
|
||||
|
||||
return schema.items.filter(
|
||||
(_, index) => index >= startIndex && (nextCategoryIndex === -1 || index < nextCategoryIndex)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -123,7 +123,7 @@ function FormCreateItem() {
|
|||
|
||||
return (
|
||||
<form className={clsx('cc-column', 'min-w-[30rem] max-w-[30rem] mx-auto', 'px-6 py-3')} onSubmit={handleSubmit}>
|
||||
<h1>
|
||||
<h1 className='select-none'>
|
||||
{itemType == LibraryItemType.RSFORM ? (
|
||||
<Overlay position='top-0 right-[0.5rem]'>
|
||||
<input
|
||||
|
@ -144,7 +144,7 @@ function FormCreateItem() {
|
|||
Создание схемы
|
||||
</h1>
|
||||
|
||||
{fileName ? <Label text={`Загружен файл: ${fileName}`} /> : null}
|
||||
{fileName ? <Label className='text-wrap' text={`Загружен файл: ${fileName}`} /> : null}
|
||||
|
||||
<TextInput
|
||||
id='schema_title'
|
||||
|
|
|
@ -94,7 +94,7 @@ function HelpRSEditor() {
|
|||
<IconTree className='inline-icon' /> отображение{' '}
|
||||
<LinkTopic text='дерева разбора' topic={HelpTopic.UI_FORMULA_TREE} />
|
||||
</li>
|
||||
<li>Ctrl + Пробел дополняет до незанятого имени</li>
|
||||
<li>Ctrl + Пробел вставка незанятого имени / замена проекции</li>
|
||||
|
||||
<h2>Термин и Текстовое определение</h2>
|
||||
<li>
|
||||
|
|
|
@ -49,7 +49,7 @@ function EditorOssCard({ isModified, onDestroy, setIsModified }: EditorOssCardPr
|
|||
/>
|
||||
<AnimateFade
|
||||
onKeyDown={handleInput}
|
||||
className={clsx('md:w-fit md:max-w-fit max-w-[32rem]', 'mx-auto ', 'flex flex-col md:flex-row px-6')}
|
||||
className={clsx('md:w-fit md:max-w-fit max-w-[32rem]', 'mx-auto pt-[1.9rem]', 'flex flex-col md:flex-row px-6')}
|
||||
>
|
||||
<FlexColumn className='px-3'>
|
||||
<FormOSS id={globals.library_item_editor} isModified={isModified} setIsModified={setIsModified} />
|
||||
|
|
|
@ -40,7 +40,7 @@ interface OssFlowProps {
|
|||
}
|
||||
|
||||
function OssFlow({ isModified, setIsModified }: OssFlowProps) {
|
||||
const { calculateHeight, colors } = useConceptOptions();
|
||||
const { mainHeight, colors } = useConceptOptions();
|
||||
const model = useOSS();
|
||||
const controller = useOssEdit();
|
||||
const flow = useReactFlow();
|
||||
|
@ -342,8 +342,6 @@ function OssFlow({ isModified, setIsModified }: OssFlowProps) {
|
|||
}
|
||||
}
|
||||
|
||||
const canvasHeight = useMemo(() => calculateHeight('1.75rem + 4px'), [calculateHeight]);
|
||||
|
||||
const OssNodeTypes: NodeTypes = useMemo(
|
||||
() => ({
|
||||
synthesis: OperationNode,
|
||||
|
@ -352,6 +350,8 @@ function OssFlow({ isModified, setIsModified }: OssFlowProps) {
|
|||
[]
|
||||
);
|
||||
|
||||
// proOptions={{ hideAttribution: true }}
|
||||
|
||||
const graph = useMemo(
|
||||
() => (
|
||||
<ReactFlow
|
||||
|
@ -360,7 +360,6 @@ function OssFlow({ isModified, setIsModified }: OssFlowProps) {
|
|||
onNodesChange={handleNodesChange}
|
||||
onEdgesChange={onEdgesChange}
|
||||
onNodeDoubleClick={handleNodeDoubleClick}
|
||||
proOptions={{ hideAttribution: true }}
|
||||
fitView
|
||||
nodeTypes={OssNodeTypes}
|
||||
maxZoom={2}
|
||||
|
@ -389,7 +388,7 @@ function OssFlow({ isModified, setIsModified }: OssFlowProps) {
|
|||
|
||||
return (
|
||||
<AnimateFade tabIndex={-1} onKeyDown={handleKeyDown}>
|
||||
<Overlay position='top-0 pt-1 right-1/2 translate-x-1/2' className='rounded-b-2xl cc-blur'>
|
||||
<Overlay position='top-[1.9rem] pt-1 right-1/2 translate-x-1/2' className='rounded-b-2xl cc-blur'>
|
||||
<ToolbarOssGraph
|
||||
isModified={isModified}
|
||||
showGrid={showGrid}
|
||||
|
@ -419,7 +418,7 @@ function OssFlow({ isModified, setIsModified }: OssFlowProps) {
|
|||
{...menuProps}
|
||||
/>
|
||||
) : null}
|
||||
<div className='relative w-[100vw]' style={{ height: canvasHeight }}>
|
||||
<div className='relative w-[100vw]' style={{ height: mainHeight }}>
|
||||
{graph}
|
||||
</div>
|
||||
</AnimateFade>
|
||||
|
|
|
@ -9,6 +9,7 @@ import { toast } from 'react-toastify';
|
|||
import { urls } from '@/app/urls';
|
||||
import InfoError, { ErrorData } from '@/components/info/InfoError';
|
||||
import Loader from '@/components/ui/Loader';
|
||||
import Overlay from '@/components/ui/Overlay';
|
||||
import TabLabel from '@/components/ui/TabLabel';
|
||||
import TextURL from '@/components/ui/TextURL';
|
||||
import AnimateFade from '@/components/wrap/AnimateFade';
|
||||
|
@ -37,7 +38,7 @@ function OssTabs() {
|
|||
const activeTab = query.get('tab') ? (Number(query.get('tab')) as OssTabID) : OssTabID.GRAPH;
|
||||
const { user } = useAuth();
|
||||
|
||||
const { calculateHeight, setNoFooter } = useConceptOptions();
|
||||
const { setNoFooter } = useConceptOptions();
|
||||
const { schema, loading, loadingError: errorLoading } = useOSS();
|
||||
const { destroyItem } = useLibrary();
|
||||
|
||||
|
@ -107,8 +108,6 @@ function OssTabs() {
|
|||
});
|
||||
}, [schema, destroyItem, router]);
|
||||
|
||||
const panelHeight = useMemo(() => calculateHeight('1.625rem + 2px'), [calculateHeight]);
|
||||
|
||||
const cardPanel = useMemo(
|
||||
() => (
|
||||
<TabPanel>
|
||||
|
@ -143,14 +142,16 @@ function OssTabs() {
|
|||
selectedTabClassName='clr-selected'
|
||||
className='flex flex-col mx-auto min-w-fit'
|
||||
>
|
||||
<TabList className={clsx('mx-auto w-fit', 'flex items-stretch', 'border-b-2 border-x-2 divide-x-2')}>
|
||||
<MenuOssTabs onDestroy={onDestroySchema} />
|
||||
<Overlay position='top-0 right-1/2 translate-x-1/2' layer='z-sticky'>
|
||||
<TabList className={clsx('w-fit', 'flex items-stretch', 'border-b-2 border-x-2 divide-x-2')}>
|
||||
<MenuOssTabs onDestroy={onDestroySchema} />
|
||||
|
||||
<TabLabel label='Карточка' title={schema.title ?? ''} />
|
||||
<TabLabel label='Граф' />
|
||||
</TabList>
|
||||
<TabLabel label='Карточка' title={schema.title ?? ''} />
|
||||
<TabLabel label='Граф' />
|
||||
</TabList>
|
||||
</Overlay>
|
||||
|
||||
<AnimateFade className='overflow-y-auto' style={{ maxHeight: panelHeight }}>
|
||||
<AnimateFade>
|
||||
{cardPanel}
|
||||
{graphPanel}
|
||||
</AnimateFade>
|
||||
|
|
|
@ -28,7 +28,7 @@ interface EditorConstituentaProps {
|
|||
function EditorConstituenta({ activeCst, isModified, setIsModified, onOpenEdit }: EditorConstituentaProps) {
|
||||
const controller = useRSEdit();
|
||||
const windowSize = useWindowSize();
|
||||
const { calculateHeight } = useConceptOptions();
|
||||
const { mainHeight } = useConceptOptions();
|
||||
|
||||
const [showList, setShowList] = useLocalStorage(storage.rseditShowList, true);
|
||||
const [toggleReset, setToggleReset] = useState(false);
|
||||
|
@ -39,7 +39,6 @@ function EditorConstituenta({ activeCst, isModified, setIsModified, onOpenEdit }
|
|||
);
|
||||
|
||||
const isNarrow = useMemo(() => !!windowSize.width && windowSize.width <= SIDELIST_LAYOUT_THRESHOLD, [windowSize]);
|
||||
const panelHeight = useMemo(() => calculateHeight('1.75rem + 4px'), [calculateHeight]);
|
||||
|
||||
function handleInput(event: React.KeyboardEvent<HTMLDivElement>) {
|
||||
if (disabled) {
|
||||
|
@ -79,7 +78,7 @@ function EditorConstituenta({ activeCst, isModified, setIsModified, onOpenEdit }
|
|||
}
|
||||
|
||||
return (
|
||||
<div className='overflow-y-auto min-h-[20rem]' style={{ maxHeight: panelHeight }}>
|
||||
<>
|
||||
<ToolbarConstituenta
|
||||
activeCst={activeCst}
|
||||
disabled={disabled}
|
||||
|
@ -89,39 +88,41 @@ function EditorConstituenta({ activeCst, isModified, setIsModified, onOpenEdit }
|
|||
onReset={() => setToggleReset(prev => !prev)}
|
||||
onToggleList={() => setShowList(prev => !prev)}
|
||||
/>
|
||||
<div
|
||||
tabIndex={-1}
|
||||
className={clsx(
|
||||
'max-w-[95rem] mx-auto', // prettier: split lines
|
||||
'flex',
|
||||
{ 'flex-col md:items-center': isNarrow }
|
||||
)}
|
||||
onKeyDown={handleInput}
|
||||
>
|
||||
<FormConstituenta
|
||||
disabled={disabled}
|
||||
id={globals.constituenta_editor}
|
||||
state={activeCst}
|
||||
isModified={isModified}
|
||||
toggleReset={toggleReset}
|
||||
setIsModified={setIsModified}
|
||||
onEditTerm={controller.editTermForms}
|
||||
onRename={controller.renameCst}
|
||||
onOpenEdit={onOpenEdit}
|
||||
/>
|
||||
<AnimatePresence initial={false}>
|
||||
{showList ? (
|
||||
<ViewConstituents
|
||||
schema={controller.schema}
|
||||
expression={activeCst?.definition_formal ?? ''}
|
||||
isBottom={isNarrow}
|
||||
activeCst={activeCst}
|
||||
onOpenEdit={onOpenEdit}
|
||||
/>
|
||||
) : null}
|
||||
</AnimatePresence>
|
||||
<div className='pt-[1.9rem] overflow-y-auto min-h-[20rem]' style={{ maxHeight: mainHeight }}>
|
||||
<div
|
||||
tabIndex={-1}
|
||||
className={clsx(
|
||||
'max-w-[95rem] mx-auto', // prettier: split lines
|
||||
'flex',
|
||||
{ 'flex-col md:items-center': isNarrow }
|
||||
)}
|
||||
onKeyDown={handleInput}
|
||||
>
|
||||
<FormConstituenta
|
||||
disabled={disabled}
|
||||
id={globals.constituenta_editor}
|
||||
state={activeCst}
|
||||
isModified={isModified}
|
||||
toggleReset={toggleReset}
|
||||
setIsModified={setIsModified}
|
||||
onEditTerm={controller.editTermForms}
|
||||
onRename={controller.renameCst}
|
||||
onOpenEdit={onOpenEdit}
|
||||
/>
|
||||
<AnimatePresence initial={false}>
|
||||
{showList ? (
|
||||
<ViewConstituents
|
||||
schema={controller.schema}
|
||||
expression={activeCst?.definition_formal ?? ''}
|
||||
isBottom={isNarrow}
|
||||
activeCst={activeCst}
|
||||
onOpenEdit={onOpenEdit}
|
||||
/>
|
||||
) : null}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ function ToolbarConstituenta({
|
|||
|
||||
return (
|
||||
<Overlay
|
||||
position='top-1 right-1/2 translate-x-1/2 xs:right-4 xs:translate-x-0 md:right-1/2 md:translate-x-1/2'
|
||||
position='cc-tab-tools right-1/2 translate-x-1/2 xs:right-4 xs:translate-x-0 md:right-1/2 md:translate-x-1/2'
|
||||
className='cc-icons outline-none transition-all duration-500'
|
||||
>
|
||||
{controller.schema && controller.schema?.oss.length > 0 ? (
|
||||
|
|
|
@ -32,7 +32,7 @@ function ParsingResult({ isOpen, data, disabled, onShowError }: ParsingResultPro
|
|||
<p
|
||||
tabIndex={-1}
|
||||
key={`error-${index}`}
|
||||
className={`clr-text-red ${disabled ? '' : 'cursor-pointer'}`}
|
||||
className={`clr-text-red break-all ${disabled ? '' : 'cursor-pointer'}`}
|
||||
onClick={disabled ? undefined : () => onShowError(error)}
|
||||
>
|
||||
<span className='mr-1 font-semibold underline'>
|
||||
|
|
|
@ -49,7 +49,7 @@ function EditorRSFormCard({ isModified, onDestroy, setIsModified }: EditorRSForm
|
|||
/>
|
||||
<AnimateFade
|
||||
onKeyDown={handleInput}
|
||||
className={clsx('md:w-fit md:max-w-fit max-w-[32rem] mx-auto', 'flex flex-col md:flex-row px-6')}
|
||||
className={clsx('md:w-fit md:max-w-fit max-w-[32rem] mx-auto', 'flex flex-col md:flex-row px-6 pt-[1.9rem]')}
|
||||
>
|
||||
<FlexColumn className='flex-shrink'>
|
||||
<FormRSForm id={globals.library_item_editor} isModified={isModified} setIsModified={setIsModified} />
|
||||
|
|
|
@ -45,7 +45,7 @@ function ToolbarRSFormCard({ modified, controller, onSubmit, onDestroy }: Toolba
|
|||
}, [controller]);
|
||||
|
||||
return (
|
||||
<Overlay position='top-1 right-1/2 translate-x-1/2' className='cc-icons'>
|
||||
<Overlay position='cc-tab-tools' className='cc-icons'>
|
||||
{ossSelector}
|
||||
{controller.isMutable || modified ? (
|
||||
<MiniButton
|
||||
|
|
|
@ -142,7 +142,7 @@ function EditorRSList({ onOpenEdit }: EditorRSListProps) {
|
|||
return (
|
||||
<>
|
||||
{controller.isContentEditable ? <ToolbarRSList /> : null}
|
||||
<AnimateFade tabIndex={-1} onKeyDown={handleKeyDown}>
|
||||
<AnimateFade tabIndex={-1} onKeyDown={handleKeyDown} className='pt-[1.9rem]'>
|
||||
{controller.isContentEditable ? (
|
||||
<div className='flex items-center border-b'>
|
||||
<div className='px-2'>
|
||||
|
|
|
@ -28,7 +28,7 @@ function ToolbarRSList() {
|
|||
|
||||
return (
|
||||
<Overlay
|
||||
position='top-1 right-4 translate-x-0 md:right-1/2 md:translate-x-1/2'
|
||||
position='cc-tab-tools right-4 translate-x-0 md:right-1/2 md:translate-x-1/2'
|
||||
className='cc-icons items-start outline-none transition-all duration-500'
|
||||
>
|
||||
{controller.schema && controller.schema?.oss.length > 0 ? (
|
||||
|
|
|
@ -287,7 +287,7 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) {
|
|||
);
|
||||
|
||||
return (
|
||||
<AnimateFade tabIndex={-1} onKeyDown={handleKeyDown}>
|
||||
<>
|
||||
<AnimatePresence>
|
||||
{showParamsDialog ? (
|
||||
<DlgGraphParams
|
||||
|
@ -298,10 +298,7 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) {
|
|||
) : null}
|
||||
</AnimatePresence>
|
||||
|
||||
<Overlay
|
||||
position='top-0 pt-1 right-1/2 translate-x-1/2'
|
||||
className='flex flex-col items-center rounded-b-2xl cc-blur'
|
||||
>
|
||||
<Overlay position='cc-tab-tools' className='flex flex-col items-center rounded-b-2xl cc-blur'>
|
||||
<ToolbarTermGraph
|
||||
is3D={is3D}
|
||||
orbit={orbit}
|
||||
|
@ -352,39 +349,41 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) {
|
|||
) : null}
|
||||
</Overlay>
|
||||
|
||||
<SelectedCounter
|
||||
hideZero
|
||||
totalCount={controller.schema?.stats?.count_all ?? 0}
|
||||
selectedCount={controller.selected.length}
|
||||
position='top-[4.3rem] sm:top-[2rem] left-0'
|
||||
/>
|
||||
<AnimateFade tabIndex={-1} onKeyDown={handleKeyDown}>
|
||||
<SelectedCounter
|
||||
hideZero
|
||||
totalCount={controller.schema?.stats?.count_all ?? 0}
|
||||
selectedCount={controller.selected.length}
|
||||
position='top-[4.3rem] sm:top-[2rem] left-0'
|
||||
/>
|
||||
|
||||
{hoverCst && hoverCstDebounced && hoverCst === hoverCstDebounced ? (
|
||||
<Overlay
|
||||
layer='z-tooltip'
|
||||
position={clsx('top-[1.6rem]', { 'left-[2.6rem]': hoverLeft, 'right-[2.6rem]': !hoverLeft })}
|
||||
className={clsx(
|
||||
'w-[25rem] max-h-[calc(100dvh-15rem)]',
|
||||
'px-3',
|
||||
'cc-scroll-y',
|
||||
'border shadow-md',
|
||||
'clr-input',
|
||||
'text-sm'
|
||||
)}
|
||||
>
|
||||
<InfoConstituenta className='pt-1 pb-2' data={hoverCstDebounced} />
|
||||
{hoverCst && hoverCstDebounced && hoverCst === hoverCstDebounced ? (
|
||||
<Overlay
|
||||
layer='z-tooltip'
|
||||
position={clsx('top-[3.5rem]', { 'left-[2.6rem]': hoverLeft, 'right-[2.6rem]': !hoverLeft })}
|
||||
className={clsx(
|
||||
'w-[25rem] max-h-[calc(100dvh-15rem)]',
|
||||
'px-3',
|
||||
'cc-scroll-y',
|
||||
'border shadow-md',
|
||||
'clr-input',
|
||||
'text-sm'
|
||||
)}
|
||||
>
|
||||
<InfoConstituenta className='pt-1 pb-2' data={hoverCstDebounced} />
|
||||
</Overlay>
|
||||
) : null}
|
||||
|
||||
<Overlay position='top-[8.15rem] sm:top-[5.9rem] left-0' className='flex gap-1'>
|
||||
<div className='flex flex-col ml-2 w-[13.5rem]'>
|
||||
{selectors}
|
||||
{viewHidden}
|
||||
</div>
|
||||
</Overlay>
|
||||
) : null}
|
||||
|
||||
<Overlay position='top-[6.25rem] sm:top-[4rem] left-0' className='flex gap-1'>
|
||||
<div className='flex flex-col ml-2 w-[13.5rem]'>
|
||||
{selectors}
|
||||
{viewHidden}
|
||||
</div>
|
||||
</Overlay>
|
||||
|
||||
{graph}
|
||||
</AnimateFade>
|
||||
{graph}
|
||||
</AnimateFade>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@ function TermGraph({
|
|||
onSelect,
|
||||
onDeselect
|
||||
}: TermGraphProps) {
|
||||
const { calculateHeight, darkMode } = useConceptOptions();
|
||||
const { mainHeight, darkMode } = useConceptOptions();
|
||||
|
||||
const { selections, setSelections } = useSelection({
|
||||
ref: graphRef,
|
||||
|
@ -111,10 +111,8 @@ function TermGraph({
|
|||
return 'calc(100vw - 1rem)';
|
||||
}, []);
|
||||
|
||||
const canvasHeight = useMemo(() => calculateHeight('1.75rem + 4px'), [calculateHeight]);
|
||||
|
||||
return (
|
||||
<div className='relative outline-none' style={{ width: canvasWidth, height: canvasHeight }}>
|
||||
<div className='relative outline-none' style={{ width: canvasWidth, height: mainHeight }}>
|
||||
<GraphUI
|
||||
nodes={nodes}
|
||||
edges={edges}
|
||||
|
|
|
@ -406,8 +406,8 @@ export const RSEditState = ({
|
|||
}
|
||||
return Math.min(prev, index);
|
||||
}, -1);
|
||||
const target = Math.max(0, currentIndex - 1) + 1;
|
||||
const data = {
|
||||
const target = Math.max(0, currentIndex - 1);
|
||||
const data: ICstMovetoData = {
|
||||
items: selected,
|
||||
move_to: target
|
||||
};
|
||||
|
@ -430,7 +430,7 @@ export const RSEditState = ({
|
|||
return Math.max(prev, index);
|
||||
}
|
||||
}, -1);
|
||||
const target = Math.min(model.schema.items.length - 1, currentIndex - count + 2) + 1;
|
||||
const target = Math.min(model.schema.items.length - 1, currentIndex - count + 2);
|
||||
const data: ICstMovetoData = {
|
||||
items: selected,
|
||||
move_to: target
|
||||
|
|
|
@ -10,6 +10,7 @@ import { urls } from '@/app/urls';
|
|||
import InfoError, { ErrorData } from '@/components/info/InfoError';
|
||||
import Divider from '@/components/ui/Divider';
|
||||
import Loader from '@/components/ui/Loader';
|
||||
import Overlay from '@/components/ui/Overlay';
|
||||
import TabLabel from '@/components/ui/TabLabel';
|
||||
import TextURL from '@/components/ui/TextURL';
|
||||
import AnimateFade from '@/components/wrap/AnimateFade';
|
||||
|
@ -45,7 +46,7 @@ function RSTabs() {
|
|||
const version = query.get('v') ? Number(query.get('v')) : undefined;
|
||||
const cstQuery = query.get('active');
|
||||
|
||||
const { setNoFooter, calculateHeight } = useConceptOptions();
|
||||
const { setNoFooter } = useConceptOptions();
|
||||
const { schema, loading, errorLoading, isArchive, itemID } = useRSForm();
|
||||
const library = useLibrary();
|
||||
const oss = useGlobalOss();
|
||||
|
@ -73,7 +74,7 @@ function RSTabs() {
|
|||
}, [schema, schema?.title]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
setNoFooter(activeTab === RSTabID.CST_EDIT || activeTab === RSTabID.CST_LIST);
|
||||
setNoFooter(activeTab !== RSTabID.CARD);
|
||||
setIsModified(false);
|
||||
if (activeTab === RSTabID.CST_EDIT) {
|
||||
const cstID = Number(cstQuery);
|
||||
|
@ -189,8 +190,6 @@ function RSTabs() {
|
|||
});
|
||||
}, [schema, library, oss, router]);
|
||||
|
||||
const panelHeight = useMemo(() => calculateHeight('1.625rem + 2px'), [calculateHeight]);
|
||||
|
||||
const cardPanel = useMemo(
|
||||
() => (
|
||||
<TabPanel>
|
||||
|
@ -255,19 +254,23 @@ function RSTabs() {
|
|||
selectedTabClassName='clr-selected'
|
||||
className='flex flex-col mx-auto min-w-fit'
|
||||
>
|
||||
<TabList className={clsx('mx-auto w-fit', 'flex items-stretch', 'border-b-2 border-x-2 divide-x-2')}>
|
||||
<MenuRSTabs onDestroy={onDestroySchema} />
|
||||
<Overlay position='top-0 right-1/2 translate-x-1/2' layer='z-sticky'>
|
||||
<TabList className={clsx('mx-auto w-fit', 'flex items-stretch', 'border-b-2 border-x-2 divide-x-2')}>
|
||||
<MenuRSTabs onDestroy={onDestroySchema} />
|
||||
|
||||
<TabLabel label='Карточка' titleHtml={`${schema.title ?? ''}<br />Версия: ${labelVersion(schema)}`} />
|
||||
<TabLabel
|
||||
label='Содержание'
|
||||
titleHtml={`Конституент: ${schema.stats?.count_all ?? 0}<br />Ошибок: ${schema.stats?.count_errors ?? 0}`}
|
||||
/>
|
||||
<TabLabel label='Редактор' />
|
||||
<TabLabel label='Граф термов' />
|
||||
</TabList>
|
||||
<TabLabel label='Карточка' titleHtml={`${schema.title ?? ''}<br />Версия: ${labelVersion(schema)}`} />
|
||||
<TabLabel
|
||||
label='Содержание'
|
||||
titleHtml={`Конституент: ${schema.stats?.count_all ?? 0}<br />Ошибок: ${
|
||||
schema.stats?.count_errors ?? 0
|
||||
}`}
|
||||
/>
|
||||
<TabLabel label='Редактор' />
|
||||
<TabLabel label='Граф термов' />
|
||||
</TabList>
|
||||
</Overlay>
|
||||
|
||||
<AnimateFade className='overflow-y-auto overflow-x-hidden' style={{ maxHeight: panelHeight }}>
|
||||
<AnimateFade className='overflow-x-hidden'>
|
||||
{cardPanel}
|
||||
{listPanel}
|
||||
{editorPanel}
|
||||
|
|
|
@ -64,6 +64,14 @@
|
|||
cursor: default;
|
||||
}
|
||||
|
||||
.react-flow__attribution {
|
||||
background-color: transparent;
|
||||
color: var(--cl-fg-60);
|
||||
.dark & {
|
||||
color: var(--cd-fg-60);
|
||||
}
|
||||
}
|
||||
|
||||
:is(.react-flow__node-input, .react-flow__node-synthesis) {
|
||||
cursor: pointer;
|
||||
|
||||
|
|
|
@ -49,17 +49,40 @@
|
|||
}
|
||||
}
|
||||
|
||||
:is(.clr-app, .clr-footer, .cc-modal-backdrop, .clr-btn-nav, .clr-input:disabled) {
|
||||
:is(.clr-app, .clr-btn-nav) {
|
||||
background-color: var(--cl-bg-100);
|
||||
.dark & {
|
||||
background-color: var(--cd-bg-100);
|
||||
}
|
||||
}
|
||||
|
||||
:is(.clr-input) {
|
||||
.clr-footer {
|
||||
color: var(--cl-fg-60);
|
||||
background-color: var(--cl-bg-100);
|
||||
.dark & {
|
||||
color: var(--cd-fg-60);
|
||||
background-color: var(--cd-bg-100);
|
||||
}
|
||||
}
|
||||
|
||||
.cc-modal-backdrop {
|
||||
opacity: 0.5;
|
||||
background-color: var(--cl-bg-100);
|
||||
.dark & {
|
||||
background-color: var(--cd-bg-100);
|
||||
}
|
||||
}
|
||||
|
||||
.clr-input {
|
||||
background-color: var(--cl-bg-120);
|
||||
&:disabled {
|
||||
background-color: var(--cl-bg-100);
|
||||
}
|
||||
.dark & {
|
||||
background-color: var(--cd-bg-120);
|
||||
&:disabled {
|
||||
background-color: var(--cd-bg-100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -126,13 +149,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
.clr-footer {
|
||||
color: var(--cl-fg-60);
|
||||
.dark & {
|
||||
color: var(--cd-fg-60);
|
||||
}
|
||||
}
|
||||
|
||||
:is(.clr-text-controls, .clr-btn-nav, .clr-btn-clear) {
|
||||
color: var(--cl-fg-80);
|
||||
&:disabled {
|
||||
|
@ -153,7 +169,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
:is(.clr-text-default, input:disabled, textarea:disabled) {
|
||||
:is(.clr-text-default, input:disabled:not(::placeholder), textarea:disabled:not(::placeholder)) {
|
||||
opacity: 1;
|
||||
-webkit-text-fill-color: var(--cl-fg-100);
|
||||
color: var(--cl-fg-100);
|
||||
|
@ -205,15 +221,15 @@
|
|||
@apply clr-text-primary;
|
||||
}
|
||||
|
||||
.cc-tab-tools {
|
||||
@apply top-[1.9rem] pt-1 right-1/2 translate-x-1/2;
|
||||
}
|
||||
|
||||
.cc-modal-blur {
|
||||
opacity: 0.3;
|
||||
backdrop-filter: blur(2px);
|
||||
}
|
||||
|
||||
.cc-modal-backdrop {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.cc-label {
|
||||
@apply text-sm font-medium cursor-default select-text whitespace-nowrap;
|
||||
}
|
||||
|
|
|
@ -357,6 +357,10 @@ export class CodeMirrorWrapper {
|
|||
return this.ref.view.state.doc.sliceString(from, to);
|
||||
}
|
||||
|
||||
getWord(position: number): SelectionRange | null {
|
||||
return this.ref.view.state.wordAt(position);
|
||||
}
|
||||
|
||||
getSelection(): SelectionRange {
|
||||
return this.ref.view.state.selection.main;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user