From a39f305fe4bfaa3f452983a709ed30ec91516c84 Mon Sep 17 00:00:00 2001 From: everythingfades Date: Fri, 16 Jan 2026 01:00:00 +0000 Subject: [PATCH 01/36] feat: first FSA draft with Reactflow, the wizard definitely need some fix, I dont think it should be the same with Input --- FSA.md | 97 +++++++++ README.md | 4 + dev/index.html | 14 ++ dev/main.tsx | 43 ++++ package.json | 10 +- src/types/FSA/FSA.backend.ts | 54 +++++ src/types/FSA/FSA.component.tsx | 135 +++++++++++++ src/types/FSA/index.tsx | 38 ++++ src/types/FSA/type.ts | 24 +++ src/types/index.ts | 4 + vite.config.ts | 1 + yarn.lock | 336 +++++++++++++++++++++++++++++++- 12 files changed, 752 insertions(+), 8 deletions(-) create mode 100644 FSA.md create mode 100644 dev/index.html create mode 100644 dev/main.tsx create mode 100644 src/types/FSA/FSA.backend.ts create mode 100644 src/types/FSA/FSA.component.tsx create mode 100644 src/types/FSA/index.tsx create mode 100644 src/types/FSA/type.ts diff --git a/FSA.md b/FSA.md new file mode 100644 index 0000000..5a76502 --- /dev/null +++ b/FSA.md @@ -0,0 +1,97 @@ +# FSA Module Documentation + +This module provides a visual editor for Finite State Automata (FSA) built with **React Flow**. It is designed to bridge the gap between a **Python-style backend** (deeply nested objects) and a **TypeScript/Zod frontend** (restricted to 2-level JSON nesting). + +## 1. The Core Data Structures + +### Frontend Schema (`FSA`) + +To satisfy the `jsonNestedSchema` (which permits only 2 levels of nesting), we use a "flattened" string format for transitions. + +```typescript +// level 1: Object properties +// level 2: String arrays +export interface FSA { + states: string[]; + alphabet: string[]; + transitions: string[]; // Format: "from_state|symbol|to_state" + initial_state: string; + accept_states: string[]; +} + +``` + +### Backend Schema (`BackendFSA`) + +The Python backend uses standard object-oriented nesting for transitions. + +```typescript +export interface BackendFSA { + states: string[]; + alphabet: string[]; + transitions: Array<{ + from_state: string; + to_state: string; + symbol: string; + }>; + initial_state: string; + accept_states: string[]; +} + +``` + +--- + +## 2. Key Components + +### `FSAInput.component.tsx` + +The primary visual editor. + +* **State Management**: Uses `useNodesState` and `useEdgesState` from React Flow. +* **Syncing**: Every change (adding a node, connecting an edge, deleting) triggers a `syncChanges` function that converts the visual graph back into the flattened `FSA` interface. +* **User Interactions**: +* **Add State**: Prompt-based creation of new nodes. +* **Connections**: Dragging from one node to another prompts for a transition symbol (defaults to `ε`). +* **Deletion**: Selecting a node/edge and pressing **Backspace** or **Delete** removes the element and cleans up orphaned transitions. + + + +### `FSAResponseAreaTub.ts` + +The controller class that integrates the editor into the application wizard. + +* **Resilience**: Uses `defaultFSA` to prevent `undefined` errors. +* **Validation**: Uses `fsaAnswerSchema.safeParse()` to guard against corrupted data. + +--- + +## 3. Transformation Logic (`FSAConverter`) + +Since the frontend and backend see the data differently, the `FSAConverter` utility is used at the network boundary. + +| Method | Source | Target | Reason | +| --- | --- | --- | --- | +| `toFrontend()` | `BackendFSA` | `FSA` | Unpacks objects into `"q0 | +| `toBackend()` | `FSA` | `BackendFSA` | Packs strings back into objects for the Python service logic. | + +--- + +## 4. Usage in the Pipeline + +1. **Load**: Data is fetched from the backend (`BackendFSA`). +2. **Convert**: `FSAConverter.toFrontend()` is called. +3. **Edit**: The user interacts with `FSAInput`. The `answer` state stays in the flattened `FSA` format. +4. [TODO] **Save**: On `onSubmit` or `onChange`, `FSAConverter.toBackend()` is called to transform the data back to the format the server expects. + +--- + +## 5. Important Implementation Notes + +* **Unique Identifiers**: Edge IDs in React Flow are generated as ``e-${from}-${symbol}-${to}``. If the automaton is Non-Deterministic (NFA), ensure symbol uniqueness or add a UUID to the ID string. +* **Visual Cues**: +* **Initial State**: Nodes matching `initial_state` are colored with a light teal background. +* **Accept States**: Nodes in `accept_states` are rendered with a double-border (4px double). + + +* **Alphabet Consistency**: The `alphabet` array is automatically derived from the unique labels present in the transitions during the sync process. \ No newline at end of file diff --git a/README.md b/README.md index 35d5e5a..aab700e 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,10 @@ Develop local TypeScript code to create custom response area types, and see them previewed live in the main application. When they're ready, provide your code to the Lambda Feedback team, who will consider including it in the main application after (human) review. +## FSA Response Section + +please refer to [FSA.md](./FSA.md) + ## Overview To create a new response area type, you'll need to: diff --git a/dev/index.html b/dev/index.html new file mode 100644 index 0000000..0894690 --- /dev/null +++ b/dev/index.html @@ -0,0 +1,14 @@ + + + + + + Library Dev Sandbox + + +
+ + + + + diff --git a/dev/main.tsx b/dev/main.tsx new file mode 100644 index 0000000..0e76b9b --- /dev/null +++ b/dev/main.tsx @@ -0,0 +1,43 @@ +import { IModularResponseSchema } from '@modules/shared/schemas/question-form.schema' +import React, { useState } from 'react' +import ReactDOM from 'react-dom/client' + +import { FSAResponseAreaTub } from '../src/types/FSA' + +const tub = new FSAResponseAreaTub() + +function Sandbox() { + const [answer, setAnswer] = + useState(null) + + const [allowSave, setAllowSave] = useState(true) + + return ( + <> +

Input

+ console.log('submit')} + handleDraftSave={() => console.log('draft save')} + displayMode="normal" + hasPreview + /> + +
+ +

Wizard

+ console.log('wizard change', val)} + setAllowSave={setAllowSave} + /> + + ) +} + +ReactDOM.createRoot(document.getElementById('root')!).render( + + + +) diff --git a/package.json b/package.json index 66e29ee..1698e24 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,8 @@ "ci": "run-p format lint typecheck", "fix": "run-s format:fix lint:fix", "preview": "vite preview", - "dev": "run-p build:watch preview" + "dev": "run-p build:watch preview", + "dev:fsa": "vite" }, "dependencies": { "@date-io/date-fns": "^2.13.2", @@ -43,9 +44,10 @@ "next": "^14.2.4", "react": "^18.3.1", "react-dom": "^18.3.1", - "react-katex": "^3.0.1", "react-hook-form": "^7.31.2", + "react-katex": "^3.0.1", "react-query": "^3.39.0", + "reactflow": "^11.11.4", "tldraw": "^3.13.1", "tss-react": "^3.7.0", "zod": "^3.14.4" @@ -66,11 +68,11 @@ "@types/shuffle-seed": "^1.1.0", "@vitejs/plugin-react": "^4.5.2", "eslint": "^9.3.0", - "eslint-plugin-react": "^7.34.2", - "eslint-plugin-react-hooks": "^4.6.2", "eslint-config-prettier": "^9.1.0", "eslint-import-resolver-typescript": "^3.6.3", "eslint-plugin-import": "^2.31.0", + "eslint-plugin-react": "^7.34.2", + "eslint-plugin-react-hooks": "^4.6.2", "globals": "^16.2.0", "npm-run-all": "^4.1.5", "prettier": "^3.3.3", diff --git a/src/types/FSA/FSA.backend.ts b/src/types/FSA/FSA.backend.ts new file mode 100644 index 0000000..fd5a07b --- /dev/null +++ b/src/types/FSA/FSA.backend.ts @@ -0,0 +1,54 @@ +// FSA.converter.ts +// this is for the inconsistency between the backend pydantic models and the frontend compromsise +import { FSA } from './type'; + +/** + * Backend representation of an FSA transition + */ +export interface BackendTransition { + from_state: string; + to_state: string; + symbol: string; +} + +/** + * Backend representation of the full FSA + */ +export interface BackendFSA { + states: string[]; + alphabet: string[]; + transitions: BackendTransition[]; + initial_state: string; + accept_states: string[]; +} + +export const FSAConverter = { + /** + * Converts frontend FSA (flat transitions) to Backend FSA (object transitions) + */ + toBackend(frontendFsa: FSA): BackendFSA { + return { + ...frontendFsa, + transitions: frontendFsa.transitions.map((tStr) => { + const [from, symbol, to] = tStr.split('|'); + return { + from_state: from || '', + to_state: to || '', + symbol: symbol || '', + }; + }), + }; + }, + + /** + * Converts Backend FSA (object transitions) to frontend FSA (flat transitions) + */ + toFrontend(backendFsa: BackendFSA): FSA { + return { + ...backendFsa, + transitions: backendFsa.transitions.map( + (t) => `${t.from_state}|${t.symbol}|${t.to_state}` + ), + }; + }, +}; \ No newline at end of file diff --git a/src/types/FSA/FSA.component.tsx b/src/types/FSA/FSA.component.tsx new file mode 100644 index 0000000..fc03e35 --- /dev/null +++ b/src/types/FSA/FSA.component.tsx @@ -0,0 +1,135 @@ +import React, { useCallback, useMemo } from 'react'; +import ReactFlow, { + addEdge, + Background, + Controls, + Connection, + useNodesState, + useEdgesState, + MarkerType, + Edge, + Node, + OnNodesDelete +} from 'reactflow'; + +import 'reactflow/dist/style.css'; +import { FSA } from './type'; + +interface FSAInputProps { + answer: FSA; + onChange: (val: FSA) => void; +} + +export const FSAInput: React.FC = ({ answer, onChange }) => { + // 1. Unpack flattened strings into React Flow Edges + // we could use the convertor, but lets just keep this here + const initialEdges: Edge[] = useMemo(() => { + return (answer.transitions || []).reduce((acc: Edge[], tStr: string) => { + const [from, symbol, to] = tStr.split('|'); + if (from && symbol && to) { + acc.push({ + id: `e-${from}-${to}-${symbol}`, + source: from, + target: to, + label: symbol, + markerEnd: { type: MarkerType.ArrowClosed }, + }); + } + return acc; + }, []); + }, [answer.transitions]); + + const initialNodes: Node[] = useMemo(() => + answer.states.map((s, i) => ({ + id: s, + data: { label: s }, + position: { x: i * 150, y: 100 }, + style: { + border: answer.accept_states.includes(s) ? '4px double #333' : '1px solid #777', + background: s === answer.initial_state ? '#e6fffa' : '#fff', + borderRadius: '50%', width: 50, height: 50, + display: 'flex', alignItems: 'center', justifyContent: 'center' + } + })), [answer]); + + const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes); + const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges); + + // Sync helper to convert current Flow state back to FSA format + const syncChanges = useCallback((currentNodes: Node[], currentEdges: Edge[]) => { + const updatedFSA: FSA = { + ...answer, + states: currentNodes.map(n => n.id), + transitions: currentEdges.map(e => `${e.source}|${e.label}|${e.target}`), + alphabet: Array.from(new Set(currentEdges.map(e => String(e.label)))) + }; + onChange(updatedFSA); + }, [answer, onChange]); + + // Handle Adding Nodes + const addState = useCallback(() => { + const id = prompt("Enter state name (e.g. q2):"); + if (!id || nodes.find(n => n.id === id)) return; + + const newNode: Node = { + id, + data: { label: id }, + position: { x: Math.random() * 400, y: Math.random() * 400 }, + style: { border: '1px solid #777', borderRadius: '50%', width: 50, height: 50 } + }; + + const updatedNodes = [...nodes, newNode]; + setNodes(updatedNodes); + syncChanges(updatedNodes, edges); + }, [nodes, edges, setNodes, syncChanges]); + + // Handle Deleting Nodes (triggered by Backspace/Delete key by default in React Flow) + const onNodesDelete: OnNodesDelete = useCallback((deletedNodes) => { + const deletedIds = new Set(deletedNodes.map(n => n.id)); + const remainingNodes = nodes.filter(n => !deletedIds.has(n.id)); + // React Flow handles edge cleanup internally in the 'edges' state, + // but we need to ensure our sync uses the filtered edges. + const remainingEdges = edges.filter(e => !deletedIds.has(e.source) && !deletedIds.has(e.target)); + + syncChanges(remainingNodes, remainingEdges); + }, [nodes, edges, syncChanges]); + + const onConnect = useCallback((params: Connection) => { + const symbol = prompt("Transition symbol:") || 'ε'; + const newEdge = { ...params, label: symbol, markerEnd: { type: MarkerType.ArrowClosed } }; + + setEdges((eds) => { + const updatedEdges = addEdge(newEdge, eds); + syncChanges(nodes, updatedEdges); + return updatedEdges; + }); + }, [nodes, syncChanges, setEdges]); + + return ( +
+
+ + + Select a node/edge and press Backspace to delete. + +
+ +
+ + + + +
+
+ ); +}; \ No newline at end of file diff --git a/src/types/FSA/index.tsx b/src/types/FSA/index.tsx new file mode 100644 index 0000000..d7c4172 --- /dev/null +++ b/src/types/FSA/index.tsx @@ -0,0 +1,38 @@ +// FSAResponseArea.tub.ts +import { BaseResponseAreaProps, BaseResponseAreaWizardProps } from '../base-props.type'; +import { ResponseAreaTub } from '../response-area-tub'; + +import { FSAInput } from './FSA.component'; +import { fsaAnswerSchema, FSA, defaultFSA } from './type'; + +export class FSAResponseAreaTub extends ResponseAreaTub { + public readonly responseType = 'FSA'; + public readonly displayWideInput = true; + protected answerSchema = fsaAnswerSchema; + protected answer: FSA = defaultFSA; + + InputComponent = (props: BaseResponseAreaProps) => { + const parsedAnswer = this.answerSchema.safeParse(props.answer); + return ( + props.handleChange(val)} + /> + ); + } + + WizardComponent = (props: BaseResponseAreaWizardProps) => { + return ( + { + props.handleChange({ + responseType: this.responseType, + answer, + }); + }} + /> + ); + } +} \ No newline at end of file diff --git a/src/types/FSA/type.ts b/src/types/FSA/type.ts new file mode 100644 index 0000000..2801b2f --- /dev/null +++ b/src/types/FSA/type.ts @@ -0,0 +1,24 @@ +// this is kind of the compromise for the zod restricts IModularResponseSchema and the backend python schema cannot match that +// see file externals/modules/shared/schemas/question-form.schema.ts for details +// since that is a external module, we should not edit that file + +import { z } from 'zod'; + +export const fsaAnswerSchema = z.object({ + states: z.array(z.string()), + alphabet: z.array(z.string()), + // Flattened: Array of "from|symbol|to" strings + transitions: z.array(z.string()), + initial_state: z.string(), + accept_states: z.array(z.string()), +}); + +export type FSA = z.infer; + +export const defaultFSA: FSA = { + states: ['q0'], + alphabet: [], + transitions: [], + initial_state: 'q0', + accept_states: [] +}; \ No newline at end of file diff --git a/src/types/index.ts b/src/types/index.ts index daeacc0..63e11e1 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -3,6 +3,7 @@ import { z } from 'zod' import { CodeResponseAreaTub } from './Code' import { EssayResponseAreaTub } from './Essay' +import { FSAResponseAreaTub } from './FSA' import { MatrixResponseAreaTub } from './Matrix' import { MultipleChoiceResponseAreaTub } from './MultipleChoice' import { NumberResponseAreaTub } from './NumberInput' @@ -25,6 +26,7 @@ export const supportedResponseTypes = [ 'ESSAY', 'CODE', 'MILKDOWN', + 'FSA' ] if (typeof window !== 'undefined') { @@ -98,6 +100,8 @@ const createReponseAreaTub = (type: string): ResponseAreaTub => { return new EssayResponseAreaTub() case 'CODE': return new CodeResponseAreaTub() + case 'FSA': + return new FSAResponseAreaTub() case 'VOID': return new VoidResponseAreaTub() default: diff --git a/vite.config.ts b/vite.config.ts index 2b0af76..9b983a3 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -4,6 +4,7 @@ import { defineConfig } from 'vite' export default defineConfig({ plugins: [react()], + root: 'dev', // for dev only define: { 'process.env': JSON.stringify({ NODE_ENV: 'production' }), }, diff --git a/yarn.lock b/yarn.lock index 8ca7d57..ea1535e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1873,6 +1873,72 @@ "@react-spring/shared" "~9.7.5" "@react-spring/types" "~9.7.5" +"@reactflow/background@11.3.14": + version "11.3.14" + resolved "https://registry.npmmirror.com/@reactflow/background/-/background-11.3.14.tgz#778ca30174f3de77fc321459ab3789e66e71a699" + integrity sha512-Gewd7blEVT5Lh6jqrvOgd4G6Qk17eGKQfsDXgyRSqM+CTwDqRldG2LsWN4sNeno6sbqVIC2fZ+rAUBFA9ZEUDA== + dependencies: + "@reactflow/core" "11.11.4" + classcat "^5.0.3" + zustand "^4.4.1" + +"@reactflow/controls@11.2.14": + version "11.2.14" + resolved "https://registry.npmmirror.com/@reactflow/controls/-/controls-11.2.14.tgz#508ed2c40d23341b3b0919dd11e76fd49cf850c7" + integrity sha512-MiJp5VldFD7FrqaBNIrQ85dxChrG6ivuZ+dcFhPQUwOK3HfYgX2RHdBua+gx+40p5Vw5It3dVNp/my4Z3jF0dw== + dependencies: + "@reactflow/core" "11.11.4" + classcat "^5.0.3" + zustand "^4.4.1" + +"@reactflow/core@11.11.4": + version "11.11.4" + resolved "https://registry.npmmirror.com/@reactflow/core/-/core-11.11.4.tgz#89bd86d1862aa1416f3f49926cede7e8c2aab6a7" + integrity sha512-H4vODklsjAq3AMq6Np4LE12i1I4Ta9PrDHuBR9GmL8uzTt2l2jh4CiQbEMpvMDcp7xi4be0hgXj+Ysodde/i7Q== + dependencies: + "@types/d3" "^7.4.0" + "@types/d3-drag" "^3.0.1" + "@types/d3-selection" "^3.0.3" + "@types/d3-zoom" "^3.0.1" + classcat "^5.0.3" + d3-drag "^3.0.0" + d3-selection "^3.0.0" + d3-zoom "^3.0.0" + zustand "^4.4.1" + +"@reactflow/minimap@11.7.14": + version "11.7.14" + resolved "https://registry.npmmirror.com/@reactflow/minimap/-/minimap-11.7.14.tgz#298d7a63cb1da06b2518c99744f716560c88ca73" + integrity sha512-mpwLKKrEAofgFJdkhwR5UQ1JYWlcAAL/ZU/bctBkuNTT1yqV+y0buoNVImsRehVYhJwffSWeSHaBR5/GJjlCSQ== + dependencies: + "@reactflow/core" "11.11.4" + "@types/d3-selection" "^3.0.3" + "@types/d3-zoom" "^3.0.1" + classcat "^5.0.3" + d3-selection "^3.0.0" + d3-zoom "^3.0.0" + zustand "^4.4.1" + +"@reactflow/node-resizer@2.2.14": + version "2.2.14" + resolved "https://registry.npmmirror.com/@reactflow/node-resizer/-/node-resizer-2.2.14.tgz#1810c0ce51aeb936f179466a6660d1e02c7a77a8" + integrity sha512-fwqnks83jUlYr6OHcdFEedumWKChTHRGw/kbCxj0oqBd+ekfs+SIp4ddyNU0pdx96JIm5iNFS0oNrmEiJbbSaA== + dependencies: + "@reactflow/core" "11.11.4" + classcat "^5.0.4" + d3-drag "^3.0.0" + d3-selection "^3.0.0" + zustand "^4.4.1" + +"@reactflow/node-toolbar@1.3.14": + version "1.3.14" + resolved "https://registry.npmmirror.com/@reactflow/node-toolbar/-/node-toolbar-1.3.14.tgz#c6ffc76f82acacdce654f2160dc9852162d6e7c9" + integrity sha512-rbynXQnH/xFNu4P9H+hVqlEUafDCkEoCy0Dg9mG22Sg+rY/0ck6KkrAQrYrTgXusd+cEJOMK0uOOFCK2/5rSGQ== + dependencies: + "@reactflow/core" "11.11.4" + classcat "^5.0.3" + zustand "^4.4.1" + "@remirror/core-constants@3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@remirror/core-constants/-/core-constants-3.0.0.tgz#96fdb89d25c62e7b6a5d08caf0ce5114370e3b8f" @@ -2307,38 +2373,155 @@ resolved "https://registry.yarnpkg.com/@types/core-js/-/core-js-2.5.8.tgz#d5c6ec44f2f3328653dce385ae586bd8261f8e85" integrity sha512-VgnAj6tIAhJhZdJ8/IpxdatM8G4OD3VWGlp6xIxUGENZlpbob9Ty4VVdC1FIEp0aK6DBscDDjyzy5FB60TuNqg== -"@types/d3-color@^3.0.0": +"@types/d3-array@*": + version "3.2.2" + resolved "https://registry.npmmirror.com/@types/d3-array/-/d3-array-3.2.2.tgz#e02151464d02d4a1b44646d0fcdb93faf88fde8c" + integrity sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw== + +"@types/d3-axis@*": + version "3.0.6" + resolved "https://registry.npmmirror.com/@types/d3-axis/-/d3-axis-3.0.6.tgz#e760e5765b8188b1defa32bc8bb6062f81e4c795" + integrity sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw== + dependencies: + "@types/d3-selection" "*" + +"@types/d3-brush@*": + version "3.0.6" + resolved "https://registry.npmmirror.com/@types/d3-brush/-/d3-brush-3.0.6.tgz#c2f4362b045d472e1b186cdbec329ba52bdaee6c" + integrity sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A== + dependencies: + "@types/d3-selection" "*" + +"@types/d3-chord@*": + version "3.0.6" + resolved "https://registry.npmmirror.com/@types/d3-chord/-/d3-chord-3.0.6.tgz#1706ca40cf7ea59a0add8f4456efff8f8775793d" + integrity sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg== + +"@types/d3-color@*", "@types/d3-color@^3.0.0": version "3.1.3" resolved "https://registry.yarnpkg.com/@types/d3-color/-/d3-color-3.1.3.tgz#368c961a18de721da8200e80bf3943fb53136af2" integrity sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A== -"@types/d3-delaunay@^6.0.4": +"@types/d3-contour@*": + version "3.0.6" + resolved "https://registry.npmmirror.com/@types/d3-contour/-/d3-contour-3.0.6.tgz#9ada3fa9c4d00e3a5093fed0356c7ab929604231" + integrity sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg== + dependencies: + "@types/d3-array" "*" + "@types/geojson" "*" + +"@types/d3-delaunay@*", "@types/d3-delaunay@^6.0.4": version "6.0.4" resolved "https://registry.yarnpkg.com/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz#185c1a80cc807fdda2a3fe960f7c11c4a27952e1" integrity sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw== +"@types/d3-dispatch@*": + version "3.0.7" + resolved "https://registry.npmmirror.com/@types/d3-dispatch/-/d3-dispatch-3.0.7.tgz#ef004d8a128046cfce434d17182f834e44ef95b2" + integrity sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA== + +"@types/d3-drag@*", "@types/d3-drag@^3.0.1": + version "3.0.7" + resolved "https://registry.npmmirror.com/@types/d3-drag/-/d3-drag-3.0.7.tgz#b13aba8b2442b4068c9a9e6d1d82f8bcea77fc02" + integrity sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ== + dependencies: + "@types/d3-selection" "*" + +"@types/d3-dsv@*": + version "3.0.7" + resolved "https://registry.npmmirror.com/@types/d3-dsv/-/d3-dsv-3.0.7.tgz#0a351f996dc99b37f4fa58b492c2d1c04e3dac17" + integrity sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g== + +"@types/d3-ease@*": + version "3.0.2" + resolved "https://registry.npmmirror.com/@types/d3-ease/-/d3-ease-3.0.2.tgz#e28db1bfbfa617076f7770dd1d9a48eaa3b6c51b" + integrity sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA== + +"@types/d3-fetch@*": + version "3.0.7" + resolved "https://registry.npmmirror.com/@types/d3-fetch/-/d3-fetch-3.0.7.tgz#c04a2b4f23181aa376f30af0283dbc7b3b569980" + integrity sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA== + dependencies: + "@types/d3-dsv" "*" + +"@types/d3-force@*": + version "3.0.10" + resolved "https://registry.npmmirror.com/@types/d3-force/-/d3-force-3.0.10.tgz#6dc8fc6e1f35704f3b057090beeeb7ac674bff1a" + integrity sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw== + +"@types/d3-format@*": + version "3.0.4" + resolved "https://registry.npmmirror.com/@types/d3-format/-/d3-format-3.0.4.tgz#b1e4465644ddb3fdf3a263febb240a6cd616de90" + integrity sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g== + "@types/d3-format@^1.4.1": version "1.4.5" resolved "https://registry.yarnpkg.com/@types/d3-format/-/d3-format-1.4.5.tgz#6392303c2ca3c287c3a1a2046455cd0a0bd50bbe" integrity sha512-mLxrC1MSWupOSncXN/HOlWUAAIffAEBaI4+PKy2uMPsKe4FNZlk7qrbTjmzJXITQQqBHivaks4Td18azgqnotA== +"@types/d3-geo@*": + version "3.1.0" + resolved "https://registry.npmmirror.com/@types/d3-geo/-/d3-geo-3.1.0.tgz#b9e56a079449174f0a2c8684a9a4df3f60522440" + integrity sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ== + dependencies: + "@types/geojson" "*" + +"@types/d3-hierarchy@*": + version "3.1.7" + resolved "https://registry.npmmirror.com/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz#6023fb3b2d463229f2d680f9ac4b47466f71f17b" + integrity sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg== + +"@types/d3-interpolate@*": + version "3.0.4" + resolved "https://registry.npmmirror.com/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz#412b90e84870285f2ff8a846c6eb60344f12a41c" + integrity sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA== + dependencies: + "@types/d3-color" "*" + "@types/d3-path@*": version "3.1.1" resolved "https://registry.yarnpkg.com/@types/d3-path/-/d3-path-3.1.1.tgz#f632b380c3aca1dba8e34aa049bcd6a4af23df8a" integrity sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg== -"@types/d3-scale-chromatic@^3.0.0": +"@types/d3-polygon@*": + version "3.0.2" + resolved "https://registry.npmmirror.com/@types/d3-polygon/-/d3-polygon-3.0.2.tgz#dfae54a6d35d19e76ac9565bcb32a8e54693189c" + integrity sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA== + +"@types/d3-quadtree@*": + version "3.0.6" + resolved "https://registry.npmmirror.com/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz#d4740b0fe35b1c58b66e1488f4e7ed02952f570f" + integrity sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg== + +"@types/d3-random@*": + version "3.0.3" + resolved "https://registry.npmmirror.com/@types/d3-random/-/d3-random-3.0.3.tgz#ed995c71ecb15e0cd31e22d9d5d23942e3300cfb" + integrity sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ== + +"@types/d3-scale-chromatic@*", "@types/d3-scale-chromatic@^3.0.0": version "3.1.0" resolved "https://registry.yarnpkg.com/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz#dc6d4f9a98376f18ea50bad6c39537f1b5463c39" integrity sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ== -"@types/d3-scale@^4.0.8": +"@types/d3-scale@*", "@types/d3-scale@^4.0.8": version "4.0.9" resolved "https://registry.yarnpkg.com/@types/d3-scale/-/d3-scale-4.0.9.tgz#57a2f707242e6fe1de81ad7bfcccaaf606179afb" integrity sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw== dependencies: "@types/d3-time" "*" +"@types/d3-selection@*", "@types/d3-selection@^3.0.3": + version "3.0.11" + resolved "https://registry.npmmirror.com/@types/d3-selection/-/d3-selection-3.0.11.tgz#bd7a45fc0a8c3167a631675e61bc2ca2b058d4a3" + integrity sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w== + +"@types/d3-shape@*": + version "3.1.8" + resolved "https://registry.npmmirror.com/@types/d3-shape/-/d3-shape-3.1.8.tgz#d1516cc508753be06852cd06758e3bb54a22b0e3" + integrity sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w== + dependencies: + "@types/d3-path" "*" + "@types/d3-shape@^3.1.6": version "3.1.7" resolved "https://registry.yarnpkg.com/@types/d3-shape/-/d3-shape-3.1.7.tgz#2b7b423dc2dfe69c8c93596e673e37443348c555" @@ -2346,6 +2529,11 @@ dependencies: "@types/d3-path" "*" +"@types/d3-time-format@*": + version "4.0.3" + resolved "https://registry.npmmirror.com/@types/d3-time-format/-/d3-time-format-4.0.3.tgz#d6bc1e6b6a7db69cccfbbdd4c34b70632d9e9db2" + integrity sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg== + "@types/d3-time-format@^2.3.1": version "2.3.4" resolved "https://registry.yarnpkg.com/@types/d3-time-format/-/d3-time-format-2.3.4.tgz#544af5184df8b3fc4d9b42b14058789acee2905e" @@ -2366,6 +2554,62 @@ resolved "https://registry.yarnpkg.com/@types/d3-time/-/d3-time-1.1.4.tgz#20da4b75c537a940e7319b75717c67a2e499515a" integrity sha512-JIvy2HjRInE+TXOmIGN5LCmeO0hkFZx5f9FZ7kiN+D+YTcc8pptsiLiuHsvwxwC7VVKmJ2ExHUgNlAiV7vQM9g== +"@types/d3-timer@*": + version "3.0.2" + resolved "https://registry.npmmirror.com/@types/d3-timer/-/d3-timer-3.0.2.tgz#70bbda77dc23aa727413e22e214afa3f0e852f70" + integrity sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw== + +"@types/d3-transition@*": + version "3.0.9" + resolved "https://registry.npmmirror.com/@types/d3-transition/-/d3-transition-3.0.9.tgz#1136bc57e9ddb3c390dccc9b5ff3b7d2b8d94706" + integrity sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg== + dependencies: + "@types/d3-selection" "*" + +"@types/d3-zoom@*", "@types/d3-zoom@^3.0.1": + version "3.0.8" + resolved "https://registry.npmmirror.com/@types/d3-zoom/-/d3-zoom-3.0.8.tgz#dccb32d1c56b1e1c6e0f1180d994896f038bc40b" + integrity sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw== + dependencies: + "@types/d3-interpolate" "*" + "@types/d3-selection" "*" + +"@types/d3@^7.4.0": + version "7.4.3" + resolved "https://registry.npmmirror.com/@types/d3/-/d3-7.4.3.tgz#d4550a85d08f4978faf0a4c36b848c61eaac07e2" + integrity sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww== + dependencies: + "@types/d3-array" "*" + "@types/d3-axis" "*" + "@types/d3-brush" "*" + "@types/d3-chord" "*" + "@types/d3-color" "*" + "@types/d3-contour" "*" + "@types/d3-delaunay" "*" + "@types/d3-dispatch" "*" + "@types/d3-drag" "*" + "@types/d3-dsv" "*" + "@types/d3-ease" "*" + "@types/d3-fetch" "*" + "@types/d3-force" "*" + "@types/d3-format" "*" + "@types/d3-geo" "*" + "@types/d3-hierarchy" "*" + "@types/d3-interpolate" "*" + "@types/d3-path" "*" + "@types/d3-polygon" "*" + "@types/d3-quadtree" "*" + "@types/d3-random" "*" + "@types/d3-scale" "*" + "@types/d3-scale-chromatic" "*" + "@types/d3-selection" "*" + "@types/d3-shape" "*" + "@types/d3-time" "*" + "@types/d3-time-format" "*" + "@types/d3-timer" "*" + "@types/d3-transition" "*" + "@types/d3-zoom" "*" + "@types/eslint@*": version "9.6.1" resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-9.6.1.tgz#d5795ad732ce81715f27f75da913004a56751584" @@ -2391,6 +2635,11 @@ resolved "https://registry.yarnpkg.com/@types/format-util/-/format-util-1.0.4.tgz#c4e3b556735149fdf047898a5b9c04650491509b" integrity sha512-xrCYOdHh5zA3LUrn6CvspYwlzSWxPso11Lx32WnAG6KvLCRecKZ/Rh21PLXUkzUFsQmrGcx/traJAFjR6dVS5Q== +"@types/geojson@*": + version "7946.0.16" + resolved "https://registry.npmmirror.com/@types/geojson/-/geojson-7946.0.16.tgz#8ebe53d69efada7044454e3305c19017d97ced2a" + integrity sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg== + "@types/json-schema@*", "@types/json-schema@^7.0.15": version "7.0.15" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" @@ -3120,6 +3369,11 @@ chance@^1.1.12: resolved "https://registry.yarnpkg.com/chance/-/chance-1.1.13.tgz#d4ecfd20c5e6799aaf5c2270d7653b32385cd6e3" integrity sha512-V6lQCljcLznE7tUYUM9EOAnnKXbctE6j/rdQkYOHIWbfGQbrzTsAXNW9CdU5XCo4ArXQCj/rb6HgxPlmGJcaUg== +classcat@^5.0.3, classcat@^5.0.4: + version "5.0.5" + resolved "https://registry.npmmirror.com/classcat/-/classcat-5.0.5.tgz#8c209f359a93ac302404a10161b501eba9c09c77" + integrity sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w== + classnames@^2.5.1: version "2.5.1" resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.5.1.tgz#ba774c614be0f016da105c858e7159eae8e7687b" @@ -3279,6 +3533,24 @@ d3-delaunay@^6.0.4: dependencies: delaunator "5" +"d3-dispatch@1 - 3": + version "3.0.1" + resolved "https://registry.npmmirror.com/d3-dispatch/-/d3-dispatch-3.0.1.tgz#5fc75284e9c2375c36c839411a0cf550cbfc4d5e" + integrity sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg== + +"d3-drag@2 - 3", d3-drag@^3.0.0: + version "3.0.0" + resolved "https://registry.npmmirror.com/d3-drag/-/d3-drag-3.0.0.tgz#994aae9cd23c719f53b5e10e3a0a6108c69607ba" + integrity sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg== + dependencies: + d3-dispatch "1 - 3" + d3-selection "3" + +"d3-ease@1 - 3": + version "3.0.1" + resolved "https://registry.npmmirror.com/d3-ease/-/d3-ease-3.0.1.tgz#9658ac38a2140d59d346160f1f6c30fda0bd12f4" + integrity sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w== + "d3-format@1 - 3": version "3.1.0" resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-3.1.0.tgz#9260e23a28ea5cb109e93b21a06e24e2ebd55641" @@ -3320,6 +3592,11 @@ d3-scale@^4.0.2: d3-time "2.1.1 - 3" d3-time-format "2 - 4" +"d3-selection@2 - 3", d3-selection@3, d3-selection@^3.0.0: + version "3.0.0" + resolved "https://registry.npmmirror.com/d3-selection/-/d3-selection-3.0.0.tgz#c25338207efa72cc5b9bd1458a1a41901f1e1b31" + integrity sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ== + d3-shape@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-3.2.0.tgz#a1a839cbd9ba45f28674c69d7f855bcf91dfc6a5" @@ -3360,6 +3637,33 @@ d3-time@^1.0.11: resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-1.1.0.tgz#b1e19d307dae9c900b7e5b25ffc5dcc249a8a0f1" integrity sha512-Xh0isrZ5rPYYdqhAVk8VLnMEidhz5aP7htAADH6MfzgmmicPkTo8LhkLxci61/lCB7n7UmE3bN0leRt+qvkLxA== +"d3-timer@1 - 3": + version "3.0.1" + resolved "https://registry.npmmirror.com/d3-timer/-/d3-timer-3.0.1.tgz#6284d2a2708285b1abb7e201eda4380af35e63b0" + integrity sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA== + +"d3-transition@2 - 3": + version "3.0.1" + resolved "https://registry.npmmirror.com/d3-transition/-/d3-transition-3.0.1.tgz#6869fdde1448868077fdd5989200cb61b2a1645f" + integrity sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w== + dependencies: + d3-color "1 - 3" + d3-dispatch "1 - 3" + d3-ease "1 - 3" + d3-interpolate "1 - 3" + d3-timer "1 - 3" + +d3-zoom@^3.0.0: + version "3.0.0" + resolved "https://registry.npmmirror.com/d3-zoom/-/d3-zoom-3.0.0.tgz#d13f4165c73217ffeaa54295cd6969b3e7aee8f3" + integrity sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw== + dependencies: + d3-dispatch "1 - 3" + d3-drag "2 - 3" + d3-interpolate "1 - 3" + d3-selection "2 - 3" + d3-transition "2 - 3" + data-view-buffer@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/data-view-buffer/-/data-view-buffer-1.0.2.tgz#211a03ba95ecaf7798a8c7198d79536211f88570" @@ -5649,6 +5953,18 @@ react@^18.3.1: dependencies: loose-envify "^1.1.0" +reactflow@^11.11.4: + version "11.11.4" + resolved "https://registry.npmmirror.com/reactflow/-/reactflow-11.11.4.tgz#e3593e313420542caed81aecbd73fb9bc6576653" + integrity sha512-70FOtJkUWH3BAOsN+LU9lCrKoKbtOPnz2uq0CV2PLdNSwxTXOhCbsZr50GmZ+Rtw3jx8Uv7/vBFtCGixLfd4Og== + dependencies: + "@reactflow/background" "11.3.14" + "@reactflow/controls" "11.2.14" + "@reactflow/core" "11.11.4" + "@reactflow/minimap" "11.7.14" + "@reactflow/node-resizer" "2.2.14" + "@reactflow/node-toolbar" "1.3.14" + read-pkg@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" @@ -6516,6 +6832,11 @@ use-sync-external-store@^1, use-sync-external-store@^1.0.0, use-sync-external-st resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz#55122e2a3edd2a6c106174c27485e0fd59bcfca0" integrity sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A== +use-sync-external-store@^1.2.2: + version "1.6.0" + resolved "https://registry.npmmirror.com/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz#b174bfa65cb2b526732d9f2ac0a408027876f32d" + integrity sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w== + util-deprecate@^1.0.1, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" @@ -6688,3 +7009,10 @@ zod@^3.14.4: version "3.25.76" resolved "https://registry.yarnpkg.com/zod/-/zod-3.25.76.tgz#26841c3f6fd22a6a2760e7ccb719179768471e34" integrity sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ== + +zustand@^4.4.1: + version "4.5.7" + resolved "https://registry.npmmirror.com/zustand/-/zustand-4.5.7.tgz#7d6bb2026a142415dd8be8891d7870e6dbe65f55" + integrity sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw== + dependencies: + use-sync-external-store "^1.2.2" From b9e9a3086e19ec0a5548252ebd579e62b943862f Mon Sep 17 00:00:00 2001 From: everythingfades Date: Fri, 16 Jan 2026 01:08:45 +0000 Subject: [PATCH 02/36] fix: dev notice in FSA.md --- FSA.md | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/FSA.md b/FSA.md index 5a76502..929f1eb 100644 --- a/FSA.md +++ b/FSA.md @@ -94,4 +94,20 @@ Since the frontend and backend see the data differently, the `FSAConverter` util * **Accept States**: Nodes in `accept_states` are rendered with a double-border (4px double). -* **Alphabet Consistency**: The `alphabet` array is automatically derived from the unique labels present in the transitions during the sync process. \ No newline at end of file +* **Alphabet Consistency**: The `alphabet` array is automatically derived from the unique labels present in the transitions during the sync process. + +## 6. Dev Notice: + +There is a temporary folder `/dev`, all the development stuff should be tested there + +run `yarn vite` or `yarn dev:fsa` to run + +also take notice in order for yarn to be configured correctly, there is a extra config + +```json +root: 'dev', // for dev only +``` + +in the vite.config.ts + +remember to remove it when we get to production \ No newline at end of file From 675d61227ba17a94428e44da62ad1ab0fd3f88fe Mon Sep 17 00:00:00 2001 From: everythingfades Date: Fri, 16 Jan 2026 23:49:06 +0000 Subject: [PATCH 03/36] fix: updated UI display, removed prompting, add information panel, need to add more info for teacher mode and find ways to test backend connect, schema may need fix as JSON.stringfy can walk pass the zod schema check --- dev/main.tsx | 20 +- src/sandbox-component.tsx | 5 +- src/types/FSA/FSA.component.tsx | 394 +++++++++++++++++++++++++------- src/types/FSA/index.tsx | 42 +++- vite.config.ts | 2 +- 5 files changed, 356 insertions(+), 107 deletions(-) diff --git a/dev/main.tsx b/dev/main.tsx index 0e76b9b..eec2322 100644 --- a/dev/main.tsx +++ b/dev/main.tsx @@ -3,35 +3,29 @@ import React, { useState } from 'react' import ReactDOM from 'react-dom/client' import { FSAResponseAreaTub } from '../src/types/FSA' +import { FSAInput } from '../src/types/FSA/FSA.component' +import { defaultFSA, FSA } from '../src/types/FSA/type' + const tub = new FSAResponseAreaTub() function Sandbox() { const [answer, setAnswer] = - useState(null) + useState(defaultFSA) - const [allowSave, setAllowSave] = useState(true) + const [, setAllowSave] = useState(true) return ( <>

Input

- console.log('submit')} - handleDraftSave={() => console.log('draft save')} - displayMode="normal" - hasPreview + onChange={(val) => console.log("wizard change", val)} />

Wizard

- console.log('wizard change', val)} - setAllowSave={setAllowSave} - /> ) } diff --git a/src/sandbox-component.tsx b/src/sandbox-component.tsx index ec553ef..098da57 100644 --- a/src/sandbox-component.tsx +++ b/src/sandbox-component.tsx @@ -1,13 +1,14 @@ import { ThemeProvider } from '@styles/minimal/theme-provider' -import { SandboxResponseAreaTub } from './types/Sandbox' +import { FSAResponseAreaTub } from './types/FSA' +// import { SandboxResponseAreaTub } from './types/Sandbox' function ResponseAreaInputWrapper({ children }: { children: React.ReactNode }) { return {children} } // wrap the components with the necessary providers; only in the sandbox -class WrappedSandboxResponseAreaTub extends SandboxResponseAreaTub { +class WrappedSandboxResponseAreaTub extends FSAResponseAreaTub { constructor() { super() diff --git a/src/types/FSA/FSA.component.tsx b/src/types/FSA/FSA.component.tsx index fc03e35..284df6c 100644 --- a/src/types/FSA/FSA.component.tsx +++ b/src/types/FSA/FSA.component.tsx @@ -1,34 +1,154 @@ -import React, { useCallback, useMemo } from 'react'; -import ReactFlow, { - addEdge, - Background, - Controls, - Connection, - useNodesState, +import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import ReactFlow, { + addEdge, + Background, + Controls, + Connection, + useNodesState, useEdgesState, MarkerType, Edge, Node, - OnNodesDelete } from 'reactflow'; import 'reactflow/dist/style.css'; import { FSA } from './type'; +import { makeStyles } from '@styles'; + +/* -------------------- styles -------------------- */ + +const useLocalStyles = makeStyles()((theme) => ({ + container: { + width: '100%', + height: 600, + display: 'flex', + border: '1px solid #ddd', + fontFamily: 'sans-serif', + }, + panel: { + width: 280, + borderRight: '1px solid #ddd', + padding: theme.spacing(2), + backgroundColor: '#fafafa', + display: 'flex', + flexDirection: 'column', + gap: theme.spacing(2), + }, + panelTitle: { + fontWeight: 600, + fontSize: 16, + borderBottom: '1px solid #eee', + paddingBottom: theme.spacing(1), + }, + field: { + display: 'flex', + flexDirection: 'column', + gap: theme.spacing(0.5), + }, + inputField: { + padding: '6px 8px', + border: '1px solid #ccc', + borderRadius: 4, + }, + checkboxRow: { + display: 'flex', + alignItems: 'center', + gap: theme.spacing(1), + cursor: 'pointer', + padding: '4px 0', + }, + deleteButton: { + marginTop: theme.spacing(2), + padding: '8px', + backgroundColor: '#fff1f0', + color: '#cf1322', + border: '1px solid #ffa39e', + borderRadius: 4, + cursor: 'pointer', + fontWeight: 600, + '&:hover': { + backgroundColor: '#ffa39e', + color: '#fff', + }, + }, + flowWrapper: { + flexGrow: 1, + display: 'flex', + flexDirection: 'column', + }, + toolbar: { + padding: theme.spacing(1), + borderBottom: '1px solid #eee', + backgroundColor: '#f9f9f9', + display: 'flex', + alignItems: 'center', + gap: theme.spacing(1.5), + }, + addButton: { + padding: '4px 12px', + cursor: 'pointer', + backgroundColor: '#fff', + border: '1px solid #ccc', + borderRadius: 4, + }, + node: { + border: '1px solid #777', + borderRadius: '50%', + width: 50, + height: 50, + backgroundColor: '#fff', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + fontWeight: 'bold', + }, + initialNode: { + backgroundColor: '#e6fffa', + borderWidth: 2, + borderColor: '#38b2ac', + }, + acceptNode: { + boxShadow: '0 0 0 4px #fff, 0 0 0 6px #333', + }, +})); + +/* -------------------- component -------------------- */ + interface FSAInputProps { answer: FSA; onChange: (val: FSA) => void; + isTeacherMode?: boolean; } -export const FSAInput: React.FC = ({ answer, onChange }) => { - // 1. Unpack flattened strings into React Flow Edges - // we could use the convertor, but lets just keep this here +export const FSAInput: React.FC = ({ + answer, + onChange, + isTeacherMode, +}) => { + const { classes, cx } = useLocalStyles(); + const [selectedNodeId, setSelectedNodeId] = useState(null); + const [selectedEdgeId, setSelectedEdgeId] = useState(null); + + useEffect(() => { + // import the css, since the import 'reactflow/dist/style.css'; dont seems to work + // the css is on the cdn anyway + const linkId = 'react-flow-css'; + if (!document.getElementById(linkId)) { + const link = document.createElement('link'); + link.id = linkId; link.rel = 'stylesheet'; + link.href = 'https://cdn.jsdelivr.net/npm/reactflow@11.10.4/dist/style.css'; + // Use a CDN fallback + document.head.appendChild(link); + } + }, []); + const initialEdges: Edge[] = useMemo(() => { return (answer.transitions || []).reduce((acc: Edge[], tStr: string) => { const [from, symbol, to] = tStr.split('|'); if (from && symbol && to) { acc.push({ - id: `e-${from}-${to}-${symbol}`, + id: `e-${from}-${to}-${symbol}-${Date.now()}`, source: from, target: to, label: symbol, @@ -39,92 +159,198 @@ export const FSAInput: React.FC = ({ answer, onChange }) => { }, []); }, [answer.transitions]); - const initialNodes: Node[] = useMemo(() => - answer.states.map((s, i) => ({ - id: s, - data: { label: s }, - position: { x: i * 150, y: 100 }, - style: { - border: answer.accept_states.includes(s) ? '4px double #333' : '1px solid #777', - background: s === answer.initial_state ? '#e6fffa' : '#fff', - borderRadius: '50%', width: 50, height: 50, - display: 'flex', alignItems: 'center', justifyContent: 'center' - } - })), [answer]); + const initialNodes: Node[] = useMemo( + () => + answer.states.map((s, i) => ({ + id: s, + data: { label: s }, + position: { x: i * 120 + 50, y: 150 }, + className: cx( + classes.node, + s === answer.initial_state && classes.initialNode, + answer.accept_states.includes(s) && classes.acceptNode, + ), + })), + [answer.states, answer.initial_state, answer.accept_states, classes, cx], + ); const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes); const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges); - // Sync helper to convert current Flow state back to FSA format - const syncChanges = useCallback((currentNodes: Node[], currentEdges: Edge[]) => { - const updatedFSA: FSA = { - ...answer, - states: currentNodes.map(n => n.id), - transitions: currentEdges.map(e => `${e.source}|${e.label}|${e.target}`), - alphabet: Array.from(new Set(currentEdges.map(e => String(e.label)))) - }; - onChange(updatedFSA); - }, [answer, onChange]); - - // Handle Adding Nodes - const addState = useCallback(() => { - const id = prompt("Enter state name (e.g. q2):"); - if (!id || nodes.find(n => n.id === id)) return; - - const newNode: Node = { - id, - data: { label: id }, - position: { x: Math.random() * 400, y: Math.random() * 400 }, - style: { border: '1px solid #777', borderRadius: '50%', width: 50, height: 50 } - }; - - const updatedNodes = [...nodes, newNode]; - setNodes(updatedNodes); - syncChanges(updatedNodes, edges); - }, [nodes, edges, setNodes, syncChanges]); - - // Handle Deleting Nodes (triggered by Backspace/Delete key by default in React Flow) - const onNodesDelete: OnNodesDelete = useCallback((deletedNodes) => { - const deletedIds = new Set(deletedNodes.map(n => n.id)); - const remainingNodes = nodes.filter(n => !deletedIds.has(n.id)); - // React Flow handles edge cleanup internally in the 'edges' state, - // but we need to ensure our sync uses the filtered edges. - const remainingEdges = edges.filter(e => !deletedIds.has(e.source) && !deletedIds.has(e.target)); - + const selectedNode = useMemo(() => nodes.find((n) => n.id === selectedNodeId), [nodes, selectedNodeId]); + const selectedEdge = useMemo(() => edges.find((e) => e.id === selectedEdgeId), [edges, selectedEdgeId]); + + const syncChanges = useCallback( + (currentNodes: Node[], currentEdges: Edge[]) => { + onChange({ + ...answer, + states: currentNodes.map((n) => n.id), + transitions: currentEdges.map((e) => `${e.source}|${e.label || 'ε'}|${e.target}`), + alphabet: Array.from(new Set(currentEdges.map((e) => String(e.label || 'ε')))).filter(s => s !== 'ε'), + }); + }, + [answer, onChange], + ); + + const deleteSelectedNode = () => { + if (!selectedNodeId) return; + const remainingNodes = nodes.filter((n) => n.id !== selectedNodeId); + const remainingEdges = edges.filter((e) => e.source !== selectedNodeId && e.target !== selectedNodeId); + setNodes(remainingNodes); + setEdges(remainingEdges); syncChanges(remainingNodes, remainingEdges); - }, [nodes, edges, syncChanges]); - - const onConnect = useCallback((params: Connection) => { - const symbol = prompt("Transition symbol:") || 'ε'; - const newEdge = { ...params, label: symbol, markerEnd: { type: MarkerType.ArrowClosed } }; - - setEdges((eds) => { - const updatedEdges = addEdge(newEdge, eds); - syncChanges(nodes, updatedEdges); - return updatedEdges; - }); - }, [nodes, syncChanges, setEdges]); + setSelectedNodeId(null); + }; + + const deleteSelectedEdge = () => { + if (!selectedEdgeId) return; + const remainingEdges = edges.filter((e) => e.id !== selectedEdgeId); + setEdges(remainingEdges); + syncChanges(nodes, remainingEdges); + setSelectedEdgeId(null); + }; + + const onConnect = useCallback( + (params: Connection) => { + if (!params.source || !params.target) return; + const symbol = `tran-${Date.now()}` + const newEdge: Edge = { + ...params, + id: `edge-${Date.now()}`, + source: params.source, + target: params.target, + label: symbol, + markerEnd: { type: MarkerType.ArrowClosed }, + }; + setEdges((eds) => { + const updated = addEdge(newEdge, eds); + syncChanges(nodes, updated); + return updated; + }); + }, + [nodes, syncChanges, setEdges], + ); + + // Prevent backspace in input from deleting node/edge in React Flow + const stopPropagation = (e: React.KeyboardEvent) => e.stopPropagation(); return ( -
-
- - - Select a node/edge and press Backspace to delete. - +
+
+
Item Properties
+ + {selectedNode && ( + <> +
+ + { + const newId = e.target.value.trim(); + if (!newId || nodes.some(n => n.id === newId)) return; + const oldId = selectedNode.id; + const updatedNodes = nodes.map(n => n.id === oldId ? { ...n, id: newId, data: { label: newId } } : n); + const updatedEdges = edges.map(e => ({ + ...e, + source: e.source === oldId ? newId : e.source, + target: e.target === oldId ? newId : e.target + })); + setNodes(updatedNodes); + setEdges(updatedEdges); + setSelectedNodeId(newId); + syncChanges(updatedNodes, updatedEdges); + }} + /> +
+
+ { + if (e.target.checked) { + // Only one initial state allowed + onChange({ + ...answer, + initial_state: selectedNode.id, + }); + } else { + if (answer.initial_state === selectedNode.id){ + onChange({ + ...answer, + initial_state: '', + }); + } + } + }} + /> + +
+ +
+ { + const isChecked = e.target.checked; + + onChange({ + ...answer, + accept_states: isChecked + ? [...answer.accept_states, selectedNode.id] + : answer.accept_states.filter( + (s) => s !== selectedNode.id, + ), + }); + }} + /> + +
+ + + )} + + {selectedEdge && ( + <> +
+ + { + const updatedEdges = edges.map(ed => ed.id === selectedEdgeId ? { ...ed, label: e.target.value } : ed); + setEdges(updatedEdges); + syncChanges(nodes, updatedEdges); + }} + /> +
+ + + )} + + {!selectedNode && !selectedEdge &&
Select an element to edit
}
- -
+ +
+
+ +
{ setSelectedNodeId(n.id); setSelectedEdgeId(null); }} + onEdgeClick={(_, e) => { setSelectedEdgeId(e.id); setSelectedNodeId(null); }} + deleteKeyCode={null} // Disables keyboard delete to prevent conflicts > diff --git a/src/types/FSA/index.tsx b/src/types/FSA/index.tsx index d7c4172..cac9160 100644 --- a/src/types/FSA/index.tsx +++ b/src/types/FSA/index.tsx @@ -11,22 +11,50 @@ export class FSAResponseAreaTub extends ResponseAreaTub { protected answerSchema = fsaAnswerSchema; protected answer: FSA = defaultFSA; + initWithConfig = (config: any) => { + this.config = config // config is not used for now + this.answer = defaultFSA + } + + customCheck = () => {} // will set this up later + InputComponent = (props: BaseResponseAreaProps) => { - const parsedAnswer = this.answerSchema.safeParse(props.answer); + // Always derive a local FSA value + console.log('FSA InputComponent props.answer:', props.answer, typeof props.answer); + const fsaAnswer: FSA = (() => { + if (!props.answer) return defaultFSA; + if (typeof props.answer === 'string') { + try { + return JSON.parse(props.answer); + } catch { + return defaultFSA; + } + } + + return props.answer as FSA; + })(); + return ( - props.handleChange(val)} - /> + <> +

Input Component

+ { + props.handleChange(JSON.stringify(answer)); + }} + /> + ); - } + }; WizardComponent = (props: BaseResponseAreaWizardProps) => { return ( { + this.answer = answer props.handleChange({ responseType: this.responseType, answer, diff --git a/vite.config.ts b/vite.config.ts index 9b983a3..b385a8c 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -4,7 +4,7 @@ import { defineConfig } from 'vite' export default defineConfig({ plugins: [react()], - root: 'dev', // for dev only + // root: 'dev', // for dev only define: { 'process.env': JSON.stringify({ NODE_ENV: 'production' }), }, From da333ba284255bc4b00b2f740b738503337b4d0d Mon Sep 17 00:00:00 2001 From: everythingfades Date: Sun, 25 Jan 2026 18:34:56 +0000 Subject: [PATCH 04/36] refactor: Reactflow is not so suitable for maths, changing to cytoscape instead --- package.json | 1 + src/types/FSA/FSA.component.tsx | 476 ++++++++++++++++---------------- src/types/FSA/index.tsx | 2 + src/types/FSA/type.ts | 4 +- yarn.lock | 5 + 5 files changed, 244 insertions(+), 244 deletions(-) diff --git a/package.json b/package.json index 1698e24..c64c1c2 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "@nivo/core": "^0.88.0", "@nivo/line": "^0.88.0", "@nivo/pie": "^0.88.0", + "cytoscape": "^3.33.1", "date-fns": "^2.28.0", "framer-motion": "^11.2.10", "katex": "^0.16.2", diff --git a/src/types/FSA/FSA.component.tsx b/src/types/FSA/FSA.component.tsx index 284df6c..0ec2d10 100644 --- a/src/types/FSA/FSA.component.tsx +++ b/src/types/FSA/FSA.component.tsx @@ -1,23 +1,10 @@ -import React, { useCallback, useEffect, useMemo, useState } from 'react'; -import ReactFlow, { - addEdge, - Background, - Controls, - Connection, - useNodesState, - useEdgesState, - MarkerType, - Edge, - Node, -} from 'reactflow'; - -import 'reactflow/dist/style.css'; -import { FSA } from './type'; - import { makeStyles } from '@styles'; +import cytoscape, { Core, NodeSingular, EdgeSingular } from 'cytoscape'; +import React, { useEffect, useRef, useState } from 'react'; -/* -------------------- styles -------------------- */ +import { FSA } from './type'; +/* -------------------- styles -------------------- */ const useLocalStyles = makeStyles()((theme) => ({ container: { width: '100%', @@ -55,235 +42,259 @@ const useLocalStyles = makeStyles()((theme) => ({ display: 'flex', alignItems: 'center', gap: theme.spacing(1), + }, + addButton: { + padding: '6px 10px', cursor: 'pointer', - padding: '4px 0', + backgroundColor: '#fff', + border: '1px solid #ccc', + borderRadius: 4, }, deleteButton: { - marginTop: theme.spacing(2), - padding: '8px', + padding: '6px', backgroundColor: '#fff1f0', color: '#cf1322', border: '1px solid #ffa39e', borderRadius: 4, cursor: 'pointer', fontWeight: 600, - '&:hover': { - backgroundColor: '#ffa39e', - color: '#fff', - }, }, - flowWrapper: { + cyWrapper: { flexGrow: 1, - display: 'flex', - flexDirection: 'column', - }, - toolbar: { - padding: theme.spacing(1), - borderBottom: '1px solid #eee', - backgroundColor: '#f9f9f9', - display: 'flex', - alignItems: 'center', - gap: theme.spacing(1.5), - }, - addButton: { - padding: '4px 12px', - cursor: 'pointer', - backgroundColor: '#fff', - border: '1px solid #ccc', - borderRadius: 4, - }, - node: { - border: '1px solid #777', - borderRadius: '50%', - width: 50, - height: 50, - backgroundColor: '#fff', - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - fontWeight: 'bold', - }, - initialNode: { - backgroundColor: '#e6fffa', - borderWidth: 2, - borderColor: '#38b2ac', - }, - acceptNode: { - boxShadow: '0 0 0 4px #fff, 0 0 0 6px #333', }, })); /* -------------------- component -------------------- */ - interface FSAInputProps { answer: FSA; onChange: (val: FSA) => void; - isTeacherMode?: boolean; } -export const FSAInput: React.FC = ({ - answer, - onChange, - isTeacherMode, -}) => { - const { classes, cx } = useLocalStyles(); - const [selectedNodeId, setSelectedNodeId] = useState(null); - const [selectedEdgeId, setSelectedEdgeId] = useState(null); - - useEffect(() => { - // import the css, since the import 'reactflow/dist/style.css'; dont seems to work - // the css is on the cdn anyway - const linkId = 'react-flow-css'; - if (!document.getElementById(linkId)) { - const link = document.createElement('link'); - link.id = linkId; link.rel = 'stylesheet'; - link.href = 'https://cdn.jsdelivr.net/npm/reactflow@11.10.4/dist/style.css'; - // Use a CDN fallback - document.head.appendChild(link); - } +export const FSAInput: React.FC = ({ answer, onChange }) => { + const { classes } = useLocalStyles(); + const containerRef = useRef(null); + const cyRef = useRef(null); + + // State for UI and logic + const [drawMode, setDrawMode] = useState(false); + const [fromNodeId, setFromNodeId] = useState(null); + const [toNodeId, setToNodeId] = useState(null); + const [selectedNode, setSelectedNode] = useState(null); + const [selectedEdge, setSelectedEdge] = useState(null); + + /* -------------------- initialize cytoscape -------------------- */ + useEffect(() => { + if (!containerRef.current) return; + + const cy = cytoscape({ + container: containerRef.current, + layout: { name: 'preset' }, + style: [ + { + selector: 'node', + style: { + label: 'data(displayLabel)', + 'text-valign': 'center', + 'text-halign': 'center', + width: 50, + height: 50, + 'background-color': '#fff', + 'border-width': 1, + 'border-color': '#555', + }, + }, + { + selector: 'edge', + style: { + label: 'data(label)', + 'curve-style': 'bezier', + 'target-arrow-shape': 'triangle', + 'line-color': '#555', + 'target-arrow-color': '#555', + }, + }, + { + selector: '.edge-source', + style: { + 'border-color': '#1890ff', + 'border-width': 3, + }, + }, + { + selector: '.edge-target', + style: { + 'border-color': '#52c41a', + 'border-width': 3, + }, + }, + ], + }); + + cyRef.current = cy; + + return () => cy.destroy(); }, []); - const initialEdges: Edge[] = useMemo(() => { - return (answer.transitions || []).reduce((acc: Edge[], tStr: string) => { - const [from, symbol, to] = tStr.split('|'); - if (from && symbol && to) { - acc.push({ - id: `e-${from}-${to}-${symbol}-${Date.now()}`, - source: from, - target: to, - label: symbol, - markerEnd: { type: MarkerType.ArrowClosed }, - }); + /* -------------------- attach handlers -------------------- */ + useEffect(() => { + const cy = cyRef.current; + if (!cy) return; + + const handleNodeTap = (e: cytoscape.EventObject) => { + const node = e.target as NodeSingular; + + if (drawMode) { + if (!fromNodeId) { + setFromNodeId(node.id()); + node.addClass('edge-source'); + } else if (!toNodeId) { + setToNodeId(node.id()); + node.addClass('edge-target'); + } + return; } - return acc; - }, []); - }, [answer.transitions]); - - const initialNodes: Node[] = useMemo( - () => - answer.states.map((s, i) => ({ - id: s, - data: { label: s }, - position: { x: i * 120 + 50, y: 150 }, - className: cx( - classes.node, - s === answer.initial_state && classes.initialNode, - answer.accept_states.includes(s) && classes.acceptNode, - ), - })), - [answer.states, answer.initial_state, answer.accept_states, classes, cx], - ); - const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes); - const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges); - - const selectedNode = useMemo(() => nodes.find((n) => n.id === selectedNodeId), [nodes, selectedNodeId]); - const selectedEdge = useMemo(() => edges.find((e) => e.id === selectedEdgeId), [edges, selectedEdgeId]); - - const syncChanges = useCallback( - (currentNodes: Node[], currentEdges: Edge[]) => { - onChange({ - ...answer, - states: currentNodes.map((n) => n.id), - transitions: currentEdges.map((e) => `${e.source}|${e.label || 'ε'}|${e.target}`), - alphabet: Array.from(new Set(currentEdges.map((e) => String(e.label || 'ε')))).filter(s => s !== 'ε'), - }); - }, - [answer, onChange], - ); + setSelectedNode(node); + setSelectedEdge(null); + }; - const deleteSelectedNode = () => { - if (!selectedNodeId) return; - const remainingNodes = nodes.filter((n) => n.id !== selectedNodeId); - const remainingEdges = edges.filter((e) => e.source !== selectedNodeId && e.target !== selectedNodeId); - setNodes(remainingNodes); - setEdges(remainingEdges); - syncChanges(remainingNodes, remainingEdges); - setSelectedNodeId(null); - }; + const handleEdgeTap = (e: cytoscape.EventObject) => { + if (drawMode) return; + setSelectedEdge(e.target as EdgeSingular); + setSelectedNode(null); + }; + + cy.on('tap', 'node', handleNodeTap); + cy.on('tap', 'edge', handleEdgeTap); + + return () => { + cy.off('tap', 'node', handleNodeTap); + cy.off('tap', 'edge', handleEdgeTap); + }; + }, [drawMode, fromNodeId, toNodeId]); + + /* -------------------- draw transition effect -------------------- */ + useEffect(() => { + const cy = cyRef.current; + if (!drawMode || !fromNodeId || !toNodeId || !cy) return; + + cy.add({ + group: 'edges', + data: { + id: `e-${fromNodeId}-${toNodeId}-${Date.now()}`, + source: fromNodeId, + target: toNodeId, + label: 'a', + }, + }); + + cy.nodes().removeClass('edge-source edge-target'); + + setDrawMode(false); + setFromNodeId(null); + setToNodeId(null); + + syncToAnswer(); + }, [drawMode, fromNodeId, toNodeId]); + + /* -------------------- helpers -------------------- */ + const syncToAnswer = () => { + const cy = cyRef.current; + if (!cy) return; - const deleteSelectedEdge = () => { - if (!selectedEdgeId) return; - const remainingEdges = edges.filter((e) => e.id !== selectedEdgeId); - setEdges(remainingEdges); - syncChanges(nodes, remainingEdges); - setSelectedEdgeId(null); + const states = cy.nodes().map((n) => n.id()); + const transitions = cy + .edges() + .map((e) => `${e.source().id()}|${e.data('label') || 'ε'}|${e.target().id()}`); + + onChange({ + ...answer, + states, + transitions, + alphabet: Array.from( + new Set(transitions.map((t) => t.split('|')[1]).filter(s => s !== undefined)), + ), + }); }; - const onConnect = useCallback( - (params: Connection) => { - if (!params.source || !params.target) return; - const symbol = `tran-${Date.now()}` - const newEdge: Edge = { - ...params, - id: `edge-${Date.now()}`, - source: params.source, - target: params.target, - label: symbol, - markerEnd: { type: MarkerType.ArrowClosed }, - }; - setEdges((eds) => { - const updated = addEdge(newEdge, eds); - syncChanges(nodes, updated); - return updated; - }); - }, - [nodes, syncChanges, setEdges], - ); + const addState = () => { + const cy = cyRef.current; + if (!cy) return; - // Prevent backspace in input from deleting node/edge in React Flow - const stopPropagation = (e: React.KeyboardEvent) => e.stopPropagation(); + const id = `q${cy.nodes().length}`; + cy.add({ + group: 'nodes', + data: { id, label: id, displayLabel: id }, + position: { x: 100 + Math.random() * 300, y: 100 + Math.random() * 300 }, + }); + syncToAnswer(); + }; + + const deleteSelected = () => { + selectedNode?.remove(); + selectedEdge?.remove(); + setSelectedNode(null); + setSelectedEdge(null); + syncToAnswer(); + }; + + /* -------------------- render -------------------- */ return (
+
Controls
+ + + + + + {drawMode && ( + <> +
From Node: {fromNodeId}
+
To Node: {toNodeId}
+ + )} +
Item Properties
+ {/* Node Properties */} {selectedNode && ( <>
- + { - const newId = e.target.value.trim(); - if (!newId || nodes.some(n => n.id === newId)) return; - const oldId = selectedNode.id; - const updatedNodes = nodes.map(n => n.id === oldId ? { ...n, id: newId, data: { label: newId } } : n); - const updatedEdges = edges.map(e => ({ - ...e, - source: e.source === oldId ? newId : e.source, - target: e.target === oldId ? newId : e.target - })); - setNodes(updatedNodes); - setEdges(updatedEdges); - setSelectedNodeId(newId); - syncChanges(updatedNodes, updatedEdges); + selectedNode.data('displayLabel', e.target.value); }} />
+
{ - if (e.target.checked) { - // Only one initial state allowed - onChange({ - ...answer, - initial_state: selectedNode.id, - }); - } else { - if (answer.initial_state === selectedNode.id){ - onChange({ - ...answer, - initial_state: '', - }); - } - } - }} + checked={answer.initial_state === selectedNode.id()} + onChange={(e) => + onChange({ + ...answer, + initial_state: e.target.checked ? selectedNode.id() : '', + }) + } />
@@ -291,71 +302,52 @@ export const FSAInput: React.FC = ({
{ - const isChecked = e.target.checked; - + checked={answer.accept_states.includes(selectedNode.id())} + onChange={(e) => onChange({ ...answer, - accept_states: isChecked - ? [...answer.accept_states, selectedNode.id] - : answer.accept_states.filter( - (s) => s !== selectedNode.id, - ), - }); - }} + accept_states: e.target.checked + ? [...answer.accept_states, selectedNode.id()] + : answer.accept_states.filter((s) => s !== selectedNode.id()), + }) + } />
- + + )} + {/* Edge Properties */} {selectedEdge && ( <>
{ - const updatedEdges = edges.map(ed => ed.id === selectedEdgeId ? { ...ed, label: e.target.value } : ed); - setEdges(updatedEdges); - syncChanges(nodes, updatedEdges); + selectedEdge.data('label', e.target.value); + syncToAnswer(); }} />
- + + )} - {!selectedNode && !selectedEdge &&
Select an element to edit
} + {!selectedNode && !selectedEdge && ( +
Select an element to edit
+ )}
-
-
- -
- { setSelectedNodeId(n.id); setSelectedEdgeId(null); }} - onEdgeClick={(_, e) => { setSelectedEdgeId(e.id); setSelectedNodeId(null); }} - deleteKeyCode={null} // Disables keyboard delete to prevent conflicts - > - - - -
+
); -}; \ No newline at end of file +}; diff --git a/src/types/FSA/index.tsx b/src/types/FSA/index.tsx index cac9160..998a922 100644 --- a/src/types/FSA/index.tsx +++ b/src/types/FSA/index.tsx @@ -1,4 +1,6 @@ // FSAResponseArea.tub.ts +import z from 'zod'; + import { BaseResponseAreaProps, BaseResponseAreaWizardProps } from '../base-props.type'; import { ResponseAreaTub } from '../response-area-tub'; diff --git a/src/types/FSA/type.ts b/src/types/FSA/type.ts index 2801b2f..a8794c9 100644 --- a/src/types/FSA/type.ts +++ b/src/types/FSA/type.ts @@ -16,9 +16,9 @@ export const fsaAnswerSchema = z.object({ export type FSA = z.infer; export const defaultFSA: FSA = { - states: ['q0'], + states: [], alphabet: [], transitions: [], - initial_state: 'q0', + initial_state: '', accept_states: [] }; \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index ea1535e..62bcefd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3507,6 +3507,11 @@ csstype@^3.0.2, csstype@^3.1.3: resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== +cytoscape@^3.33.1: + version "3.33.1" + resolved "https://registry.npmmirror.com/cytoscape/-/cytoscape-3.33.1.tgz#449e05d104b760af2912ab76482d24c01cdd4c97" + integrity sha512-iJc4TwyANnOGR1OmWhsS9ayRS3s+XQ185FmuHObThD+5AeJCakAAbWv8KimMTt08xCCLNgneQwFp+JRJOr9qGQ== + d3-array@2: version "2.12.1" resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-2.12.1.tgz#e20b41aafcdffdf5d50928004ececf815a465e81" From 21e9a1163d8597345ed7e057d439931adddb452c Mon Sep 17 00:00:00 2001 From: everythingfades Date: Tue, 27 Jan 2026 23:40:44 +0000 Subject: [PATCH 05/36] fix: align with the backend, can pass basic tests --- src/types/FSA/FSA.component.tsx | 413 +++++------------- src/types/FSA/components/ConfigPanel.tsx | 75 ++++ .../FSA/components/ItemPropertiesPanel.tsx | 159 +++++++ src/types/FSA/index.tsx | 106 ++--- src/types/FSA/styles.ts | 102 +++++ src/types/FSA/type.ts | 39 +- 6 files changed, 551 insertions(+), 343 deletions(-) create mode 100644 src/types/FSA/components/ConfigPanel.tsx create mode 100644 src/types/FSA/components/ItemPropertiesPanel.tsx create mode 100644 src/types/FSA/styles.ts diff --git a/src/types/FSA/FSA.component.tsx b/src/types/FSA/FSA.component.tsx index 0ec2d10..6e523a6 100644 --- a/src/types/FSA/FSA.component.tsx +++ b/src/types/FSA/FSA.component.tsx @@ -1,92 +1,38 @@ -import { makeStyles } from '@styles'; -import cytoscape, { Core, NodeSingular, EdgeSingular } from 'cytoscape'; -import React, { useEffect, useRef, useState } from 'react'; +import { makeStyles } from '@styles' +import cytoscape, { Core, NodeSingular, EdgeSingular } from 'cytoscape' +import React, { useEffect, useRef, useState } from 'react' -import { FSA } from './type'; +import ConfigPanel from './components/ConfigPanel' +import ItemPropertiesPanel from './components/ItemPropertiesPanel' +import { useLocalStyles } from './styles' +import { DEFAULT_FSA_CONFIG, FSA, FSAConfig } from './type' -/* -------------------- styles -------------------- */ -const useLocalStyles = makeStyles()((theme) => ({ - container: { - width: '100%', - height: 600, - display: 'flex', - border: '1px solid #ddd', - fontFamily: 'sans-serif', - }, - panel: { - width: 280, - borderRight: '1px solid #ddd', - padding: theme.spacing(2), - backgroundColor: '#fafafa', - display: 'flex', - flexDirection: 'column', - gap: theme.spacing(2), - }, - panelTitle: { - fontWeight: 600, - fontSize: 16, - borderBottom: '1px solid #eee', - paddingBottom: theme.spacing(1), - }, - field: { - display: 'flex', - flexDirection: 'column', - gap: theme.spacing(0.5), - }, - inputField: { - padding: '6px 8px', - border: '1px solid #ccc', - borderRadius: 4, - }, - checkboxRow: { - display: 'flex', - alignItems: 'center', - gap: theme.spacing(1), - }, - addButton: { - padding: '6px 10px', - cursor: 'pointer', - backgroundColor: '#fff', - border: '1px solid #ccc', - borderRadius: 4, - }, - deleteButton: { - padding: '6px', - backgroundColor: '#fff1f0', - color: '#cf1322', - border: '1px solid #ffa39e', - borderRadius: 4, - cursor: 'pointer', - fontWeight: 600, - }, - cyWrapper: { - flexGrow: 1, - }, -})); - -/* -------------------- component -------------------- */ interface FSAInputProps { - answer: FSA; - onChange: (val: FSA) => void; + answer: FSA + handleChange: (fsa: FSA) => void } -export const FSAInput: React.FC = ({ answer, onChange }) => { - const { classes } = useLocalStyles(); - const containerRef = useRef(null); - const cyRef = useRef(null); +export const FSAInput: React.FC = ({ + answer, + handleChange, +}) => { + const { classes } = useLocalStyles() + + const cyRef = useRef(null) + const containerRef = useRef(null) - // State for UI and logic - const [drawMode, setDrawMode] = useState(false); - const [fromNodeId, setFromNodeId] = useState(null); - const [toNodeId, setToNodeId] = useState(null); - const [selectedNode, setSelectedNode] = useState(null); - const [selectedEdge, setSelectedEdge] = useState(null); + const [selectedNode, setSelectedNode] = useState(null) + const [selectedEdge, setSelectedEdge] = useState(null) + const [drawMode, setDrawMode] = useState(false) + const [fromNode, setFromNode] = useState(null) + const [config, setConfig] = useState(DEFAULT_FSA_CONFIG) + const [configOpen, setConfigOpen] = useState(true) - /* -------------------- initialize cytoscape -------------------- */ + /* -------------------- init cytoscape -------------------- */ useEffect(() => { - if (!containerRef.current) return; + if (!containerRef.current) return - const cy = cytoscape({ + const cy: Core = cytoscape({ container: containerRef.current, layout: { name: 'preset' }, style: [ @@ -111,243 +57,126 @@ export const FSAInput: React.FC = ({ answer, onChange }) => { 'target-arrow-shape': 'triangle', 'line-color': '#555', 'target-arrow-color': '#555', - }, - }, - { - selector: '.edge-source', - style: { - 'border-color': '#1890ff', - 'border-width': 3, - }, - }, - { - selector: '.edge-target', - style: { - 'border-color': '#52c41a', - 'border-width': 3, + 'text-background-color': '#fff', + 'text-background-opacity': 1, + 'text-background-padding': '3px', }, }, ], - }); + }) - cyRef.current = cy; + cyRef.current = cy + return () => cy.destroy() + }, []) - return () => cy.destroy(); - }, []); - - /* -------------------- attach handlers -------------------- */ + /* -------------------- node/edge handlers -------------------- */ useEffect(() => { - const cy = cyRef.current; - if (!cy) return; - - const handleNodeTap = (e: cytoscape.EventObject) => { - const node = e.target as NodeSingular; + const cy = cyRef.current + if (!cy) return + const tapNode = (e: cytoscape.EventObject): void => { + const node = e.target as NodeSingular if (drawMode) { - if (!fromNodeId) { - setFromNodeId(node.id()); - node.addClass('edge-source'); - } else if (!toNodeId) { - setToNodeId(node.id()); - node.addClass('edge-target'); + if (!fromNode) { + setFromNode(node.id()) + node.addClass('edge-source') + } else { + cy.add({ + group: 'edges', + data: { + id: `e-${fromNode}-${node.id()}-${Date.now()}`, + source: fromNode, + target: node.id(), + label: config.epsilon_symbol, + }, + }) + cy.nodes().removeClass('edge-source') + setDrawMode(false) + setFromNode(null) + syncToBackend() } - return; + return } - setSelectedNode(node); - setSelectedEdge(null); - }; + setSelectedNode(node) + setSelectedEdge(null) + } - const handleEdgeTap = (e: cytoscape.EventObject) => { - if (drawMode) return; - setSelectedEdge(e.target as EdgeSingular); - setSelectedNode(null); - }; + const tapEdge = (e: cytoscape.EventObject): void => { + setSelectedEdge(e.target as EdgeSingular) + setSelectedNode(null) + } - cy.on('tap', 'node', handleNodeTap); - cy.on('tap', 'edge', handleEdgeTap); + cy.on('tap', 'node', tapNode) + cy.on('tap', 'edge', tapEdge) return () => { - cy.off('tap', 'node', handleNodeTap); - cy.off('tap', 'edge', handleEdgeTap); - }; - }, [drawMode, fromNodeId, toNodeId]); - - /* -------------------- draw transition effect -------------------- */ - useEffect(() => { - const cy = cyRef.current; - if (!drawMode || !fromNodeId || !toNodeId || !cy) return; - - cy.add({ - group: 'edges', - data: { - id: `e-${fromNodeId}-${toNodeId}-${Date.now()}`, - source: fromNodeId, - target: toNodeId, - label: 'a', - }, - }); - - cy.nodes().removeClass('edge-source edge-target'); - - setDrawMode(false); - setFromNodeId(null); - setToNodeId(null); - - syncToAnswer(); - }, [drawMode, fromNodeId, toNodeId]); - - /* -------------------- helpers -------------------- */ - const syncToAnswer = () => { - const cy = cyRef.current; - if (!cy) return; - - const states = cy.nodes().map((n) => n.id()); - const transitions = cy - .edges() - .map((e) => `${e.source().id()}|${e.data('label') || 'ε'}|${e.target().id()}`); - - onChange({ - ...answer, - states, - transitions, - alphabet: Array.from( - new Set(transitions.map((t) => t.split('|')[1]).filter(s => s !== undefined)), + cy.off('tap', 'node', tapNode) + cy.off('tap', 'edge', tapEdge) + } + }, [drawMode, fromNode, config.epsilon_symbol]) + + /* -------------------- sync to backend -------------------- */ + const syncToBackend = (): void => { + const cy = cyRef.current + if (!cy) return + + const fsa: FSA = { + states: cy.nodes().map((n) => n.id()), + transitions: cy.edges().map( + (e) => + `${e.source().id()}|${e.data('label') || config.epsilon_symbol}|${e.target().id()}`, ), - }); - }; + initial_state: answer.initial_state, + accept_states: answer.accept_states, + alphabet: Array.from(new Set(cy.edges().map((e) => String(e.data('label'))))), + } - const addState = () => { - const cy = cyRef.current; - if (!cy) return; + handleChange(fsa) // Only FSA, not config + } - const id = `q${cy.nodes().length}`; + /* -------------------- add state -------------------- */ + const addState = (): void => { + const cy = cyRef.current + if (!cy) return + + const id = `q${cy.nodes().length}` cy.add({ group: 'nodes', - data: { id, label: id, displayLabel: id }, + data: { id, displayLabel: id }, position: { x: 100 + Math.random() * 300, y: 100 + Math.random() * 300 }, - }); - - syncToAnswer(); - }; + }) - const deleteSelected = () => { - selectedNode?.remove(); - selectedEdge?.remove(); - setSelectedNode(null); - setSelectedEdge(null); - syncToAnswer(); - }; + syncToBackend() + } - /* -------------------- render -------------------- */ return (
-
-
Controls
- - - - - - {drawMode && ( - <> -
From Node: {fromNodeId}
-
To Node: {toNodeId}
- - )} - -
Item Properties
- - {/* Node Properties */} - {selectedNode && ( - <> -
- - { - selectedNode.data('displayLabel', e.target.value); - }} - /> -
- -
- - onChange({ - ...answer, - initial_state: e.target.checked ? selectedNode.id() : '', - }) - } - /> - -
- -
- - onChange({ - ...answer, - accept_states: e.target.checked - ? [...answer.accept_states, selectedNode.id()] - : answer.accept_states.filter((s) => s !== selectedNode.id()), - }) - } - /> - -
- - - - )} - - {/* Edge Properties */} - {selectedEdge && ( - <> -
- - { - selectedEdge.data('label', e.target.value); - syncToAnswer(); - }} - /> -
- - - - )} - - {!selectedNode && !selectedEdge && ( -
Select an element to edit
- )} -
+
+ +
- ); -}; + ) +} diff --git a/src/types/FSA/components/ConfigPanel.tsx b/src/types/FSA/components/ConfigPanel.tsx new file mode 100644 index 0000000..870db01 --- /dev/null +++ b/src/types/FSA/components/ConfigPanel.tsx @@ -0,0 +1,75 @@ +import React from 'react' + +interface EvaluationConfigPanelProps> { + config: T + setConfig: React.Dispatch> + configOpen: boolean + setConfigOpen: React.Dispatch> + classes: Record +} + +export default function EvaluationConfigPanel>({ + config, + setConfig, + configOpen, + setConfigOpen, + classes, +}: EvaluationConfigPanelProps) { + return ( +
+
setConfigOpen((o) => !o)} + > + Evaluation Config + {configOpen ? '▾' : '▸'} +
+ + {configOpen && ( +
+ {Object.entries(config).map(([key, value]) => ( +
+ + + {typeof value === 'boolean' ? ( + + setConfig((prev) => ({ + ...prev, + [key]: e.target.checked, + })) + } + /> + ) : typeof value === 'number' ? ( + + setConfig((prev) => ({ + ...prev, + [key]: Number(e.target.value), + })) + } + /> + ) : ( + + setConfig((prev) => ({ + ...prev, + [key]: e.target.value, + })) + } + /> + )} +
+ ))} +
+ )} +
+ ) +} diff --git a/src/types/FSA/components/ItemPropertiesPanel.tsx b/src/types/FSA/components/ItemPropertiesPanel.tsx new file mode 100644 index 0000000..d321471 --- /dev/null +++ b/src/types/FSA/components/ItemPropertiesPanel.tsx @@ -0,0 +1,159 @@ +import type { Core, NodeSingular, EdgeSingular } from 'cytoscape' +import React from 'react' + +import { FSA } from '../type' + +interface ItemPropertiesPanelProps { + cyRef: React.MutableRefObject + classes: Record + + addState: () => void + + drawMode: boolean + setDrawMode: React.Dispatch> + setFromNode: (id: string | null) => void + + selectedNode: NodeSingular | null + setSelectedNode: (n: NodeSingular | null) => void + + selectedEdge: EdgeSingular | null + setSelectedEdge: (e: EdgeSingular | null) => void + + answer: FSA + handleChange: (fsa: FSA) => void + + syncToBackend: () => void +} + +export default function ItemPropertiesPanel({ + cyRef, + classes, + addState, + drawMode, + setDrawMode, + setFromNode, + selectedNode, + setSelectedNode, + selectedEdge, + setSelectedEdge, + answer, + handleChange, + syncToBackend, +}: ItemPropertiesPanelProps): JSX.Element { + return ( +
+
Item Properties
+ + {/* -------------------- Actions -------------------- */} + + + + + + + {/* -------------------- Node Properties -------------------- */} + {selectedNode && ( + <> +
+ + { + selectedNode.data('displayLabel', e.target.value) + // syncToBackend() + }} + /> +
+ + {/* Initial State (unique) */} +
+ { + handleChange({ + ...answer, + initial_state: e.target.checked ? selectedNode.id() : answer.initial_state, + }) + // syncToBackend() + }} + /> + +
+ + {/* Accepting State (multiple allowed) */} +
+ { + handleChange({ + ...answer, + accept_states: e.target.checked + ? [...answer.accept_states, selectedNode.id()] + : answer.accept_states.filter( + (id) => id !== selectedNode.id(), + ), + }) + // syncToBackend() + }} + /> + +
+ + )} + + {/* -------------------- Edge Properties -------------------- */} + {selectedEdge && ( +
+ + { + selectedEdge.data('label', e.target.value) + syncToBackend() + }} + /> +
+ )} + + {/* -------------------- Delete -------------------- */} + {(selectedNode || selectedEdge) && ( + + )} +
+ ) +} diff --git a/src/types/FSA/index.tsx b/src/types/FSA/index.tsx index 998a922..7e7e837 100644 --- a/src/types/FSA/index.tsx +++ b/src/types/FSA/index.tsx @@ -1,68 +1,74 @@ -// FSAResponseArea.tub.ts -import z from 'zod'; +import { z } from 'zod' -import { BaseResponseAreaProps, BaseResponseAreaWizardProps } from '../base-props.type'; -import { ResponseAreaTub } from '../response-area-tub'; +import { + BaseResponseAreaProps, + BaseResponseAreaWizardProps, +} from '../base-props.type' +import { ResponseAreaTub } from '../response-area-tub' -import { FSAInput } from './FSA.component'; -import { fsaAnswerSchema, FSA, defaultFSA } from './type'; +import { FSAInput } from './FSA.component' +import { fsaAnswerSchema, FSA, defaultFSA, DEFAULT_FSA_CONFIG, FSAConfig } from './type' export class FSAResponseAreaTub extends ResponseAreaTub { - public readonly responseType = 'FSA'; - public readonly displayWideInput = true; - protected answerSchema = fsaAnswerSchema; - protected answer: FSA = defaultFSA; + public readonly responseType = 'FSA' + public readonly displayWideInput: boolean = true - initWithConfig = (config: any) => { - this.config = config // config is not used for now - this.answer = defaultFSA - } + protected answerSchema = fsaAnswerSchema + protected answer: FSA = defaultFSA // Never undefined now + protected config: FSAConfig = DEFAULT_FSA_CONFIG + private debug = '' + initWithConfig = (config: any) => { + this.config = { + ...DEFAULT_FSA_CONFIG, + ...config, // not too sure about this, maybe the opposite so the default config is overwritten? + } + } + customCheck = () => {} // will set this up later - InputComponent = (props: BaseResponseAreaProps) => { - // Always derive a local FSA value - console.log('FSA InputComponent props.answer:', props.answer, typeof props.answer); - const fsaAnswer: FSA = (() => { - if (!props.answer) return defaultFSA; - if (typeof props.answer === 'string') { - try { - return JSON.parse(props.answer); - } catch { - return defaultFSA; - } - } + /* -------------------- Input -------------------- */ + public InputComponent = (props: BaseResponseAreaProps): JSX.Element => { + // Always ensure a valid FSA is passed + const parsedAnswer = this.answerSchema.safeParse(props.answer) + const validAnswer: FSA = parsedAnswer.success ? parsedAnswer.data : defaultFSA - return props.answer as FSA; - })(); + return ( + <> +

{this.debug}

+ { + this.debug=JSON.stringify(val) + props.handleChange(val) + }} + /> + + ) + } + /* -------------------- Wizard -------------------- */ + public WizardComponent = ( + props: BaseResponseAreaWizardProps, + ): JSX.Element => { return ( <> -

Input Component

+

answer: {JSON.stringify(this.answer)} config: {JSON.stringify(this.config)}

{ - props.handleChange(JSON.stringify(answer)); + answer={this.answer} // Guaranteed defined + handleChange={(val: FSA): void => { + this.answer = val + console.log('Wizard val:', val) + props.handleChange({ + responseType: this.responseType, + answer: val, + config: this.config as unknown as Record + }) }} /> - ); - }; - - WizardComponent = (props: BaseResponseAreaWizardProps) => { - return ( - { - this.answer = answer - props.handleChange({ - responseType: this.responseType, - answer, - }); - }} - /> - ); + ) } -} \ No newline at end of file +} diff --git a/src/types/FSA/styles.ts b/src/types/FSA/styles.ts new file mode 100644 index 0000000..3ae419f --- /dev/null +++ b/src/types/FSA/styles.ts @@ -0,0 +1,102 @@ +import { makeStyles } from "@styles"; + +export const useLocalStyles = makeStyles()((theme) => ({ + container: { + width: '100%', + height: 600, + display: 'flex', + border: '1px solid #ddd', + fontFamily: 'sans-serif', + position: 'relative', + }, + + panel: { + width: 280, + padding: theme.spacing(2), + borderRight: '1px solid #ddd', + backgroundColor: '#fafafa', + display: 'flex', + flexDirection: 'column', + gap: theme.spacing(2), + }, + + floatingConfig: { + position: 'absolute', + right: 12, + bottom: 12, + width: 320, + maxHeight: 420, + backgroundColor: '#fafafa', + border: '1px solid #ddd', + borderRadius: 6, + boxShadow: '0 4px 12px rgba(0,0,0,0.15)', + display: 'flex', + flexDirection: 'column', + zIndex: 10, + }, + + configHeader: { + padding: theme.spacing(1), + fontWeight: 600, + borderBottom: '1px solid #eee', + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', + cursor: 'pointer', + }, + + configBody: { + padding: theme.spacing(1.5), + overflowY: 'auto', + display: 'flex', + flexDirection: 'column', + gap: theme.spacing(1.5), + }, + + panelTitle: { + fontWeight: 600, + fontSize: 16, + borderBottom: '1px solid #eee', + paddingBottom: theme.spacing(1), + }, + + field: { + display: 'flex', + flexDirection: 'column', + gap: theme.spacing(0.5), + }, + + inputField: { + padding: '6px 8px', + border: '1px solid #ccc', + borderRadius: 4, + }, + + checkboxRow: { + display: 'flex', + alignItems: 'center', + gap: theme.spacing(1), + }, + + addButton: { + padding: '6px 10px', + backgroundColor: '#fff', + border: '1px solid #ccc', + borderRadius: 4, + cursor: 'pointer', + }, + + deleteButton: { + padding: '6px', + backgroundColor: '#fff1f0', + color: '#cf1322', + border: '1px solid #ffa39e', + borderRadius: 4, + cursor: 'pointer', + fontWeight: 600, + }, + + cyWrapper: { + flexGrow: 1, + }, +})) \ No newline at end of file diff --git a/src/types/FSA/type.ts b/src/types/FSA/type.ts index a8794c9..122174c 100644 --- a/src/types/FSA/type.ts +++ b/src/types/FSA/type.ts @@ -21,4 +21,41 @@ export const defaultFSA: FSA = { transitions: [], initial_state: '', accept_states: [] -}; \ No newline at end of file +}; + +export const fsaConfigSchema = z.object({ + evaluation_mode: z.enum(['strict', 'lenient', 'partial']).optional(), + expected_type: z.enum(['DFA', 'NFA', 'any']).optional(), + feedback_verbosity: z.enum(['minimal', 'standard', 'detailed']).optional(), + + check_minimality: z.boolean().optional(), + check_completeness: z.boolean().optional(), + + highlight_errors: z.boolean().optional(), + show_counterexample: z.boolean().optional(), + + max_test_length: z.number().int().positive().optional(), + + is_dev: z.boolean().optional(), + + epsilon_symbol: z.string(), +}) + +export type FSAConfig = z.infer + +export const DEFAULT_FSA_CONFIG: FSAConfig = { + evaluation_mode: "lenient", + expected_type: "any", + feedback_verbosity: "standard", + + check_minimality: false, + check_completeness: false, + + highlight_errors: true, + show_counterexample: true, + + max_test_length: 10, + + is_dev: false, + epsilon_symbol: "epsilon" +} From 3e813d71a5cc427cf0b0e131550a57954f42eee4 Mon Sep 17 00:00:00 2001 From: everythingfades Date: Wed, 28 Jan 2026 17:09:41 +0000 Subject: [PATCH 06/36] feat: added feedback --- src/types/FSA/FSA.component.tsx | 5 +- src/types/FSA/components/FSAFeedbackPanel.tsx | 226 ++++++++++++++++++ .../FSA/components/ItemPropertiesPanel.tsx | 9 +- src/types/FSA/index.tsx | 61 +++-- src/types/FSA/type.ts | 137 +++++++++++ 5 files changed, 419 insertions(+), 19 deletions(-) create mode 100644 src/types/FSA/components/FSAFeedbackPanel.tsx diff --git a/src/types/FSA/FSA.component.tsx b/src/types/FSA/FSA.component.tsx index 6e523a6..de58b19 100644 --- a/src/types/FSA/FSA.component.tsx +++ b/src/types/FSA/FSA.component.tsx @@ -5,16 +5,18 @@ import React, { useEffect, useRef, useState } from 'react' import ConfigPanel from './components/ConfigPanel' import ItemPropertiesPanel from './components/ItemPropertiesPanel' import { useLocalStyles } from './styles' -import { DEFAULT_FSA_CONFIG, FSA, FSAConfig } from './type' +import { DEFAULT_FSA_CONFIG, FSA, FSAConfig, FSAFeedback } from './type' interface FSAInputProps { answer: FSA handleChange: (fsa: FSA) => void + feedback: FSAFeedback | null } export const FSAInput: React.FC = ({ answer, handleChange, + feedback }) => { const { classes } = useLocalStyles() @@ -166,6 +168,7 @@ export const FSAInput: React.FC = ({ syncToBackend={syncToBackend} handleChange={handleChange} answer={answer} + feedback={feedback} />
diff --git a/src/types/FSA/components/FSAFeedbackPanel.tsx b/src/types/FSA/components/FSAFeedbackPanel.tsx new file mode 100644 index 0000000..d7ecc5d --- /dev/null +++ b/src/types/FSA/components/FSAFeedbackPanel.tsx @@ -0,0 +1,226 @@ +import React, { useMemo } from 'react' + +import { FSAFeedbackSchema, type FSAFeedback } from '../type' + +interface FSAFeedbackPanelProps { + feedback: FSAFeedback | null +} + +export function FSAFeedbackPanel({ feedback }: FSAFeedbackPanelProps) { + console.log(feedback) + const parsed = useMemo(() => FSAFeedbackSchema.safeParse(feedback), [feedback]) + if (!feedback || !parsed.success) { + console.log(parsed.error?.message) + return ( +
+ No feedback yet {parsed.error?.message} +
+ ) + } + + const safeFeedback = parsed.data + + return ( +
+ {/* ================= Summary ================= */} + {safeFeedback.summary && ( +
+ {safeFeedback.summary} +
+ )} + + {/* ================= Errors ================= */} + {safeFeedback.errors.length > 0 && ( + + )} + + {/* ================= Warnings ================= */} + {safeFeedback.warnings.length > 0 && ( + + )} + + {/* ================= Structural Info ================= */} + {safeFeedback.structural && ( +
+ + + + + + {safeFeedback.structural.unreachable_states.length > 0 && ( + + )} + + {safeFeedback.structural.dead_states.length > 0 && ( + + )} +
+ )} + + {/* ================= Language ================= */} + {safeFeedback.language && ( +
+ + + {!safeFeedback.language.are_equivalent && safeFeedback.language.counterexample && ( + + )} +
+ )} + + {/* ================= Test Results ================= */} + {safeFeedback.test_results.length > 0 && ( +
+ {safeFeedback.test_results.map((t, i) => ( +
+ {JSON.stringify(t.input)} + {t.passed ? '✓' : '✗'} +
+ ))} +
+ )} + + {/* ================= Hints ================= */} + {safeFeedback.hints.length > 0 && ( +
+
    + {safeFeedback.hints.map((h, i) => ( +
  • {h}
  • + ))} +
+
+ )} +
+ ) +} + +/* =========================== + Helper components +=========================== */ + +function FeedbackSection({ + title, + items, + accent, +}: { + title: string + items: any[] + accent: string +}) { + return ( +
+
+ {items.map((e, i) => ( +
+
{e.message}
+ +
+ {e.code} · {e.severity} +
+ + {e.suggestion && ( +
+ 💡 {e.suggestion} +
+ )} +
+ ))} +
+
+ ) +} + + +function Section({ + title, + children, +}: { + title: string + children: React.ReactNode +}) { + return ( +
+
+ {title} +
+ {children} +
+ ) +} + +function KV({ label, value }: { label: string; value: React.ReactNode }) { + return ( +
+ {label}: + {value} +
+ ) +} + +function bool(v: boolean) { + return v ? 'Yes' : 'No' +} diff --git a/src/types/FSA/components/ItemPropertiesPanel.tsx b/src/types/FSA/components/ItemPropertiesPanel.tsx index d321471..bb23291 100644 --- a/src/types/FSA/components/ItemPropertiesPanel.tsx +++ b/src/types/FSA/components/ItemPropertiesPanel.tsx @@ -1,7 +1,9 @@ import type { Core, NodeSingular, EdgeSingular } from 'cytoscape' import React from 'react' -import { FSA } from '../type' +import { FSA, FSAFeedback } from '../type' + +import { FSAFeedbackPanel } from './FSAFeedbackPanel' interface ItemPropertiesPanelProps { cyRef: React.MutableRefObject @@ -23,6 +25,7 @@ interface ItemPropertiesPanelProps { handleChange: (fsa: FSA) => void syncToBackend: () => void + feedback: FSAFeedback | null } export default function ItemPropertiesPanel({ @@ -39,6 +42,7 @@ export default function ItemPropertiesPanel({ answer, handleChange, syncToBackend, + feedback }: ItemPropertiesPanelProps): JSX.Element { return (
@@ -154,6 +158,9 @@ export default function ItemPropertiesPanel({ Delete Selected )} +
) } diff --git a/src/types/FSA/index.tsx b/src/types/FSA/index.tsx index 7e7e837..d6a7314 100644 --- a/src/types/FSA/index.tsx +++ b/src/types/FSA/index.tsx @@ -1,4 +1,6 @@ -import { z } from 'zod' +// import React, { useMemo } from 'react' +// import { z } from 'zod' + import { BaseResponseAreaProps, @@ -7,7 +9,7 @@ import { import { ResponseAreaTub } from '../response-area-tub' import { FSAInput } from './FSA.component' -import { fsaAnswerSchema, FSA, defaultFSA, DEFAULT_FSA_CONFIG, FSAConfig } from './type' +import { fsaAnswerSchema, FSA, defaultFSA, DEFAULT_FSA_CONFIG, FSAConfig, FSAFeedback } from './type' export class FSAResponseAreaTub extends ResponseAreaTub { public readonly responseType = 'FSA' @@ -16,7 +18,10 @@ export class FSAResponseAreaTub extends ResponseAreaTub { protected answerSchema = fsaAnswerSchema protected answer: FSA = defaultFSA // Never undefined now protected config: FSAConfig = DEFAULT_FSA_CONFIG - private debug = '' + // private feedback: FSAFeedback | null = null + + public readonly delegateFeedback = false // we want to manage our own feedback + public readonly delegateLivePreview = false // we want live previews initWithConfig = (config: any) => { this.config = { @@ -28,25 +33,46 @@ export class FSAResponseAreaTub extends ResponseAreaTub { customCheck = () => {} // will set this up later /* -------------------- Input -------------------- */ - public InputComponent = (props: BaseResponseAreaProps): JSX.Element => { - // Always ensure a valid FSA is passed - const parsedAnswer = this.answerSchema.safeParse(props.answer) - const validAnswer: FSA = parsedAnswer.success ? parsedAnswer.data : defaultFSA - return ( - <> -

{this.debug}

+public InputComponent = (props: BaseResponseAreaProps): JSX.Element => { + // Ensure a valid FSA answer + const validAnswer: FSA = this.answerSchema.safeParse(props.answer).success + ? this.answerSchema.safeParse(props.answer).data ?? defaultFSA + : defaultFSA + + // // Parse feedback safely and memoize so it updates when props.feedback changes + // const feedback: FSAFeedback | null = useMemo(() => { + // if (!props.feedback?.feedback) return null + // try { + // return JSON.parse(props.feedback.feedback) + // } catch { + // return null + // } + // }, [props.feedback?.feedback]) + + return ( + <> +

feedback: {props.feedback?.feedback}

{ + const raw = props.feedback?.feedback + if (!raw) return {} + try { + // split by
and take the second part, trim whitespace + const jsonPart = raw.split('
')[1]?.trim() ?? '{}' + return JSON.parse(jsonPart) + } catch { + return {} // fallback to empty object if parsing fails + } + })()} answer={validAnswer} - handleChange={(val: FSA): void => { - this.debug=JSON.stringify(val) - props.handleChange(val) - }} + handleChange={(val: FSA) => props.handleChange(val)} /> - - ) - } + + ) +} + /* -------------------- Wizard -------------------- */ public WizardComponent = ( @@ -57,6 +83,7 @@ export class FSAResponseAreaTub extends ResponseAreaTub {

answer: {JSON.stringify(this.answer)} config: {JSON.stringify(this.config)}

{ this.answer = val diff --git a/src/types/FSA/type.ts b/src/types/FSA/type.ts index 122174c..516ed54 100644 --- a/src/types/FSA/type.ts +++ b/src/types/FSA/type.ts @@ -59,3 +59,140 @@ export const DEFAULT_FSA_CONFIG: FSAConfig = { is_dev: false, epsilon_symbol: "epsilon" } + +/* =========================== + Error codes +=========================== */ + +export const ErrorCodeSchema = z.enum([ + "INVALID_STATE", + "INVALID_INITIAL", + "INVALID_ACCEPT", + "INVALID_SYMBOL", + + "INVALID_TRANSITION_SOURCE", + "INVALID_TRANSITION_DEST", + "INVALID_TRANSITION_SYMBOL", + "MISSING_TRANSITION", + "DUPLICATE_TRANSITION", + + "UNREACHABLE_STATE", + "DEAD_STATE", + + "WRONG_AUTOMATON_TYPE", + "NOT_DETERMINISTIC", + "NOT_COMPLETE", + "NOT_MINIMAL", + + "LANGUAGE_MISMATCH", + "TEST_CASE_FAILED", + + "EMPTY_STATES", + "EMPTY_ALPHABET", + "EVALUATION_ERROR", +]); + +/* =========================== + Element highlighting +=========================== */ + +export const ElementHighlightTypeSchema = z.enum([ + "state", + "transition", + "initial_state", + "accept_state", + "alphabet_symbol", +]); +export const ElementHighlightSchema = z.object({ + type: ElementHighlightTypeSchema, + state_id: z.string().nullable().optional(), + from_state: z.string().nullable().optional(), + to_state: z.string().nullable().optional(), + symbol: z.string().nullable().optional(), +}); + + +/* =========================== + Validation errors +=========================== */ + +export const ValidationSeveritySchema = z.enum([ + "error", + "warning", + "info", +]); + +export const ValidationErrorSchema = z.object({ + message: z.string(), + code: ErrorCodeSchema, + severity: ValidationSeveritySchema.default("error"), + highlight: ElementHighlightSchema.nullable().optional(), + suggestion: z.string().optional(), +}); + +/* =========================== + Test results +=========================== */ + +export const TestResultSchema = z.object({ + input: z.string(), + expected: z.boolean(), + actual: z.boolean(), + passed: z.boolean(), + trace: z.array(z.string()).optional(), +}); + +/* =========================== + Structural analysis +=========================== */ + +export const StructuralInfoSchema = z.object({ + is_deterministic: z.boolean(), + is_complete: z.boolean(), + + num_states: z.number().int().min(0), + num_transitions: z.number().int().min(0), + + unreachable_states: z.array(z.string()).default([]), + dead_states: z.array(z.string()).default([]), +}); + +/* =========================== + Language comparison +=========================== */ + +export const CounterexampleTypeSchema = z.enum([ + "should_accept", + "should_reject", +]); + +export const LanguageComparisonSchema = z.object({ + are_equivalent: z.boolean(), + counterexample: z.string().nullable().optional(), + counterexample_type: CounterexampleTypeSchema.nullable().optional(), +}); + +/* =========================== + Top-level feedback +=========================== */ + +export const FSAFeedbackSchema = z.object({ + summary: z.string().default(""), + + errors: z.array(ValidationErrorSchema).default([]), + warnings: z.array(ValidationErrorSchema).default([]), + + structural: StructuralInfoSchema.optional(), + language: LanguageComparisonSchema.optional(), + + test_results: z.array(TestResultSchema).default([]), + hints: z.array(z.string()).default([]), +}); + +export type ErrorCode = z.infer; +export type ElementHighlight = z.infer; +export type ValidationError = z.infer; +export type TestResult = z.infer; +export type StructuralInfo = z.infer; +export type LanguageComparison = z.infer; +export type FSAFeedback = z.infer; From 5456800a3b9f4ef2bd22360d1b6c0be9a2b04b59 Mon Sep 17 00:00:00 2001 From: everythingfades Date: Wed, 28 Jan 2026 17:14:09 +0000 Subject: [PATCH 07/36] fix: remove debugging logs --- src/types/FSA/components/FSAFeedbackPanel.tsx | 1 - src/types/FSA/index.tsx | 66 +++++++++---------- 2 files changed, 30 insertions(+), 37 deletions(-) diff --git a/src/types/FSA/components/FSAFeedbackPanel.tsx b/src/types/FSA/components/FSAFeedbackPanel.tsx index d7ecc5d..4e89fe9 100644 --- a/src/types/FSA/components/FSAFeedbackPanel.tsx +++ b/src/types/FSA/components/FSAFeedbackPanel.tsx @@ -7,7 +7,6 @@ interface FSAFeedbackPanelProps { } export function FSAFeedbackPanel({ feedback }: FSAFeedbackPanelProps) { - console.log(feedback) const parsed = useMemo(() => FSAFeedbackSchema.safeParse(feedback), [feedback]) if (!feedback || !parsed.success) { console.log(parsed.error?.message) diff --git a/src/types/FSA/index.tsx b/src/types/FSA/index.tsx index d6a7314..7ec599f 100644 --- a/src/types/FSA/index.tsx +++ b/src/types/FSA/index.tsx @@ -51,25 +51,22 @@ public InputComponent = (props: BaseResponseAreaProps): JSX.Element => { // }, [props.feedback?.feedback]) return ( - <> -

feedback: {props.feedback?.feedback}

- { - const raw = props.feedback?.feedback - if (!raw) return {} - try { - // split by
and take the second part, trim whitespace - const jsonPart = raw.split('
')[1]?.trim() ?? '{}' - return JSON.parse(jsonPart) - } catch { - return {} // fallback to empty object if parsing fails - } - })()} - answer={validAnswer} - handleChange={(val: FSA) => props.handleChange(val)} - /> - + { + const raw = props.feedback?.feedback + if (!raw) return {} + try { + // split by
and take the second part, trim whitespace + const jsonPart = raw.split('
')[1]?.trim() ?? '{}' + return JSON.parse(jsonPart) + } catch { + return {} // fallback to empty object if parsing fails + } + })()} + answer={validAnswer} + handleChange={(val: FSA) => props.handleChange(val)} + /> ) } @@ -79,23 +76,20 @@ public InputComponent = (props: BaseResponseAreaProps): JSX.Element => { props: BaseResponseAreaWizardProps, ): JSX.Element => { return ( - <> -

answer: {JSON.stringify(this.answer)} config: {JSON.stringify(this.config)}

- { - this.answer = val - console.log('Wizard val:', val) - props.handleChange({ - responseType: this.responseType, - answer: val, - config: this.config as unknown as Record - }) - }} - /> - + { + this.answer = val + console.log('Wizard val:', val) + props.handleChange({ + responseType: this.responseType, + answer: val, + config: this.config as unknown as Record + }) + }} + /> ) } } From 822f87e0815157e95b7254af8dc1d2dc32fef2f7 Mon Sep 17 00:00:00 2001 From: Johnny Wan <2695191695@qq.com> Date: Fri, 30 Jan 2026 04:38:42 +0000 Subject: [PATCH 08/36] feat: implement GraphQL data fetching and add live preview functionality in FSA component --- .gitignore | 2 +- externals/api/fetcher.ts | 61 ++- src/types/FSA/FSA.component.tsx | 462 +++++++++++++++++- .../FSA/components/ItemPropertiesPanel.tsx | 26 +- src/types/FSA/index.tsx | 55 +-- 5 files changed, 568 insertions(+), 38 deletions(-) diff --git a/.gitignore b/.gitignore index 1b68e4d..a0919bd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ # dependencies /node_modules - +/client-web-main # production /build /dist diff --git a/externals/api/fetcher.ts b/externals/api/fetcher.ts index 4fa1f95..b0f1f8f 100644 --- a/externals/api/fetcher.ts +++ b/externals/api/fetcher.ts @@ -1,7 +1,62 @@ +type GraphqlResponse = { + data: TData | null + errors?: Array<{ message?: string }> | null +} + +function getGraphqlUrl(): string { + // 1) Explicit override (handy in sandbox contexts) + const override = (globalThis as any).__LF_GRAPHQL_URL + if (typeof override === 'string' && override.length > 0) return override + + // 2) Common paths (works when frontend and API share origin/proxy) + // Prefer /graphql (client-web-main default), but also try /api/graphql (common convention) + return new URL('/graphql', globalThis.location?.origin ?? 'http://localhost') + .toString() +} + export const fetchData = ( - _query: string, - _variables?: TVariables, + query: string, + variables?: TVariables, _options?: unknown, ): (() => Promise) => { - return null as any + return async () => { + const url = getGraphqlUrl() + + const res = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + // In the real app, auth is usually cookie-based or handled by the host page. + // Include credentials so the sandbox can reuse the host session. + credentials: 'include', + body: JSON.stringify({ + query, + variables, + }), + }) + + if (!res.ok) { + throw new Error( + `GraphQL request failed (${res.status} ${res.statusText})`, + ) + } + + const json = (await res.json()) as GraphqlResponse + + if (json.errors && json.errors.length > 0) { + const msg = + json.errors + .map(e => e?.message) + .filter(Boolean) + .join('\n') || 'GraphQL error' + throw new Error(msg) + } + + if (json.data === null) { + throw new Error('GraphQL: no data returned') + } + + return json.data + } } diff --git a/src/types/FSA/FSA.component.tsx b/src/types/FSA/FSA.component.tsx index de58b19..a1e5518 100644 --- a/src/types/FSA/FSA.component.tsx +++ b/src/types/FSA/FSA.component.tsx @@ -1,22 +1,37 @@ -import { makeStyles } from '@styles' import cytoscape, { Core, NodeSingular, EdgeSingular } from 'cytoscape' -import React, { useEffect, useRef, useState } from 'react' +import React, { useEffect, useMemo, useRef, useState } from 'react' import ConfigPanel from './components/ConfigPanel' import ItemPropertiesPanel from './components/ItemPropertiesPanel' import { useLocalStyles } from './styles' -import { DEFAULT_FSA_CONFIG, FSA, FSAConfig, FSAFeedback } from './type' +import { useSubmitResponsePreviewMutation } from '@api/graphql' + +import { + DEFAULT_FSA_CONFIG, + FSA, + FSAConfig, + FSAFeedback, + FSAFeedbackSchema, + ValidationError, + ValidationErrorSchema, +} from './type' interface FSAInputProps { answer: FSA handleChange: (fsa: FSA) => void feedback: FSAFeedback | null + hasPreview?: boolean + responseAreaId?: string + universalResponseAreaId?: string } export const FSAInput: React.FC = ({ answer, handleChange, - feedback + feedback, + hasPreview, + responseAreaId, + universalResponseAreaId, }) => { const { classes } = useLocalStyles() @@ -30,6 +45,90 @@ export const FSAInput: React.FC = ({ const [config, setConfig] = useState(DEFAULT_FSA_CONFIG) const [configOpen, setConfigOpen] = useState(true) + const [previewIsLoading, setPreviewIsLoading] = useState(false) + const [previewText, setPreviewText] = useState(null) + const [previewFeedback, setPreviewFeedback] = useState(null) + + type PreviewSympy = Record + + const localPreview = useMemo(() => { + // Always compute a fast local preview as a fallback. + return validateFsaPreview(answer, config) + }, [answer, config]) + + /* -------------------- debounce preview call -------------------- */ + useEffect(() => { + // If live preview is off, show normal (check) feedback if any. + if (!hasPreview) { + setPreviewIsLoading(false) + setPreviewText(null) + setPreviewFeedback(feedback) + return + } + + // If we can't call the backend preview, fall back to local validation. + if (!responseAreaId || !universalResponseAreaId) { + setPreviewIsLoading(false) + setPreviewText(null) + setPreviewFeedback(localPreview) + return + } + + const requireDeterministic = config.expected_type === 'DFA' + const showWarnings = true + + const timeout = setTimeout(async () => { + setPreviewIsLoading(true) + try { + const data = await useSubmitResponsePreviewMutation.fetcher({ + submission: answer, + additionalParams: { + require_deterministic: requireDeterministic, + show_warnings: showWarnings, + }, + responseAreaId, + universalResponseAreaId, + })() + + const res = data.submitResponsePreview + + // Best-effort mapping: the backend returns { preview: { latex, feedback, sympy } } + const previewObj = res.preview + if (previewObj && typeof previewObj === 'object') { + const maybeFeedback = (previewObj as any).feedback + const maybeSympy = (previewObj as any).sympy as PreviewSympy | undefined + + setPreviewText(typeof maybeFeedback === 'string' ? maybeFeedback : null) + setPreviewFeedback( + maybeSympy ? sympyToFsaFeedback(maybeSympy, answer) : localPreview, + ) + } else { + // No structured preview; show server string feedback if present + setPreviewText(res.feedback ?? null) + setPreviewFeedback(localPreview) + } + } catch { + // If network preview fails (common in sandbox contexts), still give the student useful local feedback. + setPreviewText(null) + setPreviewFeedback(localPreview) + } finally { + setPreviewIsLoading(false) + } + }, 500) + + return () => { + clearTimeout(timeout) + } + }, [ + answer, + config.expected_type, + feedback, + hasPreview, + localPreview, + responseAreaId, + universalResponseAreaId, + ]) + /* -------------------- init cytoscape -------------------- */ useEffect(() => { if (!containerRef.current) return @@ -64,6 +163,26 @@ export const FSAInput: React.FC = ({ 'text-background-padding': '3px', }, }, + { + selector: '.preview-error', + style: { + 'border-width': 4, + 'border-color': '#d32f2f', + 'line-color': '#d32f2f', + 'target-arrow-color': '#d32f2f', + 'background-color': '#ffebee', + }, + }, + { + selector: '.preview-warning', + style: { + 'border-width': 4, + 'border-color': '#ed6c02', + 'line-color': '#ed6c02', + 'target-arrow-color': '#ed6c02', + 'background-color': '#fff3e0', + }, + }, ], }) @@ -71,6 +190,24 @@ export const FSAInput: React.FC = ({ return () => cy.destroy() }, []) + /* -------------------- apply preview highlights -------------------- */ + useEffect(() => { + const cy = cyRef.current + if (!cy) return + + cy.elements().removeClass('preview-error preview-warning') + + const fb = previewFeedback + if (!fb) return + + for (const err of fb.errors) { + applyHighlight(cy, err, 'preview-error') + } + for (const warn of fb.warnings) { + applyHighlight(cy, warn, 'preview-warning') + } + }, [previewFeedback]) + /* -------------------- node/edge handlers -------------------- */ useEffect(() => { const cy = cyRef.current @@ -168,7 +305,9 @@ export const FSAInput: React.FC = ({ syncToBackend={syncToBackend} handleChange={handleChange} answer={answer} - feedback={feedback} + feedback={previewFeedback} + previewText={previewText} + previewIsLoading={previewIsLoading} />
@@ -183,3 +322,316 @@ export const FSAInput: React.FC = ({
) } + +function parseTransition(t: string): { from: string; symbol: string; to: string } { + const [from = '', symbol = '', to = ''] = t.split('|') + return { from, symbol, to } +} + +function sympyToFsaFeedback(sympy: Record, answer: FSA): FSAFeedback { + const errorsRaw = Array.isArray(sympy.errors) ? sympy.errors : [] + const warningsRaw = Array.isArray(sympy.warnings) ? sympy.warnings : [] + + const errors: ValidationError[] = [] + const warnings: ValidationError[] = [] + + for (const e of errorsRaw) { + const parsed = ValidationErrorSchema.safeParse(e) + if (parsed.success) errors.push(parsed.data) + } + for (const w of warningsRaw) { + const parsed = ValidationErrorSchema.safeParse(w) + if (parsed.success) warnings.push(parsed.data) + } + + const numStates = readNumber(sympy.num_states, answer.states.length) + const numTransitions = readNumber(sympy.num_transitions, answer.transitions.length) + + const isDet = readBoolean(sympy.is_deterministic, false) + const isComplete = readBoolean(sympy.is_complete, false) + const unreachable = readStringArray(sympy.unreachable_states) + const dead = readStringArray(sympy.dead_states) + + const stateWord = numStates === 1 ? 'state' : 'states' + const transWord = numTransitions === 1 ? 'transition' : 'transitions' + const summary = `${isDet ? 'DFA' : 'NFA'} with ${numStates} ${stateWord} and ${numTransitions} ${transWord}` + + const candidate: FSAFeedback = { + summary, + errors, + warnings, + structural: { + is_deterministic: isDet, + is_complete: isComplete, + num_states: numStates, + num_transitions: numTransitions, + unreachable_states: unreachable, + dead_states: dead, + }, + test_results: [], + hints: [], + } + + const parsed = FSAFeedbackSchema.safeParse(candidate) + return parsed.success ? parsed.data : candidate +} + +function readBoolean(v: unknown, fallback: boolean): boolean { + return typeof v === 'boolean' ? v : fallback +} + +function readNumber(v: unknown, fallback: number): number { + return typeof v === 'number' && Number.isFinite(v) ? v : fallback +} + +function readStringArray(v: unknown): string[] { + if (!Array.isArray(v)) return [] + return v.filter(x => typeof x === 'string') as string[] +} + +function validateFsaPreview(answer: FSA, config: FSAConfig): FSAFeedback { + const errors: ValidationError[] = [] + const warnings: ValidationError[] = [] + + const states = new Set(answer.states) + const alphabet = new Set(answer.alphabet) + + const transitions = answer.transitions.map(parseTransition) + + // Structural errors + if (answer.states.length === 0) { + errors.push({ + message: 'No states defined.', + code: 'EMPTY_STATES', + severity: 'error', + highlight: null, + }) + } + + if (!answer.initial_state || !states.has(answer.initial_state)) { + errors.push({ + message: 'Initial state is missing or not in the set of states.', + code: 'INVALID_INITIAL', + severity: 'error', + highlight: answer.initial_state + ? { type: 'initial_state', state_id: answer.initial_state } + : { type: 'initial_state', state_id: null }, + }) + } + + for (const acc of answer.accept_states) { + if (!states.has(acc)) { + errors.push({ + message: `Accept state '${acc}' is not in the set of states.`, + code: 'INVALID_ACCEPT', + severity: 'error', + highlight: { type: 'accept_state', state_id: acc }, + }) + } + } + + for (const tr of transitions) { + if (!states.has(tr.from)) { + errors.push({ + message: `Transition source state '${tr.from}' is not a valid state.`, + code: 'INVALID_TRANSITION_SOURCE', + severity: 'error', + highlight: { type: 'transition', from_state: tr.from, to_state: tr.to, symbol: tr.symbol }, + }) + } + if (!states.has(tr.to)) { + errors.push({ + message: `Transition destination state '${tr.to}' is not a valid state.`, + code: 'INVALID_TRANSITION_DEST', + severity: 'error', + highlight: { type: 'transition', from_state: tr.from, to_state: tr.to, symbol: tr.symbol }, + }) + } + if (!tr.symbol) { + errors.push({ + message: `A transition is missing its symbol.`, + code: 'INVALID_TRANSITION_SYMBOL', + severity: 'error', + highlight: { type: 'transition', from_state: tr.from, to_state: tr.to, symbol: null }, + }) + } else if (alphabet.size > 0 && !alphabet.has(tr.symbol)) { + errors.push({ + message: `Transition symbol '${tr.symbol}' is not in the alphabet.`, + code: 'INVALID_SYMBOL', + severity: 'error', + highlight: { type: 'transition', from_state: tr.from, to_state: tr.to, symbol: tr.symbol }, + }) + } + } + + const requireDeterministic = config.expected_type === 'DFA' + + const keyToTargets = new Map>() + let hasEpsilon = false + for (const tr of transitions) { + if (tr.symbol === config.epsilon_symbol) hasEpsilon = true + const key = `${tr.from}||${tr.symbol}` + const set = keyToTargets.get(key) ?? new Set() + set.add(tr.to) + keyToTargets.set(key, set) + } + + let isDeterministic = true + for (const [key, tos] of keyToTargets.entries()) { + if (tos.size > 1) { + isDeterministic = false + const [from, symbol] = key.split('||') + errors.push({ + message: `Non-determinism detected: multiple transitions from '${from}' on symbol '${symbol}'.`, + code: 'NOT_DETERMINISTIC', + severity: requireDeterministic ? 'error' : 'warning', + highlight: { type: 'state', state_id: from }, + }) + } + } + + if (hasEpsilon) { + isDeterministic = false + if (requireDeterministic) { + errors.push({ + message: `Epsilon transitions are not allowed in a DFA (epsilon = '${config.epsilon_symbol}').`, + code: 'NOT_DETERMINISTIC', + severity: 'error', + highlight: null, + }) + } + } + + // Reachability + const reachable = new Set() + if (answer.initial_state && states.has(answer.initial_state)) { + const stack = [answer.initial_state] + while (stack.length) { + const s = stack.pop()! + if (reachable.has(s)) continue + reachable.add(s) + for (const tr of transitions) { + if (tr.from === s && states.has(tr.to) && !reachable.has(tr.to)) { + stack.push(tr.to) + } + } + } + } + + const unreachableStates = answer.states.filter(s => !reachable.has(s)) + if (unreachableStates.length > 0) { + for (const s of unreachableStates) { + warnings.push({ + message: `State '${s}' is unreachable from the initial state.`, + code: 'UNREACHABLE_STATE', + severity: 'warning', + highlight: { type: 'state', state_id: s }, + }) + } + } + + // Dead states: cannot reach any accept state + const reverse = new Map>() + for (const tr of transitions) { + const set = reverse.get(tr.to) ?? new Set() + set.add(tr.from) + reverse.set(tr.to, set) + } + + const canReachAccept = new Set() + const queue: string[] = [] + for (const a of answer.accept_states) { + if (states.has(a)) { + canReachAccept.add(a) + queue.push(a) + } + } + while (queue.length) { + const cur = queue.shift()! + const prevs = reverse.get(cur) + if (!prevs) continue + for (const p of prevs) { + if (!canReachAccept.has(p)) { + canReachAccept.add(p) + queue.push(p) + } + } + } + + const deadStates = answer.states.filter(s => states.has(s) && !canReachAccept.has(s)) + if (deadStates.length > 0) { + for (const s of deadStates) { + warnings.push({ + message: `State '${s}' is a dead state (cannot reach an accept state).`, + code: 'DEAD_STATE', + severity: 'warning', + highlight: { type: 'state', state_id: s }, + }) + } + } + + // Completeness (only meaningful for DFA) + let isComplete = false + if (isDeterministic && alphabet.size > 0 && !alphabet.has(config.epsilon_symbol)) { + isComplete = true + for (const s of answer.states) { + for (const sym of alphabet) { + const key = `${s}||${sym}` + if (!keyToTargets.get(key) || (keyToTargets.get(key)?.size ?? 0) !== 1) { + isComplete = false + if (requireDeterministic) { + warnings.push({ + message: `Missing transition from '${s}' on symbol '${sym}'.`, + code: 'MISSING_TRANSITION', + severity: 'warning', + highlight: { type: 'state', state_id: s }, + }) + } + } + } + } + } + + const numStates = answer.states.length + const numTransitions = answer.transitions.length + const stateWord = numStates === 1 ? 'state' : 'states' + const transWord = numTransitions === 1 ? 'transition' : 'transitions' + const summary = `${isDeterministic ? 'DFA' : 'NFA'} with ${numStates} ${stateWord} and ${numTransitions} ${transWord}` + + return { + summary, + errors, + warnings, + structural: { + is_deterministic: isDeterministic, + is_complete: isComplete, + num_states: numStates, + num_transitions: numTransitions, + unreachable_states: unreachableStates, + dead_states: deadStates, + }, + test_results: [], + hints: [], + } +} + +function applyHighlight(cy: Core, err: ValidationError, className: string) { + const h = err.highlight + if (!h) return + + if ((h.type === 'state' || h.type === 'initial_state' || h.type === 'accept_state') && h.state_id) { + cy.getElementById(h.state_id).addClass(className) + return + } + + if (h.type === 'transition' && h.from_state && h.to_state) { + const candidates = cy.edges().filter(e => { + const fromOk = e.source().id() === h.from_state + const toOk = e.target().id() === h.to_state + const label = String(e.data('label') ?? '') + const symOk = h.symbol ? label === h.symbol : true + return fromOk && toOk && symOk + }) + candidates.addClass(className) + } +} diff --git a/src/types/FSA/components/ItemPropertiesPanel.tsx b/src/types/FSA/components/ItemPropertiesPanel.tsx index bb23291..0fefd1a 100644 --- a/src/types/FSA/components/ItemPropertiesPanel.tsx +++ b/src/types/FSA/components/ItemPropertiesPanel.tsx @@ -26,6 +26,8 @@ interface ItemPropertiesPanelProps { syncToBackend: () => void feedback: FSAFeedback | null + previewText?: string | null + previewIsLoading?: boolean } export default function ItemPropertiesPanel({ @@ -42,7 +44,9 @@ export default function ItemPropertiesPanel({ answer, handleChange, syncToBackend, - feedback + feedback, + previewText, + previewIsLoading, }: ItemPropertiesPanelProps): JSX.Element { return (
@@ -158,6 +162,26 @@ export default function ItemPropertiesPanel({ Delete Selected )} + {previewIsLoading ? ( +
Previewing…
+ ) : null} + {previewText ? ( +
+ {previewText} +
+ ) : null} diff --git a/src/types/FSA/index.tsx b/src/types/FSA/index.tsx index 7ec599f..b398f56 100644 --- a/src/types/FSA/index.tsx +++ b/src/types/FSA/index.tsx @@ -9,7 +9,14 @@ import { import { ResponseAreaTub } from '../response-area-tub' import { FSAInput } from './FSA.component' -import { fsaAnswerSchema, FSA, defaultFSA, DEFAULT_FSA_CONFIG, FSAConfig, FSAFeedback } from './type' +import { + DEFAULT_FSA_CONFIG, + FSA, + FSAConfig, + FSAFeedbackSchema, + defaultFSA, + fsaAnswerSchema, +} from './type' export class FSAResponseAreaTub extends ResponseAreaTub { public readonly responseType = 'FSA' @@ -36,36 +43,30 @@ export class FSAResponseAreaTub extends ResponseAreaTub { public InputComponent = (props: BaseResponseAreaProps): JSX.Element => { // Ensure a valid FSA answer - const validAnswer: FSA = this.answerSchema.safeParse(props.answer).success - ? this.answerSchema.safeParse(props.answer).data ?? defaultFSA - : defaultFSA + const parsedAnswer = this.answerSchema.safeParse(props.answer) + const validAnswer: FSA = parsedAnswer.success ? parsedAnswer.data : defaultFSA - // // Parse feedback safely and memoize so it updates when props.feedback changes - // const feedback: FSAFeedback | null = useMemo(() => { - // if (!props.feedback?.feedback) return null - // try { - // return JSON.parse(props.feedback.feedback) - // } catch { - // return null - // } - // }, [props.feedback?.feedback]) + const checkFeedback = (() => { + const raw = props.feedback?.feedback + if (!raw) return null + try { + // legacy format sometimes embeds JSON after a
+ const jsonPart = raw.split('
')[1]?.trim() ?? raw.trim() + const parsed = FSAFeedbackSchema.safeParse(JSON.parse(jsonPart)) + return parsed.success ? parsed.data : null + } catch { + return null + } + })() return ( { - const raw = props.feedback?.feedback - if (!raw) return {} - try { - // split by
and take the second part, trim whitespace - const jsonPart = raw.split('
')[1]?.trim() ?? '{}' - return JSON.parse(jsonPart) - } catch { - return {} // fallback to empty object if parsing fails - } - })()} answer={validAnswer} handleChange={(val: FSA) => props.handleChange(val)} + feedback={checkFeedback} + hasPreview={props.hasPreview} + responseAreaId={props.responseAreaId} + universalResponseAreaId={props.universalResponseAreaId} /> ) } @@ -77,12 +78,10 @@ public InputComponent = (props: BaseResponseAreaProps): JSX.Element => { ): JSX.Element => { return ( { this.answer = val - console.log('Wizard val:', val) props.handleChange({ responseType: this.responseType, answer: val, From 8b60ada913a42db49a490baa093841c05bdbf415 Mon Sep 17 00:00:00 2001 From: everythingfades Date: Fri, 30 Jan 2026 12:25:44 +0000 Subject: [PATCH 09/36] fix: style modification --- src/types/FSA/FSA.component.tsx | 129 ++++++++++++++++++++++++++++++-- src/types/FSA/index.tsx | 4 +- src/types/FSA/styles.ts | 1 + 3 files changed, 126 insertions(+), 8 deletions(-) diff --git a/src/types/FSA/FSA.component.tsx b/src/types/FSA/FSA.component.tsx index de58b19..4e744ad 100644 --- a/src/types/FSA/FSA.component.tsx +++ b/src/types/FSA/FSA.component.tsx @@ -16,7 +16,7 @@ interface FSAInputProps { export const FSAInput: React.FC = ({ answer, handleChange, - feedback + feedback, }) => { const { classes } = useLocalStyles() @@ -38,6 +38,7 @@ export const FSAInput: React.FC = ({ container: containerRef.current, layout: { name: 'preset' }, style: [ + // ---------------- Nodes ---------------- { selector: 'node', style: { @@ -51,6 +52,33 @@ export const FSAInput: React.FC = ({ 'border-color': '#555', }, }, + + { + selector: 'node.initial', + style: { + 'border-width': 3, + 'border-color': '#1976d2', + }, + }, + + { + selector: 'node.accept', + style: { + 'border-style': 'double', + 'border-width': 4, + }, + }, + + { + selector: 'node.error-highlight', + style: { + 'background-color': '#ffebee', + 'border-color': '#d32f2f', + 'border-width': 4, + }, + }, + + // ---------------- Edges ---------------- { selector: 'edge', style: { @@ -64,6 +92,16 @@ export const FSAInput: React.FC = ({ 'text-background-padding': '3px', }, }, + + { + selector: 'edge.error-highlight', + style: { + 'line-color': '#d32f2f', + 'target-arrow-color': '#d32f2f', + 'line-style': 'dashed', + width: 3, + }, + }, ], }) @@ -78,6 +116,7 @@ export const FSAInput: React.FC = ({ const tapNode = (e: cytoscape.EventObject): void => { const node = e.target as NodeSingular + if (drawMode) { if (!fromNode) { setFromNode(node.id()) @@ -124,17 +163,19 @@ export const FSAInput: React.FC = ({ if (!cy) return const fsa: FSA = { - states: cy.nodes().map((n) => n.id()), - transitions: cy.edges().map( + states: cy.nodes()?.map((n) => n.id()), + transitions: cy.edges()?.map( (e) => `${e.source().id()}|${e.data('label') || config.epsilon_symbol}|${e.target().id()}`, ), initial_state: answer.initial_state, accept_states: answer.accept_states, - alphabet: Array.from(new Set(cy.edges().map((e) => String(e.data('label'))))), + alphabet: Array.from( + new Set(cy.edges().map((e) => String(e.data('label')))), + ), } - handleChange(fsa) // Only FSA, not config + handleChange(fsa) } /* -------------------- add state -------------------- */ @@ -146,12 +187,88 @@ export const FSAInput: React.FC = ({ cy.add({ group: 'nodes', data: { id, displayLabel: id }, - position: { x: 100 + Math.random() * 300, y: 100 + Math.random() * 300 }, + position: { + x: 100 + Math.random() * 300, + y: 100 + Math.random() * 300, + }, }) syncToBackend() } + /* -------------------- apply initial / accept styling -------------------- */ + useEffect(() => { + const cy = cyRef.current + if (!cy) return + + cy.nodes().removeClass('initial accept') + + if (answer.initial_state) { + cy.$id(answer.initial_state).addClass('initial') + } + + for (const id of answer.accept_states) { + cy.$id(id).addClass('accept') + } + }, [answer.initial_state, answer.accept_states]) + + /* -------------------- apply feedback highlights -------------------- */ + useEffect(() => { + const cy = cyRef.current + if (!cy) return + + cy.nodes().removeClass('error-highlight') + cy.edges().removeClass('error-highlight') + + if (!feedback || !feedback.errors) return + + const highlights = feedback.errors + .map((e) => e.highlight) + .filter(Boolean) + + for (const h of highlights) { + if (!h) continue + + switch (h.type) { + case 'state': + case 'initial_state': + case 'accept_state': { + if (h.state_id) { + cy.$id(h.state_id).addClass('error-highlight') + } + break + } + + case 'transition': { + cy.edges() + .filter((e) => { + const fromOk = h.from_state + ? e.source().id() === h.from_state + : true + const toOk = h.to_state + ? e.target().id() === h.to_state + : true + const symOk = h.symbol + ? e.data('label') === h.symbol + : true + return fromOk && toOk && symOk + }) + .addClass('error-highlight') + break + } + + case 'alphabet_symbol': { + if (h.symbol) { + cy.edges() + .filter((e) => e.data('label') === h.symbol) + .addClass('error-highlight') + } + break + } + } + } + }, [feedback]) + return (
{ this.config = { @@ -30,7 +30,7 @@ export class FSAResponseAreaTub extends ResponseAreaTub { } } - customCheck = () => {} // will set this up later + // customCheck = () => {} // will set this up later /* -------------------- Input -------------------- */ diff --git a/src/types/FSA/styles.ts b/src/types/FSA/styles.ts index 3ae419f..8ed068e 100644 --- a/src/types/FSA/styles.ts +++ b/src/types/FSA/styles.ts @@ -18,6 +18,7 @@ export const useLocalStyles = makeStyles()((theme) => ({ display: 'flex', flexDirection: 'column', gap: theme.spacing(2), + overflowY: 'auto' }, floatingConfig: { From c88db1b971c0eff49a95e7ca47b31a747f70dd65 Mon Sep 17 00:00:00 2001 From: everythingfades Date: Fri, 30 Jan 2026 18:55:45 +0000 Subject: [PATCH 10/36] revert: revert back to before preview --- .gitignore | 2 +- externals/api/fetcher.ts | 61 +- src/types/FSA/FSA.component.tsx | 589 +----------------- .../FSA/components/ItemPropertiesPanel.tsx | 26 +- src/types/FSA/index.tsx | 59 +- src/types/FSA/styles.ts | 1 - 6 files changed, 45 insertions(+), 693 deletions(-) diff --git a/.gitignore b/.gitignore index a0919bd..1b68e4d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ # dependencies /node_modules -/client-web-main + # production /build /dist diff --git a/externals/api/fetcher.ts b/externals/api/fetcher.ts index b0f1f8f..4fa1f95 100644 --- a/externals/api/fetcher.ts +++ b/externals/api/fetcher.ts @@ -1,62 +1,7 @@ -type GraphqlResponse = { - data: TData | null - errors?: Array<{ message?: string }> | null -} - -function getGraphqlUrl(): string { - // 1) Explicit override (handy in sandbox contexts) - const override = (globalThis as any).__LF_GRAPHQL_URL - if (typeof override === 'string' && override.length > 0) return override - - // 2) Common paths (works when frontend and API share origin/proxy) - // Prefer /graphql (client-web-main default), but also try /api/graphql (common convention) - return new URL('/graphql', globalThis.location?.origin ?? 'http://localhost') - .toString() -} - export const fetchData = ( - query: string, - variables?: TVariables, + _query: string, + _variables?: TVariables, _options?: unknown, ): (() => Promise) => { - return async () => { - const url = getGraphqlUrl() - - const res = await fetch(url, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - // In the real app, auth is usually cookie-based or handled by the host page. - // Include credentials so the sandbox can reuse the host session. - credentials: 'include', - body: JSON.stringify({ - query, - variables, - }), - }) - - if (!res.ok) { - throw new Error( - `GraphQL request failed (${res.status} ${res.statusText})`, - ) - } - - const json = (await res.json()) as GraphqlResponse - - if (json.errors && json.errors.length > 0) { - const msg = - json.errors - .map(e => e?.message) - .filter(Boolean) - .join('\n') || 'GraphQL error' - throw new Error(msg) - } - - if (json.data === null) { - throw new Error('GraphQL: no data returned') - } - - return json.data - } + return null as any } diff --git a/src/types/FSA/FSA.component.tsx b/src/types/FSA/FSA.component.tsx index de76e14..de58b19 100644 --- a/src/types/FSA/FSA.component.tsx +++ b/src/types/FSA/FSA.component.tsx @@ -1,36 +1,22 @@ -import { useSubmitResponsePreviewMutation } from '@api/graphql' +import { makeStyles } from '@styles' import cytoscape, { Core, NodeSingular, EdgeSingular } from 'cytoscape' -import React, { useEffect, useMemo, useRef, useState } from 'react' +import React, { useEffect, useRef, useState } from 'react' import ConfigPanel from './components/ConfigPanel' import ItemPropertiesPanel from './components/ItemPropertiesPanel' import { useLocalStyles } from './styles' -import { - DEFAULT_FSA_CONFIG, - FSA, - FSAConfig, - FSAFeedback, - FSAFeedbackSchema, - ValidationError, - ValidationErrorSchema, -} from './type' +import { DEFAULT_FSA_CONFIG, FSA, FSAConfig, FSAFeedback } from './type' interface FSAInputProps { answer: FSA handleChange: (fsa: FSA) => void feedback: FSAFeedback | null - hasPreview?: boolean - responseAreaId?: string - universalResponseAreaId?: string } export const FSAInput: React.FC = ({ answer, handleChange, - feedback, - hasPreview, - responseAreaId, - universalResponseAreaId, + feedback }) => { const { classes } = useLocalStyles() @@ -44,90 +30,6 @@ export const FSAInput: React.FC = ({ const [config, setConfig] = useState(DEFAULT_FSA_CONFIG) const [configOpen, setConfigOpen] = useState(true) - const [previewIsLoading, setPreviewIsLoading] = useState(false) - const [previewText, setPreviewText] = useState(null) - const [previewFeedback, setPreviewFeedback] = useState(null) - - type PreviewSympy = Record - - const localPreview = useMemo(() => { - // Always compute a fast local preview as a fallback. - return validateFsaPreview(answer, config) - }, [answer, config]) - - /* -------------------- debounce preview call -------------------- */ - useEffect(() => { - // If live preview is off, show normal (check) feedback if any. - if (!hasPreview) { - setPreviewIsLoading(false) - setPreviewText(null) - setPreviewFeedback(feedback) - return - } - - // If we can't call the backend preview, fall back to local validation. - if (!responseAreaId || !universalResponseAreaId) { - setPreviewIsLoading(false) - setPreviewText(null) - setPreviewFeedback(localPreview) - return - } - - const requireDeterministic = config.expected_type === 'DFA' - const showWarnings = true - - const timeout = setTimeout(async () => { - setPreviewIsLoading(true) - try { - const data = await useSubmitResponsePreviewMutation.fetcher({ - submission: answer, - additionalParams: { - require_deterministic: requireDeterministic, - show_warnings: showWarnings, - }, - responseAreaId, - universalResponseAreaId, - })() - - const res = data.submitResponsePreview - - // Best-effort mapping: the backend returns { preview: { latex, feedback, sympy } } - const previewObj = res.preview - if (previewObj && typeof previewObj === 'object') { - const maybeFeedback = (previewObj as any).feedback - const maybeSympy = (previewObj as any).sympy as PreviewSympy | undefined - - setPreviewText(typeof maybeFeedback === 'string' ? maybeFeedback : null) - setPreviewFeedback( - maybeSympy ? sympyToFsaFeedback(maybeSympy, answer) : localPreview, - ) - } else { - // No structured preview; show server string feedback if present - setPreviewText(res.feedback ?? null) - setPreviewFeedback(localPreview) - } - } catch { - // If network preview fails (common in sandbox contexts), still give the student useful local feedback. - setPreviewText(null) - setPreviewFeedback(localPreview) - } finally { - setPreviewIsLoading(false) - } - }, 500) - - return () => { - clearTimeout(timeout) - } - }, [ - answer, - config.expected_type, - feedback, - hasPreview, - localPreview, - responseAreaId, - universalResponseAreaId, - ]) - /* -------------------- init cytoscape -------------------- */ useEffect(() => { if (!containerRef.current) return @@ -136,7 +38,6 @@ export const FSAInput: React.FC = ({ container: containerRef.current, layout: { name: 'preset' }, style: [ - // ---------------- Nodes ---------------- { selector: 'node', style: { @@ -150,33 +51,6 @@ export const FSAInput: React.FC = ({ 'border-color': '#555', }, }, - - { - selector: 'node.initial', - style: { - 'border-width': 3, - 'border-color': '#1976d2', - }, - }, - - { - selector: 'node.accept', - style: { - 'border-style': 'double', - 'border-width': 4, - }, - }, - - { - selector: 'node.error-highlight', - style: { - 'background-color': '#ffebee', - 'border-color': '#d32f2f', - 'border-width': 4, - }, - }, - - // ---------------- Edges ---------------- { selector: 'edge', style: { @@ -190,37 +64,6 @@ export const FSAInput: React.FC = ({ 'text-background-padding': '3px', }, }, - { - selector: '.preview-error', - style: { - 'border-width': 4, - 'border-color': '#d32f2f', - 'line-color': '#d32f2f', - 'target-arrow-color': '#d32f2f', - 'background-color': '#ffebee', - width: 3, - }, - }, - { - selector: '.preview-warning', - style: { - 'border-width': 4, - 'border-color': '#ed6c02', - 'line-color': '#ed6c02', - 'target-arrow-color': '#ed6c02', - 'background-color': '#fff3e0', - }, - }, - - { - selector: 'edge.error-highlight', - style: { - 'line-color': '#d32f2f', - 'target-arrow-color': '#d32f2f', - 'line-style': 'dashed', - width: 3, - }, - }, ], }) @@ -228,24 +71,6 @@ export const FSAInput: React.FC = ({ return () => cy.destroy() }, []) - /* -------------------- apply preview highlights -------------------- */ - useEffect(() => { - const cy = cyRef.current - if (!cy) return - - cy.elements().removeClass('preview-error preview-warning') - - const fb = previewFeedback - if (!fb) return - - for (const err of fb.errors) { - applyHighlight(cy, err, 'preview-error') - } - for (const warn of fb.warnings) { - applyHighlight(cy, warn, 'preview-warning') - } - }, [previewFeedback]) - /* -------------------- node/edge handlers -------------------- */ useEffect(() => { const cy = cyRef.current @@ -253,7 +78,6 @@ export const FSAInput: React.FC = ({ const tapNode = (e: cytoscape.EventObject): void => { const node = e.target as NodeSingular - if (drawMode) { if (!fromNode) { setFromNode(node.id()) @@ -300,19 +124,17 @@ export const FSAInput: React.FC = ({ if (!cy) return const fsa: FSA = { - states: cy.nodes()?.map((n) => n.id()), - transitions: cy.edges()?.map( + states: cy.nodes().map((n) => n.id()), + transitions: cy.edges().map( (e) => `${e.source().id()}|${e.data('label') || config.epsilon_symbol}|${e.target().id()}`, ), initial_state: answer.initial_state, accept_states: answer.accept_states, - alphabet: Array.from( - new Set(cy.edges().map((e) => String(e.data('label')))), - ), + alphabet: Array.from(new Set(cy.edges().map((e) => String(e.data('label'))))), } - handleChange(fsa) + handleChange(fsa) // Only FSA, not config } /* -------------------- add state -------------------- */ @@ -324,88 +146,12 @@ export const FSAInput: React.FC = ({ cy.add({ group: 'nodes', data: { id, displayLabel: id }, - position: { - x: 100 + Math.random() * 300, - y: 100 + Math.random() * 300, - }, + position: { x: 100 + Math.random() * 300, y: 100 + Math.random() * 300 }, }) syncToBackend() } - /* -------------------- apply initial / accept styling -------------------- */ - useEffect(() => { - const cy = cyRef.current - if (!cy) return - - cy.nodes().removeClass('initial accept') - - if (answer.initial_state) { - cy.$id(answer.initial_state).addClass('initial') - } - - for (const id of answer.accept_states) { - cy.$id(id).addClass('accept') - } - }, [answer.initial_state, answer.accept_states]) - - /* -------------------- apply feedback highlights -------------------- */ - useEffect(() => { - const cy = cyRef.current - if (!cy) return - - cy.nodes().removeClass('error-highlight') - cy.edges().removeClass('error-highlight') - - if (!feedback || !feedback.errors) return - - const highlights = feedback.errors - .map((e) => e.highlight) - .filter(Boolean) - - for (const h of highlights) { - if (!h) continue - - switch (h.type) { - case 'state': - case 'initial_state': - case 'accept_state': { - if (h.state_id) { - cy.$id(h.state_id).addClass('error-highlight') - } - break - } - - case 'transition': { - cy.edges() - .filter((e) => { - const fromOk = h.from_state - ? e.source().id() === h.from_state - : true - const toOk = h.to_state - ? e.target().id() === h.to_state - : true - const symOk = h.symbol - ? e.data('label') === h.symbol - : true - return fromOk && toOk && symOk - }) - .addClass('error-highlight') - break - } - - case 'alphabet_symbol': { - if (h.symbol) { - cy.edges() - .filter((e) => e.data('label') === h.symbol) - .addClass('error-highlight') - } - break - } - } - } - }, [feedback]) - return (
= ({ syncToBackend={syncToBackend} handleChange={handleChange} answer={answer} - feedback={previewFeedback} - previewText={previewText} - previewIsLoading={previewIsLoading} + feedback={feedback} />
@@ -439,316 +183,3 @@ export const FSAInput: React.FC = ({
) } - -function parseTransition(t: string): { from: string; symbol: string; to: string } { - const [from = '', symbol = '', to = ''] = t.split('|') - return { from, symbol, to } -} - -function sympyToFsaFeedback(sympy: Record, answer: FSA): FSAFeedback { - const errorsRaw = Array.isArray(sympy.errors) ? sympy.errors : [] - const warningsRaw = Array.isArray(sympy.warnings) ? sympy.warnings : [] - - const errors: ValidationError[] = [] - const warnings: ValidationError[] = [] - - for (const e of errorsRaw) { - const parsed = ValidationErrorSchema.safeParse(e) - if (parsed.success) errors.push(parsed.data) - } - for (const w of warningsRaw) { - const parsed = ValidationErrorSchema.safeParse(w) - if (parsed.success) warnings.push(parsed.data) - } - - const numStates = readNumber(sympy.num_states, answer.states.length) - const numTransitions = readNumber(sympy.num_transitions, answer.transitions.length) - - const isDet = readBoolean(sympy.is_deterministic, false) - const isComplete = readBoolean(sympy.is_complete, false) - const unreachable = readStringArray(sympy.unreachable_states) - const dead = readStringArray(sympy.dead_states) - - const stateWord = numStates === 1 ? 'state' : 'states' - const transWord = numTransitions === 1 ? 'transition' : 'transitions' - const summary = `${isDet ? 'DFA' : 'NFA'} with ${numStates} ${stateWord} and ${numTransitions} ${transWord}` - - const candidate: FSAFeedback = { - summary, - errors, - warnings, - structural: { - is_deterministic: isDet, - is_complete: isComplete, - num_states: numStates, - num_transitions: numTransitions, - unreachable_states: unreachable, - dead_states: dead, - }, - test_results: [], - hints: [], - } - - const parsed = FSAFeedbackSchema.safeParse(candidate) - return parsed.success ? parsed.data : candidate -} - -function readBoolean(v: unknown, fallback: boolean): boolean { - return typeof v === 'boolean' ? v : fallback -} - -function readNumber(v: unknown, fallback: number): number { - return typeof v === 'number' && Number.isFinite(v) ? v : fallback -} - -function readStringArray(v: unknown): string[] { - if (!Array.isArray(v)) return [] - return v.filter(x => typeof x === 'string') as string[] -} - -function validateFsaPreview(answer: FSA, config: FSAConfig): FSAFeedback { - const errors: ValidationError[] = [] - const warnings: ValidationError[] = [] - - const states = new Set(answer.states) - const alphabet = new Set(answer.alphabet) - - const transitions = answer.transitions.map(parseTransition) - - // Structural errors - if (answer.states.length === 0) { - errors.push({ - message: 'No states defined.', - code: 'EMPTY_STATES', - severity: 'error', - highlight: null, - }) - } - - if (!answer.initial_state || !states.has(answer.initial_state)) { - errors.push({ - message: 'Initial state is missing or not in the set of states.', - code: 'INVALID_INITIAL', - severity: 'error', - highlight: answer.initial_state - ? { type: 'initial_state', state_id: answer.initial_state } - : { type: 'initial_state', state_id: null }, - }) - } - - for (const acc of answer.accept_states) { - if (!states.has(acc)) { - errors.push({ - message: `Accept state '${acc}' is not in the set of states.`, - code: 'INVALID_ACCEPT', - severity: 'error', - highlight: { type: 'accept_state', state_id: acc }, - }) - } - } - - for (const tr of transitions) { - if (!states.has(tr.from)) { - errors.push({ - message: `Transition source state '${tr.from}' is not a valid state.`, - code: 'INVALID_TRANSITION_SOURCE', - severity: 'error', - highlight: { type: 'transition', from_state: tr.from, to_state: tr.to, symbol: tr.symbol }, - }) - } - if (!states.has(tr.to)) { - errors.push({ - message: `Transition destination state '${tr.to}' is not a valid state.`, - code: 'INVALID_TRANSITION_DEST', - severity: 'error', - highlight: { type: 'transition', from_state: tr.from, to_state: tr.to, symbol: tr.symbol }, - }) - } - if (!tr.symbol) { - errors.push({ - message: `A transition is missing its symbol.`, - code: 'INVALID_TRANSITION_SYMBOL', - severity: 'error', - highlight: { type: 'transition', from_state: tr.from, to_state: tr.to, symbol: null }, - }) - } else if (alphabet.size > 0 && !alphabet.has(tr.symbol)) { - errors.push({ - message: `Transition symbol '${tr.symbol}' is not in the alphabet.`, - code: 'INVALID_SYMBOL', - severity: 'error', - highlight: { type: 'transition', from_state: tr.from, to_state: tr.to, symbol: tr.symbol }, - }) - } - } - - const requireDeterministic = config.expected_type === 'DFA' - - const keyToTargets = new Map>() - let hasEpsilon = false - for (const tr of transitions) { - if (tr.symbol === config.epsilon_symbol) hasEpsilon = true - const key = `${tr.from}||${tr.symbol}` - const set = keyToTargets.get(key) ?? new Set() - set.add(tr.to) - keyToTargets.set(key, set) - } - - let isDeterministic = true - for (const [key, tos] of keyToTargets.entries()) { - if (tos.size > 1) { - isDeterministic = false - const [from, symbol] = key.split('||') - errors.push({ - message: `Non-determinism detected: multiple transitions from '${from}' on symbol '${symbol}'.`, - code: 'NOT_DETERMINISTIC', - severity: requireDeterministic ? 'error' : 'warning', - highlight: { type: 'state', state_id: from }, - }) - } - } - - if (hasEpsilon) { - isDeterministic = false - if (requireDeterministic) { - errors.push({ - message: `Epsilon transitions are not allowed in a DFA (epsilon = '${config.epsilon_symbol}').`, - code: 'NOT_DETERMINISTIC', - severity: 'error', - highlight: null, - }) - } - } - - // Reachability - const reachable = new Set() - if (answer.initial_state && states.has(answer.initial_state)) { - const stack = [answer.initial_state] - while (stack.length) { - const s = stack.pop()! - if (reachable.has(s)) continue - reachable.add(s) - for (const tr of transitions) { - if (tr.from === s && states.has(tr.to) && !reachable.has(tr.to)) { - stack.push(tr.to) - } - } - } - } - - const unreachableStates = answer.states.filter(s => !reachable.has(s)) - if (unreachableStates.length > 0) { - for (const s of unreachableStates) { - warnings.push({ - message: `State '${s}' is unreachable from the initial state.`, - code: 'UNREACHABLE_STATE', - severity: 'warning', - highlight: { type: 'state', state_id: s }, - }) - } - } - - // Dead states: cannot reach any accept state - const reverse = new Map>() - for (const tr of transitions) { - const set = reverse.get(tr.to) ?? new Set() - set.add(tr.from) - reverse.set(tr.to, set) - } - - const canReachAccept = new Set() - const queue: string[] = [] - for (const a of answer.accept_states) { - if (states.has(a)) { - canReachAccept.add(a) - queue.push(a) - } - } - while (queue.length) { - const cur = queue.shift()! - const prevs = reverse.get(cur) - if (!prevs) continue - for (const p of prevs) { - if (!canReachAccept.has(p)) { - canReachAccept.add(p) - queue.push(p) - } - } - } - - const deadStates = answer.states.filter(s => states.has(s) && !canReachAccept.has(s)) - if (deadStates.length > 0) { - for (const s of deadStates) { - warnings.push({ - message: `State '${s}' is a dead state (cannot reach an accept state).`, - code: 'DEAD_STATE', - severity: 'warning', - highlight: { type: 'state', state_id: s }, - }) - } - } - - // Completeness (only meaningful for DFA) - let isComplete = false - if (isDeterministic && alphabet.size > 0 && !alphabet.has(config.epsilon_symbol)) { - isComplete = true - for (const s of answer.states) { - for (const sym of alphabet) { - const key = `${s}||${sym}` - if (!keyToTargets.get(key) || (keyToTargets.get(key)?.size ?? 0) !== 1) { - isComplete = false - if (requireDeterministic) { - warnings.push({ - message: `Missing transition from '${s}' on symbol '${sym}'.`, - code: 'MISSING_TRANSITION', - severity: 'warning', - highlight: { type: 'state', state_id: s }, - }) - } - } - } - } - } - - const numStates = answer.states.length - const numTransitions = answer.transitions.length - const stateWord = numStates === 1 ? 'state' : 'states' - const transWord = numTransitions === 1 ? 'transition' : 'transitions' - const summary = `${isDeterministic ? 'DFA' : 'NFA'} with ${numStates} ${stateWord} and ${numTransitions} ${transWord}` - - return { - summary, - errors, - warnings, - structural: { - is_deterministic: isDeterministic, - is_complete: isComplete, - num_states: numStates, - num_transitions: numTransitions, - unreachable_states: unreachableStates, - dead_states: deadStates, - }, - test_results: [], - hints: [], - } -} - -function applyHighlight(cy: Core, err: ValidationError, className: string) { - const h = err.highlight - if (!h) return - - if ((h.type === 'state' || h.type === 'initial_state' || h.type === 'accept_state') && h.state_id) { - cy.getElementById(h.state_id).addClass(className) - return - } - - if (h.type === 'transition' && h.from_state && h.to_state) { - const candidates = cy.edges().filter(e => { - const fromOk = e.source().id() === h.from_state - const toOk = e.target().id() === h.to_state - const label = String(e.data('label') ?? '') - const symOk = h.symbol ? label === h.symbol : true - return fromOk && toOk && symOk - }) - candidates.addClass(className) - } -} diff --git a/src/types/FSA/components/ItemPropertiesPanel.tsx b/src/types/FSA/components/ItemPropertiesPanel.tsx index 0fefd1a..bb23291 100644 --- a/src/types/FSA/components/ItemPropertiesPanel.tsx +++ b/src/types/FSA/components/ItemPropertiesPanel.tsx @@ -26,8 +26,6 @@ interface ItemPropertiesPanelProps { syncToBackend: () => void feedback: FSAFeedback | null - previewText?: string | null - previewIsLoading?: boolean } export default function ItemPropertiesPanel({ @@ -44,9 +42,7 @@ export default function ItemPropertiesPanel({ answer, handleChange, syncToBackend, - feedback, - previewText, - previewIsLoading, + feedback }: ItemPropertiesPanelProps): JSX.Element { return (
@@ -162,26 +158,6 @@ export default function ItemPropertiesPanel({ Delete Selected )} - {previewIsLoading ? ( -
Previewing…
- ) : null} - {previewText ? ( -
- {previewText} -
- ) : null} diff --git a/src/types/FSA/index.tsx b/src/types/FSA/index.tsx index 05f3102..7ec599f 100644 --- a/src/types/FSA/index.tsx +++ b/src/types/FSA/index.tsx @@ -9,14 +9,7 @@ import { import { ResponseAreaTub } from '../response-area-tub' import { FSAInput } from './FSA.component' -import { - DEFAULT_FSA_CONFIG, - FSA, - FSAConfig, - FSAFeedbackSchema, - defaultFSA, - fsaAnswerSchema, -} from './type' +import { fsaAnswerSchema, FSA, defaultFSA, DEFAULT_FSA_CONFIG, FSAConfig, FSAFeedback } from './type' export class FSAResponseAreaTub extends ResponseAreaTub { public readonly responseType = 'FSA' @@ -28,7 +21,7 @@ export class FSAResponseAreaTub extends ResponseAreaTub { // private feedback: FSAFeedback | null = null public readonly delegateFeedback = false // we want to manage our own feedback - public readonly delegateLivePreview = true// we want live previews + public readonly delegateLivePreview = false // we want live previews initWithConfig = (config: any) => { this.config = { @@ -37,36 +30,42 @@ export class FSAResponseAreaTub extends ResponseAreaTub { } } - // customCheck = () => {} // will set this up later + customCheck = () => {} // will set this up later /* -------------------- Input -------------------- */ public InputComponent = (props: BaseResponseAreaProps): JSX.Element => { // Ensure a valid FSA answer - const parsedAnswer = this.answerSchema.safeParse(props.answer) - const validAnswer: FSA = parsedAnswer.success ? parsedAnswer.data : defaultFSA + const validAnswer: FSA = this.answerSchema.safeParse(props.answer).success + ? this.answerSchema.safeParse(props.answer).data ?? defaultFSA + : defaultFSA - const checkFeedback = (() => { - const raw = props.feedback?.feedback - if (!raw) return null - try { - // legacy format sometimes embeds JSON after a
- const jsonPart = raw.split('
')[1]?.trim() ?? raw.trim() - const parsed = FSAFeedbackSchema.safeParse(JSON.parse(jsonPart)) - return parsed.success ? parsed.data : null - } catch { - return null - } - })() + // // Parse feedback safely and memoize so it updates when props.feedback changes + // const feedback: FSAFeedback | null = useMemo(() => { + // if (!props.feedback?.feedback) return null + // try { + // return JSON.parse(props.feedback.feedback) + // } catch { + // return null + // } + // }, [props.feedback?.feedback]) return ( { + const raw = props.feedback?.feedback + if (!raw) return {} + try { + // split by
and take the second part, trim whitespace + const jsonPart = raw.split('
')[1]?.trim() ?? '{}' + return JSON.parse(jsonPart) + } catch { + return {} // fallback to empty object if parsing fails + } + })()} answer={validAnswer} handleChange={(val: FSA) => props.handleChange(val)} - feedback={checkFeedback} - hasPreview={props.hasPreview} - responseAreaId={props.responseAreaId} - universalResponseAreaId={props.universalResponseAreaId} /> ) } @@ -78,10 +77,12 @@ public InputComponent = (props: BaseResponseAreaProps): JSX.Element => { ): JSX.Element => { return ( { this.answer = val + console.log('Wizard val:', val) props.handleChange({ responseType: this.responseType, answer: val, diff --git a/src/types/FSA/styles.ts b/src/types/FSA/styles.ts index 8ed068e..3ae419f 100644 --- a/src/types/FSA/styles.ts +++ b/src/types/FSA/styles.ts @@ -18,7 +18,6 @@ export const useLocalStyles = makeStyles()((theme) => ({ display: 'flex', flexDirection: 'column', gap: theme.spacing(2), - overflowY: 'auto' }, floatingConfig: { From 1370a0dea641407593b708eb9685f56b36976572 Mon Sep 17 00:00:00 2001 From: everythingfades Date: Fri, 30 Jan 2026 19:04:19 +0000 Subject: [PATCH 11/36] fix: styles forgetten by the revert --- src/types/FSA/FSA.component.tsx | 129 ++++++++++++++++++++++++++++++-- src/types/FSA/index.tsx | 4 +- src/types/FSA/styles.ts | 1 + 3 files changed, 126 insertions(+), 8 deletions(-) diff --git a/src/types/FSA/FSA.component.tsx b/src/types/FSA/FSA.component.tsx index de58b19..4e744ad 100644 --- a/src/types/FSA/FSA.component.tsx +++ b/src/types/FSA/FSA.component.tsx @@ -16,7 +16,7 @@ interface FSAInputProps { export const FSAInput: React.FC = ({ answer, handleChange, - feedback + feedback, }) => { const { classes } = useLocalStyles() @@ -38,6 +38,7 @@ export const FSAInput: React.FC = ({ container: containerRef.current, layout: { name: 'preset' }, style: [ + // ---------------- Nodes ---------------- { selector: 'node', style: { @@ -51,6 +52,33 @@ export const FSAInput: React.FC = ({ 'border-color': '#555', }, }, + + { + selector: 'node.initial', + style: { + 'border-width': 3, + 'border-color': '#1976d2', + }, + }, + + { + selector: 'node.accept', + style: { + 'border-style': 'double', + 'border-width': 4, + }, + }, + + { + selector: 'node.error-highlight', + style: { + 'background-color': '#ffebee', + 'border-color': '#d32f2f', + 'border-width': 4, + }, + }, + + // ---------------- Edges ---------------- { selector: 'edge', style: { @@ -64,6 +92,16 @@ export const FSAInput: React.FC = ({ 'text-background-padding': '3px', }, }, + + { + selector: 'edge.error-highlight', + style: { + 'line-color': '#d32f2f', + 'target-arrow-color': '#d32f2f', + 'line-style': 'dashed', + width: 3, + }, + }, ], }) @@ -78,6 +116,7 @@ export const FSAInput: React.FC = ({ const tapNode = (e: cytoscape.EventObject): void => { const node = e.target as NodeSingular + if (drawMode) { if (!fromNode) { setFromNode(node.id()) @@ -124,17 +163,19 @@ export const FSAInput: React.FC = ({ if (!cy) return const fsa: FSA = { - states: cy.nodes().map((n) => n.id()), - transitions: cy.edges().map( + states: cy.nodes()?.map((n) => n.id()), + transitions: cy.edges()?.map( (e) => `${e.source().id()}|${e.data('label') || config.epsilon_symbol}|${e.target().id()}`, ), initial_state: answer.initial_state, accept_states: answer.accept_states, - alphabet: Array.from(new Set(cy.edges().map((e) => String(e.data('label'))))), + alphabet: Array.from( + new Set(cy.edges().map((e) => String(e.data('label')))), + ), } - handleChange(fsa) // Only FSA, not config + handleChange(fsa) } /* -------------------- add state -------------------- */ @@ -146,12 +187,88 @@ export const FSAInput: React.FC = ({ cy.add({ group: 'nodes', data: { id, displayLabel: id }, - position: { x: 100 + Math.random() * 300, y: 100 + Math.random() * 300 }, + position: { + x: 100 + Math.random() * 300, + y: 100 + Math.random() * 300, + }, }) syncToBackend() } + /* -------------------- apply initial / accept styling -------------------- */ + useEffect(() => { + const cy = cyRef.current + if (!cy) return + + cy.nodes().removeClass('initial accept') + + if (answer.initial_state) { + cy.$id(answer.initial_state).addClass('initial') + } + + for (const id of answer.accept_states) { + cy.$id(id).addClass('accept') + } + }, [answer.initial_state, answer.accept_states]) + + /* -------------------- apply feedback highlights -------------------- */ + useEffect(() => { + const cy = cyRef.current + if (!cy) return + + cy.nodes().removeClass('error-highlight') + cy.edges().removeClass('error-highlight') + + if (!feedback || !feedback.errors) return + + const highlights = feedback.errors + .map((e) => e.highlight) + .filter(Boolean) + + for (const h of highlights) { + if (!h) continue + + switch (h.type) { + case 'state': + case 'initial_state': + case 'accept_state': { + if (h.state_id) { + cy.$id(h.state_id).addClass('error-highlight') + } + break + } + + case 'transition': { + cy.edges() + .filter((e) => { + const fromOk = h.from_state + ? e.source().id() === h.from_state + : true + const toOk = h.to_state + ? e.target().id() === h.to_state + : true + const symOk = h.symbol + ? e.data('label') === h.symbol + : true + return fromOk && toOk && symOk + }) + .addClass('error-highlight') + break + } + + case 'alphabet_symbol': { + if (h.symbol) { + cy.edges() + .filter((e) => e.data('label') === h.symbol) + .addClass('error-highlight') + } + break + } + } + } + }, [feedback]) + return (
{ this.config = { @@ -30,7 +30,7 @@ export class FSAResponseAreaTub extends ResponseAreaTub { } } - customCheck = () => {} // will set this up later + // customCheck = () => {} // will set this up later /* -------------------- Input -------------------- */ diff --git a/src/types/FSA/styles.ts b/src/types/FSA/styles.ts index 3ae419f..8ed068e 100644 --- a/src/types/FSA/styles.ts +++ b/src/types/FSA/styles.ts @@ -18,6 +18,7 @@ export const useLocalStyles = makeStyles()((theme) => ({ display: 'flex', flexDirection: 'column', gap: theme.spacing(2), + overflowY: 'auto' }, floatingConfig: { From 04a1e4dcd56716adf3d54aabeec156a72fe7c772 Mon Sep 17 00:00:00 2001 From: everythingfades Date: Sun, 1 Feb 2026 02:43:20 +0000 Subject: [PATCH 12/36] feat: should be everything --- src/types/FSA/FSA.backend.ts | 54 --- src/types/FSA/FSA.component.tsx | 19 +- src/types/FSA/components/ConfigPanel.tsx | 32 +- src/types/FSA/components/FSAFeedbackPanel.tsx | 55 ++- .../FSA/components/ItemPropertiesPanel.tsx | 10 +- src/types/FSA/index.tsx | 136 ++++--- src/types/FSA/type.ts | 27 +- src/types/FSA/utils.ts | 13 + src/types/FSA/validateFSA.ts | 333 ++++++++++++++++++ 9 files changed, 529 insertions(+), 150 deletions(-) delete mode 100644 src/types/FSA/FSA.backend.ts create mode 100644 src/types/FSA/utils.ts create mode 100644 src/types/FSA/validateFSA.ts diff --git a/src/types/FSA/FSA.backend.ts b/src/types/FSA/FSA.backend.ts deleted file mode 100644 index fd5a07b..0000000 --- a/src/types/FSA/FSA.backend.ts +++ /dev/null @@ -1,54 +0,0 @@ -// FSA.converter.ts -// this is for the inconsistency between the backend pydantic models and the frontend compromsise -import { FSA } from './type'; - -/** - * Backend representation of an FSA transition - */ -export interface BackendTransition { - from_state: string; - to_state: string; - symbol: string; -} - -/** - * Backend representation of the full FSA - */ -export interface BackendFSA { - states: string[]; - alphabet: string[]; - transitions: BackendTransition[]; - initial_state: string; - accept_states: string[]; -} - -export const FSAConverter = { - /** - * Converts frontend FSA (flat transitions) to Backend FSA (object transitions) - */ - toBackend(frontendFsa: FSA): BackendFSA { - return { - ...frontendFsa, - transitions: frontendFsa.transitions.map((tStr) => { - const [from, symbol, to] = tStr.split('|'); - return { - from_state: from || '', - to_state: to || '', - symbol: symbol || '', - }; - }), - }; - }, - - /** - * Converts Backend FSA (object transitions) to frontend FSA (flat transitions) - */ - toFrontend(backendFsa: BackendFSA): FSA { - return { - ...backendFsa, - transitions: backendFsa.transitions.map( - (t) => `${t.from_state}|${t.symbol}|${t.to_state}` - ), - }; - }, -}; \ No newline at end of file diff --git a/src/types/FSA/FSA.component.tsx b/src/types/FSA/FSA.component.tsx index 4e744ad..08335bd 100644 --- a/src/types/FSA/FSA.component.tsx +++ b/src/types/FSA/FSA.component.tsx @@ -5,18 +5,22 @@ import React, { useEffect, useRef, useState } from 'react' import ConfigPanel from './components/ConfigPanel' import ItemPropertiesPanel from './components/ItemPropertiesPanel' import { useLocalStyles } from './styles' -import { DEFAULT_FSA_CONFIG, FSA, FSAConfig, FSAFeedback } from './type' +import { CheckPhase, DEFAULT_FSA_CONFIG, FSA, FSAConfig, FSAFeedback } from './type' interface FSAInputProps { answer: FSA handleChange: (fsa: FSA) => void feedback: FSAFeedback | null + previewFeedback: FSAFeedback | null + phase: CheckPhase } export const FSAInput: React.FC = ({ answer, handleChange, feedback, + previewFeedback, + phase }) => { const { classes } = useLocalStyles() @@ -173,6 +177,7 @@ export const FSAInput: React.FC = ({ alphabet: Array.from( new Set(cy.edges().map((e) => String(e.data('label')))), ), + config: JSON.stringify(config) } handleChange(fsa) @@ -286,13 +291,23 @@ export const FSAInput: React.FC = ({ handleChange={handleChange} answer={answer} feedback={feedback} + previewFeedback={previewFeedback} + phase={phase} />
{ + const fsa: FSA = { + ...answer, + config: JSON.stringify(val) + } + + handleChange(fsa) + setConfig(val) + }} configOpen={configOpen} setConfigOpen={setConfigOpen} classes={classes} diff --git a/src/types/FSA/components/ConfigPanel.tsx b/src/types/FSA/components/ConfigPanel.tsx index 870db01..240ee63 100644 --- a/src/types/FSA/components/ConfigPanel.tsx +++ b/src/types/FSA/components/ConfigPanel.tsx @@ -1,20 +1,22 @@ import React from 'react' -interface EvaluationConfigPanelProps> { - config: T - setConfig: React.Dispatch> +import { FSAConfig } from '../type' + +interface EvaluationConfigPanelProps { + config: FSAConfig + setConfig: (config: FSAConfig) => void configOpen: boolean setConfigOpen: React.Dispatch> classes: Record } -export default function EvaluationConfigPanel>({ +export default function EvaluationConfigPanel({ config, setConfig, configOpen, setConfigOpen, - classes, -}: EvaluationConfigPanelProps) { + classes +}: EvaluationConfigPanelProps) { return (
>({ type="checkbox" checked={value} onChange={(e) => - setConfig((prev) => ({ - ...prev, + setConfig({ + ...config, [key]: e.target.checked, - })) + }) } /> ) : typeof value === 'number' ? ( @@ -48,10 +50,10 @@ export default function EvaluationConfigPanel>({ className={classes.inputField} value={value} onChange={(e) => - setConfig((prev) => ({ - ...prev, + setConfig({ + ...config, [key]: Number(e.target.value), - })) + }) } /> ) : ( @@ -59,10 +61,10 @@ export default function EvaluationConfigPanel>({ className={classes.inputField} value={String(value)} onChange={(e) => - setConfig((prev) => ({ - ...prev, + setConfig({ + ...config, [key]: e.target.value, - })) + }) } /> )} diff --git a/src/types/FSA/components/FSAFeedbackPanel.tsx b/src/types/FSA/components/FSAFeedbackPanel.tsx index 4e89fe9..0280090 100644 --- a/src/types/FSA/components/FSAFeedbackPanel.tsx +++ b/src/types/FSA/components/FSAFeedbackPanel.tsx @@ -1,18 +1,32 @@ import React, { useMemo } from 'react' -import { FSAFeedbackSchema, type FSAFeedback } from '../type' +import { + CheckPhase, + FSAFeedbackSchema, + type FSAFeedback, +} from '../type' interface FSAFeedbackPanelProps { feedback: FSAFeedback | null + phase: CheckPhase } -export function FSAFeedbackPanel({ feedback }: FSAFeedbackPanelProps) { - const parsed = useMemo(() => FSAFeedbackSchema.safeParse(feedback), [feedback]) +export function FSAFeedbackPanel({ + feedback, + phase, +}: FSAFeedbackPanelProps) { + + const parsed = useMemo( + () => FSAFeedbackSchema.safeParse(feedback), + [feedback], + ) + if (!feedback || !parsed.success) { - console.log(parsed.error?.message) return (
- No feedback yet {parsed.error?.message} + {phase === CheckPhase.PreviewError + ? 'Preview errors found' + : 'No feedback yet'}
) } @@ -21,6 +35,18 @@ export function FSAFeedbackPanel({ feedback }: FSAFeedbackPanelProps) { return (
+
+ { + phase == CheckPhase.PreviewError ? "Errors in Preview" : "Errors in Evaluation" + } +
{/* ================= Summary ================= */} {safeFeedback.summary && (
0 && ( @@ -85,12 +115,13 @@ export function FSAFeedbackPanel({ feedback }: FSAFeedbackPanelProps) { value={bool(safeFeedback.language.are_equivalent)} /> - {!safeFeedback.language.are_equivalent && safeFeedback.language.counterexample && ( - - )} + {!safeFeedback.language.are_equivalent && + safeFeedback.language.counterexample && ( + + )} )} diff --git a/src/types/FSA/components/ItemPropertiesPanel.tsx b/src/types/FSA/components/ItemPropertiesPanel.tsx index bb23291..9bd3fdf 100644 --- a/src/types/FSA/components/ItemPropertiesPanel.tsx +++ b/src/types/FSA/components/ItemPropertiesPanel.tsx @@ -1,7 +1,7 @@ import type { Core, NodeSingular, EdgeSingular } from 'cytoscape' import React from 'react' -import { FSA, FSAFeedback } from '../type' +import { CheckPhase, FSA, FSAFeedback } from '../type' import { FSAFeedbackPanel } from './FSAFeedbackPanel' @@ -26,6 +26,8 @@ interface ItemPropertiesPanelProps { syncToBackend: () => void feedback: FSAFeedback | null + previewFeedback: FSAFeedback | null + phase: CheckPhase } export default function ItemPropertiesPanel({ @@ -42,7 +44,9 @@ export default function ItemPropertiesPanel({ answer, handleChange, syncToBackend, - feedback + feedback, + previewFeedback, + phase }: ItemPropertiesPanelProps): JSX.Element { return (
@@ -160,6 +164,8 @@ export default function ItemPropertiesPanel({ )}
) diff --git a/src/types/FSA/index.tsx b/src/types/FSA/index.tsx index c2c2439..d30d776 100644 --- a/src/types/FSA/index.tsx +++ b/src/types/FSA/index.tsx @@ -1,5 +1,5 @@ // import React, { useMemo } from 'react' -// import { z } from 'zod' +import { z } from 'zod' import { @@ -9,84 +9,108 @@ import { import { ResponseAreaTub } from '../response-area-tub' import { FSAInput } from './FSA.component' -import { fsaAnswerSchema, FSA, defaultFSA, DEFAULT_FSA_CONFIG, FSAConfig, FSAFeedback } from './type' +import { fsaAnswerSchema, FSA, defaultFSA, DEFAULT_FSA_CONFIG, FSAConfig, FSAFeedback, ValidationError, CheckPhase, FSAFeedbackSchema, fsaConfigSchema } from './type' +import { validateFSA } from './validateFSA' export class FSAResponseAreaTub extends ResponseAreaTub { public readonly responseType = 'FSA' - public readonly displayWideInput: boolean = true + public readonly displayWideInput = true protected answerSchema = fsaAnswerSchema - protected answer: FSA = defaultFSA // Never undefined now - protected config: FSAConfig = DEFAULT_FSA_CONFIG - // private feedback: FSAFeedback | null = null + protected answer: FSA = defaultFSA - public readonly delegateFeedback = false // we want to manage our own feedback - public readonly delegateLivePreview = true// we want live previews + private previewFeedback: FSAFeedback | null = null + private phase: CheckPhase = CheckPhase.Idle + private response: FSA | null = null - initWithConfig = (config: any) => { - this.config = { - ...DEFAULT_FSA_CONFIG, - ...config, // not too sure about this, maybe the opposite so the default config is overwritten? - } - } - - // customCheck = () => {} // will set this up later + public readonly delegateFeedback = false + public readonly delegateLivePreview = true + + initWithConfig = () => {} + + /* -------------------- Custom Check -------------------- */ + + customCheck = () => { + // Block submission if preview validation fails + if (this.previewFeedback) { + throw new Error('preview failed') + } + + // Preview passed — ensure it's cleared + this.previewFeedback = null + // this.phase = CheckPhase.Idle + } /* -------------------- Input -------------------- */ -public InputComponent = (props: BaseResponseAreaProps): JSX.Element => { - // Ensure a valid FSA answer - const validAnswer: FSA = this.answerSchema.safeParse(props.answer).success - ? this.answerSchema.safeParse(props.answer).data ?? defaultFSA - : defaultFSA - - // // Parse feedback safely and memoize so it updates when props.feedback changes - // const feedback: FSAFeedback | null = useMemo(() => { - // if (!props.feedback?.feedback) return null - // try { - // return JSON.parse(props.feedback.feedback) - // } catch { - // return null - // } - // }, [props.feedback?.feedback]) - - return ( - { - const raw = props.feedback?.feedback - if (!raw) return {} - try { - // split by
and take the second part, trim whitespace - const jsonPart = raw.split('
')[1]?.trim() ?? '{}' - return JSON.parse(jsonPart) - } catch { - return {} // fallback to empty object if parsing fails - } - })()} - answer={validAnswer} - handleChange={(val: FSA) => props.handleChange(val)} - /> - ) -} + public InputComponent = (props: BaseResponseAreaProps): JSX.Element => { + // Ensure a valid FSA answer + const parsed = this.answerSchema.safeParse(props.answer) + const validAnswer = parsed.success ? parsed.data : defaultFSA + + this.response = validAnswer + + /* ---------- Extract submitted feedback ---------- */ + + const submittedFeedback: FSAFeedback | null = (() => { + const raw = props.feedback?.feedback + if (!raw) return null + + try { + const jsonPart = raw.split('
')[1]?.trim() + if (!jsonPart) return null + return JSON.parse(jsonPart) + } catch { + return null + } + })() + /* ---------- Effective feedback ---------- */ + + const effectiveFeedback = + this.previewFeedback ?? submittedFeedback + + return ( + { + props.handleChange(val) + + const preview = validateFSA(val) + + if (preview.errors.length > 0) { + this.previewFeedback = preview + this.phase = CheckPhase.PreviewError + } else { + this.previewFeedback = null // 🔥 THIS IS THE KEY + this.phase = CheckPhase.Idle + } + }} + /> + ) + } /* -------------------- Wizard -------------------- */ + public WizardComponent = ( props: BaseResponseAreaWizardProps, ): JSX.Element => { return ( { + feedback={null} + answer={this.answer} + phase={CheckPhase.Evaluated} + previewFeedback={null} + handleChange={(val: FSA) => { this.answer = val - console.log('Wizard val:', val) props.handleChange({ responseType: this.responseType, answer: val, - config: this.config as unknown as Record }) }} /> diff --git a/src/types/FSA/type.ts b/src/types/FSA/type.ts index 516ed54..aad2a8a 100644 --- a/src/types/FSA/type.ts +++ b/src/types/FSA/type.ts @@ -11,18 +11,11 @@ export const fsaAnswerSchema = z.object({ transitions: z.array(z.string()), initial_state: z.string(), accept_states: z.array(z.string()), + config: z.string() }); export type FSA = z.infer; -export const defaultFSA: FSA = { - states: [], - alphabet: [], - transitions: [], - initial_state: '', - accept_states: [] -}; - export const fsaConfigSchema = z.object({ evaluation_mode: z.enum(['strict', 'lenient', 'partial']).optional(), expected_type: z.enum(['DFA', 'NFA', 'any']).optional(), @@ -127,7 +120,7 @@ export const ValidationErrorSchema = z.object({ code: ErrorCodeSchema, severity: ValidationSeveritySchema.default("error"), highlight: ElementHighlightSchema.nullable().optional(), - suggestion: z.string().optional(), + suggestion: z.string().nullable().optional(), }); /* =========================== @@ -196,3 +189,19 @@ export type TestResult = z.infer; export type StructuralInfo = z.infer; export type LanguageComparison = z.infer; export type FSAFeedback = z.infer; + +export enum CheckPhase { + Idle = 'IDLE', + PreviewError = 'PREVIEW_ERROR', + Evaluating = 'EVALUATING', // we never have access to the api call, so this is useless + Evaluated = 'EVALUATED', +} + +export const defaultFSA: FSA = { + states: [], + alphabet: [], + transitions: [], + initial_state: '', + accept_states: [], + config: JSON.stringify(DEFAULT_FSA_CONFIG) +}; \ No newline at end of file diff --git a/src/types/FSA/utils.ts b/src/types/FSA/utils.ts new file mode 100644 index 0000000..5654792 --- /dev/null +++ b/src/types/FSA/utils.ts @@ -0,0 +1,13 @@ +import { CheckPhase, FSAFeedback } from "./type" + +export function mergeFeedback( + feedback: FSAFeedback | null, + previewFeedback: FSAFeedback | null, + phase: CheckPhase, +): FSAFeedback | null { + if (phase === CheckPhase.PreviewError) { + return previewFeedback + } + + return feedback +} \ No newline at end of file diff --git a/src/types/FSA/validateFSA.ts b/src/types/FSA/validateFSA.ts new file mode 100644 index 0000000..01a4520 --- /dev/null +++ b/src/types/FSA/validateFSA.ts @@ -0,0 +1,333 @@ +import type { FSA, FSAFeedback } from "./type"; +import { ValidationError } from "./type"; + +/* =========================== + Internal helper types +=========================== */ + +type MutableFeedback = Pick; + +type OutgoingMap = Map>; + +/* =========================== + Basic validation checks +=========================== */ + +const checkStates = ( + states: string[], + feedback: MutableFeedback +): boolean => { + if (states.length === 0) { + feedback.errors.push({ + message: "The automaton has no states.", + code: "EMPTY_STATES", + severity: "error", + }); + return false; + } + return true; +}; + +const checkAlphabet = ( + alphabet: string[], + feedback: MutableFeedback +): boolean => { + if (alphabet.length === 0) { + feedback.errors.push({ + message: "The automaton has no alphabet symbols.", + code: "EMPTY_ALPHABET", + severity: "error", + }); + return false; + } + return true; +}; + +const checkInitialState = ( + initial_state: string, + states: string[], + feedback: MutableFeedback +): boolean => { + if (!states.includes(initial_state)) { + feedback.errors.push({ + message: `Initial state "${initial_state}" is not a valid state.`, + code: "INVALID_INITIAL", + severity: "error", + highlight: { + type: "initial_state", + state_id: initial_state, + }, + }); + return false; + } + return true; +}; + +const checkAcceptStates = ( + accept_states: string[], + states: string[], + feedback: MutableFeedback +): boolean => { + let ok = true; + + for (const s of accept_states) { + if (!states.includes(s)) { + ok = false; + feedback.errors.push({ + message: `Accept state "${s}" is not a valid state.`, + code: "INVALID_ACCEPT", + severity: "error", + highlight: { + type: "accept_state", + state_id: s, + }, + }); + } + } + + return ok; +}; + +const checkTransitions = ( + transitions: string[], + states: string[], + alphabet: string[], + feedback: MutableFeedback +): OutgoingMap => { + const outgoing: OutgoingMap = new Map(); + + for (const t of transitions) { + const parts = t.split("|"); + + // Cannot safely highlight: transition not parseable + if (parts.length !== 3) { + feedback.errors.push({ + message: `Invalid transition format "${t}".`, + code: "INVALID_TRANSITION_SYMBOL", + severity: "error", + }); + continue; + } + + const [from, symbol, to] = parts; + + // Still not safely highlightable + if (!from || !symbol || !to) { + feedback.errors.push({ + message: `Transition unrecognisable "${t}".`, + code: "INVALID_SYMBOL", + severity: "error", + } as ValidationError); + continue; + } + + if (!states.includes(from)) { + feedback.errors.push({ + message: `Transition source "${from}" is invalid.`, + code: "INVALID_TRANSITION_SOURCE", + severity: "error", + highlight: { + type: "transition", + from_state: from, + to_state: to, + symbol, + }, + }); + } + + if (!states.includes(to)) { + feedback.errors.push({ + message: `Transition destination "${to}" is invalid.`, + code: "INVALID_TRANSITION_DEST", + severity: "error", + highlight: { + type: "transition", + from_state: from, + to_state: to, + symbol, + }, + }); + } + + if (!alphabet.includes(symbol)) { + feedback.errors.push({ + message: `Transition symbol "${symbol}" is invalid.`, + code: "INVALID_TRANSITION_SYMBOL", + severity: "error", + highlight: { + type: "alphabet_symbol", + symbol, + }, + }); + } + + if (!outgoing.has(from)) outgoing.set(from, []); + outgoing.get(from)!.push({ symbol, to }); + } + + return outgoing; +}; + +/* =========================== + Structural computations +=========================== */ + +const computeDeterminism = (outgoing: OutgoingMap): boolean => { + for (const edges of outgoing.values()) { + const seen = new Set(); + for (const { symbol } of edges) { + if (seen.has(symbol)) return false; + seen.add(symbol); + } + } + return true; +}; + +const computeCompleteness = ( + states: string[], + alphabet: string[], + outgoing: OutgoingMap +): boolean => { + for (const state of states) { + const edges = outgoing.get(state) ?? []; + const covered = new Set(edges.map(e => e.symbol)); + + for (const sym of alphabet) { + if (!covered.has(sym)) return false; + } + } + return true; +}; + +const computeReachableStates = ( + initial_state: string, + states: string[], + outgoing: OutgoingMap +): Set => { + const reachable = new Set(); + const stack: string[] = []; + + if (states.includes(initial_state)) { + reachable.add(initial_state); + stack.push(initial_state); + } + + while (stack.length) { + const s = stack.pop()!; + for (const { to } of outgoing.get(s) ?? []) { + if (!reachable.has(to)) { + reachable.add(to); + stack.push(to); + } + } + } + + return reachable; +}; + +const computeDeadStates = ( + states: string[], + accept_states: string[], + outgoing: OutgoingMap +): string[] => { + const reverse = new Map(); + + for (const [from, edges] of outgoing) { + for (const { to } of edges) { + if (!reverse.has(to)) reverse.set(to, []); + reverse.get(to)!.push(from); + } + } + + const canReachAccept = new Set(accept_states); + const queue = [...accept_states]; + + while (queue.length) { + const s = queue.shift()!; + for (const p of reverse.get(s) ?? []) { + if (!canReachAccept.has(p)) { + canReachAccept.add(p); + queue.push(p); + } + } + } + + return states.filter( + s => !canReachAccept.has(s) && !accept_states.includes(s) + ); +}; + +/* =========================== + Public API +=========================== */ + +export const validateFSA = (fsa: FSA | null): FSAFeedback => { + const feedback: FSAFeedback = { + summary: "", + errors: [], + warnings: [], + test_results: [], + hints: [], + }; + + if (!fsa) { + feedback.errors.push({ + message: "No automaton provided.", + code: "EVALUATION_ERROR", + severity: "error", + }); + return feedback; + } + + const { states, alphabet, transitions, initial_state, accept_states } = fsa; + + const statesOk = checkStates(states, feedback); + const alphabetOk = checkAlphabet(alphabet, feedback); + const initialOk = checkInitialState(initial_state, states, feedback); + const acceptOk = checkAcceptStates(accept_states, states, feedback); + + const outgoing = checkTransitions( + transitions, + states, + alphabet, + feedback + ); + + const is_deterministic = computeDeterminism(outgoing); + const is_complete = computeCompleteness(states, alphabet, outgoing); + + const reachable = computeReachableStates( + initial_state, + states, + outgoing + ); + const unreachable_states = states.filter(s => !reachable.has(s)); + + const dead_states = computeDeadStates( + states, + accept_states, + outgoing + ); + + feedback.structural = { + is_deterministic, + is_complete, + num_states: states.length, + num_transitions: transitions.length, + unreachable_states, + dead_states, + }; + + const isValid = + statesOk && + alphabetOk && + initialOk && + acceptOk && + feedback.errors.length === 0; + + feedback.summary = isValid + ? "The automaton is a valid finite-state automaton." + : "The automaton is not a valid finite-state automaton."; + + return feedback; +}; From 690aff5b9d7cb6ce2799efd31a090f15ed496c60 Mon Sep 17 00:00:00 2001 From: everythingfades Date: Thu, 5 Feb 2026 01:06:11 +0000 Subject: [PATCH 13/36] feat: first draft, still need fix --- dev/index.html | 14 - dev/main.tsx | 37 -- package.json | 1 + src/sandbox-component.tsx | 4 +- src/types/FSA/FSA.component.tsx | 317 --------- src/types/FSA/components/ConfigPanel.tsx | 77 --- src/types/FSA/components/FSAFeedbackPanel.tsx | 256 -------- src/types/FSA/index.tsx | 119 ---- src/types/FSA/type.ts | 207 ------ src/types/FSA/utils.ts | 13 - src/types/FSA/validateFSA.ts | 333 ---------- src/types/Graph/Graph.component.tsx | 611 ++++++++++++++++++ src/types/Graph/components/ConfigPanel.tsx | 77 +++ .../Graph/components/GraphFeedbackPanel.tsx | 256 ++++++++ .../components/ItemPropertiesPanel.tsx | 93 +-- src/types/Graph/index.tsx | 120 ++++ src/types/{FSA => Graph}/styles.ts | 0 src/types/Graph/type.ts | 75 +++ src/types/Graph/utils.ts | 13 + src/types/index.ts | 6 +- yarn.lock | 5 + 21 files changed, 1184 insertions(+), 1450 deletions(-) delete mode 100644 dev/index.html delete mode 100644 dev/main.tsx delete mode 100644 src/types/FSA/FSA.component.tsx delete mode 100644 src/types/FSA/components/ConfigPanel.tsx delete mode 100644 src/types/FSA/components/FSAFeedbackPanel.tsx delete mode 100644 src/types/FSA/index.tsx delete mode 100644 src/types/FSA/type.ts delete mode 100644 src/types/FSA/utils.ts delete mode 100644 src/types/FSA/validateFSA.ts create mode 100644 src/types/Graph/Graph.component.tsx create mode 100644 src/types/Graph/components/ConfigPanel.tsx create mode 100644 src/types/Graph/components/GraphFeedbackPanel.tsx rename src/types/{FSA => Graph}/components/ItemPropertiesPanel.tsx (51%) create mode 100644 src/types/Graph/index.tsx rename src/types/{FSA => Graph}/styles.ts (100%) create mode 100644 src/types/Graph/type.ts create mode 100644 src/types/Graph/utils.ts diff --git a/dev/index.html b/dev/index.html deleted file mode 100644 index 0894690..0000000 --- a/dev/index.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - Library Dev Sandbox - - -
- - - - - diff --git a/dev/main.tsx b/dev/main.tsx deleted file mode 100644 index eec2322..0000000 --- a/dev/main.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { IModularResponseSchema } from '@modules/shared/schemas/question-form.schema' -import React, { useState } from 'react' -import ReactDOM from 'react-dom/client' - -import { FSAResponseAreaTub } from '../src/types/FSA' -import { FSAInput } from '../src/types/FSA/FSA.component' -import { defaultFSA, FSA } from '../src/types/FSA/type' - - -const tub = new FSAResponseAreaTub() - -function Sandbox() { - const [answer, setAnswer] = - useState(defaultFSA) - - const [, setAllowSave] = useState(true) - - return ( - <> -

Input

- console.log("wizard change", val)} - /> - -
- -

Wizard

- - ) -} - -ReactDOM.createRoot(document.getElementById('root')!).render( - - - -) diff --git a/package.json b/package.json index c64c1c2..dda9aa2 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "markdown-to-jsx": "^7.1.7", "monaco-editor": "^0.50.0", "next": "^14.2.4", + "paper": "^0.12.18", "react": "^18.3.1", "react-dom": "^18.3.1", "react-hook-form": "^7.31.2", diff --git a/src/sandbox-component.tsx b/src/sandbox-component.tsx index 098da57..9dae867 100644 --- a/src/sandbox-component.tsx +++ b/src/sandbox-component.tsx @@ -1,6 +1,6 @@ import { ThemeProvider } from '@styles/minimal/theme-provider' -import { FSAResponseAreaTub } from './types/FSA' +import { GraphResponseAreaTub } from './types/Graph' // import { SandboxResponseAreaTub } from './types/Sandbox' function ResponseAreaInputWrapper({ children }: { children: React.ReactNode }) { @@ -8,7 +8,7 @@ function ResponseAreaInputWrapper({ children }: { children: React.ReactNode }) { } // wrap the components with the necessary providers; only in the sandbox -class WrappedSandboxResponseAreaTub extends FSAResponseAreaTub { +class WrappedSandboxResponseAreaTub extends GraphResponseAreaTub { constructor() { super() diff --git a/src/types/FSA/FSA.component.tsx b/src/types/FSA/FSA.component.tsx deleted file mode 100644 index 08335bd..0000000 --- a/src/types/FSA/FSA.component.tsx +++ /dev/null @@ -1,317 +0,0 @@ -import { makeStyles } from '@styles' -import cytoscape, { Core, NodeSingular, EdgeSingular } from 'cytoscape' -import React, { useEffect, useRef, useState } from 'react' - -import ConfigPanel from './components/ConfigPanel' -import ItemPropertiesPanel from './components/ItemPropertiesPanel' -import { useLocalStyles } from './styles' -import { CheckPhase, DEFAULT_FSA_CONFIG, FSA, FSAConfig, FSAFeedback } from './type' - -interface FSAInputProps { - answer: FSA - handleChange: (fsa: FSA) => void - feedback: FSAFeedback | null - previewFeedback: FSAFeedback | null - phase: CheckPhase -} - -export const FSAInput: React.FC = ({ - answer, - handleChange, - feedback, - previewFeedback, - phase -}) => { - const { classes } = useLocalStyles() - - const cyRef = useRef(null) - const containerRef = useRef(null) - - const [selectedNode, setSelectedNode] = useState(null) - const [selectedEdge, setSelectedEdge] = useState(null) - const [drawMode, setDrawMode] = useState(false) - const [fromNode, setFromNode] = useState(null) - const [config, setConfig] = useState(DEFAULT_FSA_CONFIG) - const [configOpen, setConfigOpen] = useState(true) - - /* -------------------- init cytoscape -------------------- */ - useEffect(() => { - if (!containerRef.current) return - - const cy: Core = cytoscape({ - container: containerRef.current, - layout: { name: 'preset' }, - style: [ - // ---------------- Nodes ---------------- - { - selector: 'node', - style: { - label: 'data(displayLabel)', - 'text-valign': 'center', - 'text-halign': 'center', - width: 50, - height: 50, - 'background-color': '#fff', - 'border-width': 1, - 'border-color': '#555', - }, - }, - - { - selector: 'node.initial', - style: { - 'border-width': 3, - 'border-color': '#1976d2', - }, - }, - - { - selector: 'node.accept', - style: { - 'border-style': 'double', - 'border-width': 4, - }, - }, - - { - selector: 'node.error-highlight', - style: { - 'background-color': '#ffebee', - 'border-color': '#d32f2f', - 'border-width': 4, - }, - }, - - // ---------------- Edges ---------------- - { - selector: 'edge', - style: { - label: 'data(label)', - 'curve-style': 'bezier', - 'target-arrow-shape': 'triangle', - 'line-color': '#555', - 'target-arrow-color': '#555', - 'text-background-color': '#fff', - 'text-background-opacity': 1, - 'text-background-padding': '3px', - }, - }, - - { - selector: 'edge.error-highlight', - style: { - 'line-color': '#d32f2f', - 'target-arrow-color': '#d32f2f', - 'line-style': 'dashed', - width: 3, - }, - }, - ], - }) - - cyRef.current = cy - return () => cy.destroy() - }, []) - - /* -------------------- node/edge handlers -------------------- */ - useEffect(() => { - const cy = cyRef.current - if (!cy) return - - const tapNode = (e: cytoscape.EventObject): void => { - const node = e.target as NodeSingular - - if (drawMode) { - if (!fromNode) { - setFromNode(node.id()) - node.addClass('edge-source') - } else { - cy.add({ - group: 'edges', - data: { - id: `e-${fromNode}-${node.id()}-${Date.now()}`, - source: fromNode, - target: node.id(), - label: config.epsilon_symbol, - }, - }) - cy.nodes().removeClass('edge-source') - setDrawMode(false) - setFromNode(null) - syncToBackend() - } - return - } - - setSelectedNode(node) - setSelectedEdge(null) - } - - const tapEdge = (e: cytoscape.EventObject): void => { - setSelectedEdge(e.target as EdgeSingular) - setSelectedNode(null) - } - - cy.on('tap', 'node', tapNode) - cy.on('tap', 'edge', tapEdge) - - return () => { - cy.off('tap', 'node', tapNode) - cy.off('tap', 'edge', tapEdge) - } - }, [drawMode, fromNode, config.epsilon_symbol]) - - /* -------------------- sync to backend -------------------- */ - const syncToBackend = (): void => { - const cy = cyRef.current - if (!cy) return - - const fsa: FSA = { - states: cy.nodes()?.map((n) => n.id()), - transitions: cy.edges()?.map( - (e) => - `${e.source().id()}|${e.data('label') || config.epsilon_symbol}|${e.target().id()}`, - ), - initial_state: answer.initial_state, - accept_states: answer.accept_states, - alphabet: Array.from( - new Set(cy.edges().map((e) => String(e.data('label')))), - ), - config: JSON.stringify(config) - } - - handleChange(fsa) - } - - /* -------------------- add state -------------------- */ - const addState = (): void => { - const cy = cyRef.current - if (!cy) return - - const id = `q${cy.nodes().length}` - cy.add({ - group: 'nodes', - data: { id, displayLabel: id }, - position: { - x: 100 + Math.random() * 300, - y: 100 + Math.random() * 300, - }, - }) - - syncToBackend() - } - - /* -------------------- apply initial / accept styling -------------------- */ - useEffect(() => { - const cy = cyRef.current - if (!cy) return - - cy.nodes().removeClass('initial accept') - - if (answer.initial_state) { - cy.$id(answer.initial_state).addClass('initial') - } - - for (const id of answer.accept_states) { - cy.$id(id).addClass('accept') - } - }, [answer.initial_state, answer.accept_states]) - - /* -------------------- apply feedback highlights -------------------- */ - useEffect(() => { - const cy = cyRef.current - if (!cy) return - - cy.nodes().removeClass('error-highlight') - cy.edges().removeClass('error-highlight') - - if (!feedback || !feedback.errors) return - - const highlights = feedback.errors - .map((e) => e.highlight) - .filter(Boolean) - - for (const h of highlights) { - if (!h) continue - - switch (h.type) { - case 'state': - case 'initial_state': - case 'accept_state': { - if (h.state_id) { - cy.$id(h.state_id).addClass('error-highlight') - } - break - } - - case 'transition': { - cy.edges() - .filter((e) => { - const fromOk = h.from_state - ? e.source().id() === h.from_state - : true - const toOk = h.to_state - ? e.target().id() === h.to_state - : true - const symOk = h.symbol - ? e.data('label') === h.symbol - : true - return fromOk && toOk && symOk - }) - .addClass('error-highlight') - break - } - - case 'alphabet_symbol': { - if (h.symbol) { - cy.edges() - .filter((e) => e.data('label') === h.symbol) - .addClass('error-highlight') - } - break - } - } - } - }, [feedback]) - - return ( -
- - -
- - { - const fsa: FSA = { - ...answer, - config: JSON.stringify(val) - } - - handleChange(fsa) - setConfig(val) - }} - configOpen={configOpen} - setConfigOpen={setConfigOpen} - classes={classes} - /> -
- ) -} diff --git a/src/types/FSA/components/ConfigPanel.tsx b/src/types/FSA/components/ConfigPanel.tsx deleted file mode 100644 index 240ee63..0000000 --- a/src/types/FSA/components/ConfigPanel.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import React from 'react' - -import { FSAConfig } from '../type' - -interface EvaluationConfigPanelProps { - config: FSAConfig - setConfig: (config: FSAConfig) => void - configOpen: boolean - setConfigOpen: React.Dispatch> - classes: Record -} - -export default function EvaluationConfigPanel({ - config, - setConfig, - configOpen, - setConfigOpen, - classes -}: EvaluationConfigPanelProps) { - return ( -
-
setConfigOpen((o) => !o)} - > - Evaluation Config - {configOpen ? '▾' : '▸'} -
- - {configOpen && ( -
- {Object.entries(config).map(([key, value]) => ( -
- - - {typeof value === 'boolean' ? ( - - setConfig({ - ...config, - [key]: e.target.checked, - }) - } - /> - ) : typeof value === 'number' ? ( - - setConfig({ - ...config, - [key]: Number(e.target.value), - }) - } - /> - ) : ( - - setConfig({ - ...config, - [key]: e.target.value, - }) - } - /> - )} -
- ))} -
- )} -
- ) -} diff --git a/src/types/FSA/components/FSAFeedbackPanel.tsx b/src/types/FSA/components/FSAFeedbackPanel.tsx deleted file mode 100644 index 0280090..0000000 --- a/src/types/FSA/components/FSAFeedbackPanel.tsx +++ /dev/null @@ -1,256 +0,0 @@ -import React, { useMemo } from 'react' - -import { - CheckPhase, - FSAFeedbackSchema, - type FSAFeedback, -} from '../type' - -interface FSAFeedbackPanelProps { - feedback: FSAFeedback | null - phase: CheckPhase -} - -export function FSAFeedbackPanel({ - feedback, - phase, -}: FSAFeedbackPanelProps) { - - const parsed = useMemo( - () => FSAFeedbackSchema.safeParse(feedback), - [feedback], - ) - - if (!feedback || !parsed.success) { - return ( -
- {phase === CheckPhase.PreviewError - ? 'Preview errors found' - : 'No feedback yet'} -
- ) - } - - const safeFeedback = parsed.data - - return ( -
-
- { - phase == CheckPhase.PreviewError ? "Errors in Preview" : "Errors in Evaluation" - } -
- {/* ================= Summary ================= */} - {safeFeedback.summary && ( -
- {safeFeedback.summary} -
- )} - - {/* ================= Errors ================= */} - {safeFeedback.errors.length > 0 && ( - - )} - - {/* ================= Warnings ================= */} - {safeFeedback.warnings.length > 0 && ( - - )} - - {/* ================= Structural Info ================= */} - {safeFeedback.structural && ( -
- - - - - - {safeFeedback.structural.unreachable_states.length > 0 && ( - - )} - - {safeFeedback.structural.dead_states.length > 0 && ( - - )} -
- )} - - {/* ================= Language ================= */} - {safeFeedback.language && ( -
- - - {!safeFeedback.language.are_equivalent && - safeFeedback.language.counterexample && ( - - )} -
- )} - - {/* ================= Test Results ================= */} - {safeFeedback.test_results.length > 0 && ( -
- {safeFeedback.test_results.map((t, i) => ( -
- {JSON.stringify(t.input)} - {t.passed ? '✓' : '✗'} -
- ))} -
- )} - - {/* ================= Hints ================= */} - {safeFeedback.hints.length > 0 && ( -
-
    - {safeFeedback.hints.map((h, i) => ( -
  • {h}
  • - ))} -
-
- )} -
- ) -} - -/* =========================== - Helper components -=========================== */ - -function FeedbackSection({ - title, - items, - accent, -}: { - title: string - items: any[] - accent: string -}) { - return ( -
-
- {items.map((e, i) => ( -
-
{e.message}
- -
- {e.code} · {e.severity} -
- - {e.suggestion && ( -
- 💡 {e.suggestion} -
- )} -
- ))} -
-
- ) -} - - -function Section({ - title, - children, -}: { - title: string - children: React.ReactNode -}) { - return ( -
-
- {title} -
- {children} -
- ) -} - -function KV({ label, value }: { label: string; value: React.ReactNode }) { - return ( -
- {label}: - {value} -
- ) -} - -function bool(v: boolean) { - return v ? 'Yes' : 'No' -} diff --git a/src/types/FSA/index.tsx b/src/types/FSA/index.tsx deleted file mode 100644 index d30d776..0000000 --- a/src/types/FSA/index.tsx +++ /dev/null @@ -1,119 +0,0 @@ -// import React, { useMemo } from 'react' -import { z } from 'zod' - - -import { - BaseResponseAreaProps, - BaseResponseAreaWizardProps, -} from '../base-props.type' -import { ResponseAreaTub } from '../response-area-tub' - -import { FSAInput } from './FSA.component' -import { fsaAnswerSchema, FSA, defaultFSA, DEFAULT_FSA_CONFIG, FSAConfig, FSAFeedback, ValidationError, CheckPhase, FSAFeedbackSchema, fsaConfigSchema } from './type' -import { validateFSA } from './validateFSA' - -export class FSAResponseAreaTub extends ResponseAreaTub { - public readonly responseType = 'FSA' - public readonly displayWideInput = true - - protected answerSchema = fsaAnswerSchema - protected answer: FSA = defaultFSA - - private previewFeedback: FSAFeedback | null = null - private phase: CheckPhase = CheckPhase.Idle - private response: FSA | null = null - - public readonly delegateFeedback = false - public readonly delegateLivePreview = true - - initWithConfig = () => {} - - /* -------------------- Custom Check -------------------- */ - - customCheck = () => { - // Block submission if preview validation fails - if (this.previewFeedback) { - throw new Error('preview failed') - } - - // Preview passed — ensure it's cleared - this.previewFeedback = null - // this.phase = CheckPhase.Idle - } - - /* -------------------- Input -------------------- */ - - public InputComponent = (props: BaseResponseAreaProps): JSX.Element => { - // Ensure a valid FSA answer - const parsed = this.answerSchema.safeParse(props.answer) - const validAnswer = parsed.success ? parsed.data : defaultFSA - - this.response = validAnswer - - /* ---------- Extract submitted feedback ---------- */ - - const submittedFeedback: FSAFeedback | null = (() => { - const raw = props.feedback?.feedback - if (!raw) return null - - try { - const jsonPart = raw.split('
')[1]?.trim() - if (!jsonPart) return null - return JSON.parse(jsonPart) - } catch { - return null - } - })() - - /* ---------- Effective feedback ---------- */ - - const effectiveFeedback = - this.previewFeedback ?? submittedFeedback - - return ( - { - props.handleChange(val) - - const preview = validateFSA(val) - - if (preview.errors.length > 0) { - this.previewFeedback = preview - this.phase = CheckPhase.PreviewError - } else { - this.previewFeedback = null // 🔥 THIS IS THE KEY - this.phase = CheckPhase.Idle - } - }} - /> - ) - } - - /* -------------------- Wizard -------------------- */ - - public WizardComponent = ( - props: BaseResponseAreaWizardProps, - ): JSX.Element => { - return ( - { - this.answer = val - props.handleChange({ - responseType: this.responseType, - answer: val, - }) - }} - /> - ) - } -} diff --git a/src/types/FSA/type.ts b/src/types/FSA/type.ts deleted file mode 100644 index aad2a8a..0000000 --- a/src/types/FSA/type.ts +++ /dev/null @@ -1,207 +0,0 @@ -// this is kind of the compromise for the zod restricts IModularResponseSchema and the backend python schema cannot match that -// see file externals/modules/shared/schemas/question-form.schema.ts for details -// since that is a external module, we should not edit that file - -import { z } from 'zod'; - -export const fsaAnswerSchema = z.object({ - states: z.array(z.string()), - alphabet: z.array(z.string()), - // Flattened: Array of "from|symbol|to" strings - transitions: z.array(z.string()), - initial_state: z.string(), - accept_states: z.array(z.string()), - config: z.string() -}); - -export type FSA = z.infer; - -export const fsaConfigSchema = z.object({ - evaluation_mode: z.enum(['strict', 'lenient', 'partial']).optional(), - expected_type: z.enum(['DFA', 'NFA', 'any']).optional(), - feedback_verbosity: z.enum(['minimal', 'standard', 'detailed']).optional(), - - check_minimality: z.boolean().optional(), - check_completeness: z.boolean().optional(), - - highlight_errors: z.boolean().optional(), - show_counterexample: z.boolean().optional(), - - max_test_length: z.number().int().positive().optional(), - - is_dev: z.boolean().optional(), - - epsilon_symbol: z.string(), -}) - -export type FSAConfig = z.infer - -export const DEFAULT_FSA_CONFIG: FSAConfig = { - evaluation_mode: "lenient", - expected_type: "any", - feedback_verbosity: "standard", - - check_minimality: false, - check_completeness: false, - - highlight_errors: true, - show_counterexample: true, - - max_test_length: 10, - - is_dev: false, - epsilon_symbol: "epsilon" -} - -/* =========================== - Error codes -=========================== */ - -export const ErrorCodeSchema = z.enum([ - "INVALID_STATE", - "INVALID_INITIAL", - "INVALID_ACCEPT", - "INVALID_SYMBOL", - - "INVALID_TRANSITION_SOURCE", - "INVALID_TRANSITION_DEST", - "INVALID_TRANSITION_SYMBOL", - "MISSING_TRANSITION", - "DUPLICATE_TRANSITION", - - "UNREACHABLE_STATE", - "DEAD_STATE", - - "WRONG_AUTOMATON_TYPE", - "NOT_DETERMINISTIC", - "NOT_COMPLETE", - "NOT_MINIMAL", - - "LANGUAGE_MISMATCH", - "TEST_CASE_FAILED", - - "EMPTY_STATES", - "EMPTY_ALPHABET", - "EVALUATION_ERROR", -]); - -/* =========================== - Element highlighting -=========================== */ - -export const ElementHighlightTypeSchema = z.enum([ - "state", - "transition", - "initial_state", - "accept_state", - "alphabet_symbol", -]); -export const ElementHighlightSchema = z.object({ - type: ElementHighlightTypeSchema, - state_id: z.string().nullable().optional(), - from_state: z.string().nullable().optional(), - to_state: z.string().nullable().optional(), - symbol: z.string().nullable().optional(), -}); - - -/* =========================== - Validation errors -=========================== */ - -export const ValidationSeveritySchema = z.enum([ - "error", - "warning", - "info", -]); - -export const ValidationErrorSchema = z.object({ - message: z.string(), - code: ErrorCodeSchema, - severity: ValidationSeveritySchema.default("error"), - highlight: ElementHighlightSchema.nullable().optional(), - suggestion: z.string().nullable().optional(), -}); - -/* =========================== - Test results -=========================== */ - -export const TestResultSchema = z.object({ - input: z.string(), - expected: z.boolean(), - actual: z.boolean(), - passed: z.boolean(), - trace: z.array(z.string()).optional(), -}); - -/* =========================== - Structural analysis -=========================== */ - -export const StructuralInfoSchema = z.object({ - is_deterministic: z.boolean(), - is_complete: z.boolean(), - - num_states: z.number().int().min(0), - num_transitions: z.number().int().min(0), - - unreachable_states: z.array(z.string()).default([]), - dead_states: z.array(z.string()).default([]), -}); - -/* =========================== - Language comparison -=========================== */ - -export const CounterexampleTypeSchema = z.enum([ - "should_accept", - "should_reject", -]); - -export const LanguageComparisonSchema = z.object({ - are_equivalent: z.boolean(), - counterexample: z.string().nullable().optional(), - counterexample_type: CounterexampleTypeSchema.nullable().optional(), -}); - -/* =========================== - Top-level feedback -=========================== */ - -export const FSAFeedbackSchema = z.object({ - summary: z.string().default(""), - - errors: z.array(ValidationErrorSchema).default([]), - warnings: z.array(ValidationErrorSchema).default([]), - - structural: StructuralInfoSchema.optional(), - language: LanguageComparisonSchema.optional(), - - test_results: z.array(TestResultSchema).default([]), - hints: z.array(z.string()).default([]), -}); - -export type ErrorCode = z.infer; -export type ElementHighlight = z.infer; -export type ValidationError = z.infer; -export type TestResult = z.infer; -export type StructuralInfo = z.infer; -export type LanguageComparison = z.infer; -export type FSAFeedback = z.infer; - -export enum CheckPhase { - Idle = 'IDLE', - PreviewError = 'PREVIEW_ERROR', - Evaluating = 'EVALUATING', // we never have access to the api call, so this is useless - Evaluated = 'EVALUATED', -} - -export const defaultFSA: FSA = { - states: [], - alphabet: [], - transitions: [], - initial_state: '', - accept_states: [], - config: JSON.stringify(DEFAULT_FSA_CONFIG) -}; \ No newline at end of file diff --git a/src/types/FSA/utils.ts b/src/types/FSA/utils.ts deleted file mode 100644 index 5654792..0000000 --- a/src/types/FSA/utils.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { CheckPhase, FSAFeedback } from "./type" - -export function mergeFeedback( - feedback: FSAFeedback | null, - previewFeedback: FSAFeedback | null, - phase: CheckPhase, -): FSAFeedback | null { - if (phase === CheckPhase.PreviewError) { - return previewFeedback - } - - return feedback -} \ No newline at end of file diff --git a/src/types/FSA/validateFSA.ts b/src/types/FSA/validateFSA.ts deleted file mode 100644 index 01a4520..0000000 --- a/src/types/FSA/validateFSA.ts +++ /dev/null @@ -1,333 +0,0 @@ -import type { FSA, FSAFeedback } from "./type"; -import { ValidationError } from "./type"; - -/* =========================== - Internal helper types -=========================== */ - -type MutableFeedback = Pick; - -type OutgoingMap = Map>; - -/* =========================== - Basic validation checks -=========================== */ - -const checkStates = ( - states: string[], - feedback: MutableFeedback -): boolean => { - if (states.length === 0) { - feedback.errors.push({ - message: "The automaton has no states.", - code: "EMPTY_STATES", - severity: "error", - }); - return false; - } - return true; -}; - -const checkAlphabet = ( - alphabet: string[], - feedback: MutableFeedback -): boolean => { - if (alphabet.length === 0) { - feedback.errors.push({ - message: "The automaton has no alphabet symbols.", - code: "EMPTY_ALPHABET", - severity: "error", - }); - return false; - } - return true; -}; - -const checkInitialState = ( - initial_state: string, - states: string[], - feedback: MutableFeedback -): boolean => { - if (!states.includes(initial_state)) { - feedback.errors.push({ - message: `Initial state "${initial_state}" is not a valid state.`, - code: "INVALID_INITIAL", - severity: "error", - highlight: { - type: "initial_state", - state_id: initial_state, - }, - }); - return false; - } - return true; -}; - -const checkAcceptStates = ( - accept_states: string[], - states: string[], - feedback: MutableFeedback -): boolean => { - let ok = true; - - for (const s of accept_states) { - if (!states.includes(s)) { - ok = false; - feedback.errors.push({ - message: `Accept state "${s}" is not a valid state.`, - code: "INVALID_ACCEPT", - severity: "error", - highlight: { - type: "accept_state", - state_id: s, - }, - }); - } - } - - return ok; -}; - -const checkTransitions = ( - transitions: string[], - states: string[], - alphabet: string[], - feedback: MutableFeedback -): OutgoingMap => { - const outgoing: OutgoingMap = new Map(); - - for (const t of transitions) { - const parts = t.split("|"); - - // Cannot safely highlight: transition not parseable - if (parts.length !== 3) { - feedback.errors.push({ - message: `Invalid transition format "${t}".`, - code: "INVALID_TRANSITION_SYMBOL", - severity: "error", - }); - continue; - } - - const [from, symbol, to] = parts; - - // Still not safely highlightable - if (!from || !symbol || !to) { - feedback.errors.push({ - message: `Transition unrecognisable "${t}".`, - code: "INVALID_SYMBOL", - severity: "error", - } as ValidationError); - continue; - } - - if (!states.includes(from)) { - feedback.errors.push({ - message: `Transition source "${from}" is invalid.`, - code: "INVALID_TRANSITION_SOURCE", - severity: "error", - highlight: { - type: "transition", - from_state: from, - to_state: to, - symbol, - }, - }); - } - - if (!states.includes(to)) { - feedback.errors.push({ - message: `Transition destination "${to}" is invalid.`, - code: "INVALID_TRANSITION_DEST", - severity: "error", - highlight: { - type: "transition", - from_state: from, - to_state: to, - symbol, - }, - }); - } - - if (!alphabet.includes(symbol)) { - feedback.errors.push({ - message: `Transition symbol "${symbol}" is invalid.`, - code: "INVALID_TRANSITION_SYMBOL", - severity: "error", - highlight: { - type: "alphabet_symbol", - symbol, - }, - }); - } - - if (!outgoing.has(from)) outgoing.set(from, []); - outgoing.get(from)!.push({ symbol, to }); - } - - return outgoing; -}; - -/* =========================== - Structural computations -=========================== */ - -const computeDeterminism = (outgoing: OutgoingMap): boolean => { - for (const edges of outgoing.values()) { - const seen = new Set(); - for (const { symbol } of edges) { - if (seen.has(symbol)) return false; - seen.add(symbol); - } - } - return true; -}; - -const computeCompleteness = ( - states: string[], - alphabet: string[], - outgoing: OutgoingMap -): boolean => { - for (const state of states) { - const edges = outgoing.get(state) ?? []; - const covered = new Set(edges.map(e => e.symbol)); - - for (const sym of alphabet) { - if (!covered.has(sym)) return false; - } - } - return true; -}; - -const computeReachableStates = ( - initial_state: string, - states: string[], - outgoing: OutgoingMap -): Set => { - const reachable = new Set(); - const stack: string[] = []; - - if (states.includes(initial_state)) { - reachable.add(initial_state); - stack.push(initial_state); - } - - while (stack.length) { - const s = stack.pop()!; - for (const { to } of outgoing.get(s) ?? []) { - if (!reachable.has(to)) { - reachable.add(to); - stack.push(to); - } - } - } - - return reachable; -}; - -const computeDeadStates = ( - states: string[], - accept_states: string[], - outgoing: OutgoingMap -): string[] => { - const reverse = new Map(); - - for (const [from, edges] of outgoing) { - for (const { to } of edges) { - if (!reverse.has(to)) reverse.set(to, []); - reverse.get(to)!.push(from); - } - } - - const canReachAccept = new Set(accept_states); - const queue = [...accept_states]; - - while (queue.length) { - const s = queue.shift()!; - for (const p of reverse.get(s) ?? []) { - if (!canReachAccept.has(p)) { - canReachAccept.add(p); - queue.push(p); - } - } - } - - return states.filter( - s => !canReachAccept.has(s) && !accept_states.includes(s) - ); -}; - -/* =========================== - Public API -=========================== */ - -export const validateFSA = (fsa: FSA | null): FSAFeedback => { - const feedback: FSAFeedback = { - summary: "", - errors: [], - warnings: [], - test_results: [], - hints: [], - }; - - if (!fsa) { - feedback.errors.push({ - message: "No automaton provided.", - code: "EVALUATION_ERROR", - severity: "error", - }); - return feedback; - } - - const { states, alphabet, transitions, initial_state, accept_states } = fsa; - - const statesOk = checkStates(states, feedback); - const alphabetOk = checkAlphabet(alphabet, feedback); - const initialOk = checkInitialState(initial_state, states, feedback); - const acceptOk = checkAcceptStates(accept_states, states, feedback); - - const outgoing = checkTransitions( - transitions, - states, - alphabet, - feedback - ); - - const is_deterministic = computeDeterminism(outgoing); - const is_complete = computeCompleteness(states, alphabet, outgoing); - - const reachable = computeReachableStates( - initial_state, - states, - outgoing - ); - const unreachable_states = states.filter(s => !reachable.has(s)); - - const dead_states = computeDeadStates( - states, - accept_states, - outgoing - ); - - feedback.structural = { - is_deterministic, - is_complete, - num_states: states.length, - num_transitions: transitions.length, - unreachable_states, - dead_states, - }; - - const isValid = - statesOk && - alphabetOk && - initialOk && - acceptOk && - feedback.errors.length === 0; - - feedback.summary = isValid - ? "The automaton is a valid finite-state automaton." - : "The automaton is not a valid finite-state automaton."; - - return feedback; -}; diff --git a/src/types/Graph/Graph.component.tsx b/src/types/Graph/Graph.component.tsx new file mode 100644 index 0000000..096f21e --- /dev/null +++ b/src/types/Graph/Graph.component.tsx @@ -0,0 +1,611 @@ +import { makeStyles } from '@styles' +import cytoscape, { Core, NodeSingular, EdgeSingular } from 'cytoscape' +import paper from 'paper' +import React, { useEffect, useRef, useState, useCallback } from 'react' + +import { Graph, Node, Edge } from './type' + +/* ----------------------------- Local Styles ----------------------------- */ +export const useLocalStyles = makeStyles()((theme) => ({ + container: { + width: '100%', + height: 600, + display: 'flex', + border: '1px solid #ddd', + fontFamily: 'sans-serif', + position: 'relative' + }, + panel: { + width: 280, + padding: theme.spacing(2), + borderRight: '1px solid #ddd', + backgroundColor: '#fafafa', + display: 'flex', + flexDirection: 'column', + gap: theme.spacing(2), + overflowY: 'auto' + }, + panelTitle: { + fontWeight: 600, + fontSize: 16, + borderBottom: '1px solid #eee', + paddingBottom: theme.spacing(1) + }, + field: { + display: 'flex', + flexDirection: 'column', + gap: theme.spacing(0.5) + }, + inputField: { + padding: '6px 8px', + border: '1px solid #ccc', + borderRadius: 4 + }, + addButton: { + padding: '6px 10px', + backgroundColor: '#fff', + border: '1px solid #ccc', + borderRadius: 4, + cursor: 'pointer' + }, + deleteButton: { + padding: '6px', + backgroundColor: '#fff1f0', + color: '#cf1322', + border: '1px solid #ffa39e', + borderRadius: 4, + cursor: 'pointer', + fontWeight: 600 + }, + cyWrapper: { + flexGrow: 1, + position: 'relative' + }, + drawCanvas: { + position: 'absolute', + top: 0, + left: 0, + width: '100%', + height: '100%', + pointerEvents: 'none', + zIndex: 10 + } +})) + +/* ----------------------------- Graph Editor ----------------------------- */ +interface GraphEditorProps { + graph: Graph + onChange: (graph: Graph) => void +} + +export const GraphEditor: React.FC = ({ graph, onChange }) => { + const { classes } = useLocalStyles() + + const cyRef = useRef(null) + const containerRef = useRef(null) + const drawCanvasRef = useRef(null) + + const [drawMode, setDrawMode] = useState(false) + const [selectedNode, setSelectedNode] = useState(null) + const [selectedEdge, setSelectedEdge] = useState(null) + const [fromNode, setFromNode] = useState(null) + const [nodeCounter, setNodeCounter] = useState(graph.nodes.length) + const [isDrawing, setIsDrawing] = useState(false) + + // Drawing state + const pathRef = useRef(null) + const startPointRef = useRef(null) + const paperInitializedRef = useRef(false) + + /* -------------------- Initialize Cytoscape -------------------- */ + useEffect(() => { + if (!containerRef.current) return + + const cy: Core = cytoscape({ + container: containerRef.current, + layout: { name: 'preset' }, + style: [ + { + selector: 'node', + style: { + label: 'data(displayLabel)', + 'text-valign': 'center', + 'text-halign': 'center', + width: 50, + height: 50, + 'background-color': '#fff', + 'border-width': 1, + 'border-color': '#555', + 'font-size': '14px' + } + }, + { + selector: 'edge', + style: { + label: 'data(label)', + 'curve-style': 'bezier', + 'target-arrow-shape': 'triangle', + 'line-color': '#555', + 'target-arrow-color': '#555', + 'font-size': '12px' + } + }, + { + selector: '.edge-source', + style: { + 'background-color': '#d4edda', + 'border-color': '#155724' + } + } + ], + }) + + cyRef.current = cy + + // Load initial nodes + graph.nodes.forEach((n: Node) => + cy.add({ + group: 'nodes', + data: { id: n.id, displayLabel: n.label ?? n.id }, + position: { x: n.x ?? 100, y: n.y ?? 100 } + }), + ) + + // Load initial edges + graph.edges.forEach((e: Edge) => + cy.add({ + group: 'edges', + data: { + id: e.id ?? `e-${e.source}-${e.target}`, + source: e.source, + target: e.target, + label: e.label ?? '' + } + }), + ) + + return () => { + cy.destroy() + } + }, [graph]) + + /* -------------------- Sync to Graph -------------------- */ + const syncToGraph = useCallback((): void => { + const cy = cyRef.current + if (!cy) return + + const nodes: Node[] = cy.nodes().map((n) => ({ + id: n.id(), + label: n.data('displayLabel') as string, + x: n.position().x, + y: n.position().y, + metadata: {}, + })) + + const edges: Edge[] = cy.edges().map((e) => ({ + id: e.id(), + source: e.source().id(), + target: e.target().id(), + label: (e.data('label') as string) ?? '', + metadata: {}, + weight: 0 + })) + + onChange({ ...graph, nodes, edges }) + }, [graph, onChange]) + + /* -------------------- Add Node -------------------- */ + const addNode = useCallback((): void => { + const cy = cyRef.current + if (!cy) return + + const id = `n${nodeCounter}` + cy.add({ + group: 'nodes', + data: { id, displayLabel: id }, + position: { + x: 100 + Math.random() * 300, + y: 100 + Math.random() * 300 + } + }) + setNodeCounter(nodeCounter + 1) + syncToGraph() + }, [nodeCounter, syncToGraph]) + + const addNodeWithPos = useCallback((x: number, y: number): void => { + const cy = cyRef.current + if (!cy) return + + const id = `n${nodeCounter}` + cy.add({ + group: 'nodes', + data: { id, displayLabel: id }, + position: { x, y } + }) + setNodeCounter(nodeCounter + 1) + syncToGraph() + }, [nodeCounter, syncToGraph]) + + /* -------------------- Paper.js Setup -------------------- */ + useEffect(() => { + const canvas = drawCanvasRef.current + if (!canvas) return + + // Initialize Paper.js only once + if (!paperInitializedRef.current) { + paper.setup(canvas) + paperInitializedRef.current = true + } + + // Setup canvas size + const updateCanvasSize = () => { + const container = containerRef.current + if (!container || !canvas) return + + const { width, height } = container.getBoundingClientRect() + + // Set canvas dimensions + canvas.width = width + canvas.height = height + + // Update Paper.js view + paper.view.viewSize = new paper.Size(width, height) + paper.view.update() + } + + updateCanvasSize() + + // Handle resizing + const resizeObserver = new ResizeObserver(updateCanvasSize) + if (containerRef.current) { + resizeObserver.observe(containerRef.current) + } + + return () => { + resizeObserver.disconnect() + } + }, []) + + /* -------------------- Drawing Handlers -------------------- */ + const handlePointerDown = useCallback((e: React.PointerEvent) => { + if (!drawMode) return + + e.preventDefault() + e.stopPropagation() + + const rect = drawCanvasRef.current?.getBoundingClientRect() + if (!rect || !drawCanvasRef.current) return + + const x = e.clientX - rect.left + const y = e.clientY - rect.top + + // Clear any existing paths from previous drawings + if (pathRef.current) { + pathRef.current.remove() + } + + // Create new path + pathRef.current = new paper.Path() + pathRef.current.strokeColor = new paper.Color('red') + pathRef.current.strokeWidth = 3 + + startPointRef.current = new paper.Point(x, y) + pathRef.current.add(startPointRef.current) + + setIsDrawing(true) + }, [drawMode]) + + const handlePointerMove = useCallback((e: React.PointerEvent) => { + if (!drawMode || !isDrawing || !pathRef.current) return + + e.preventDefault() + e.stopPropagation() + + const rect = drawCanvasRef.current?.getBoundingClientRect() + if (!rect) return + + const x = e.clientX - rect.left + const y = e.clientY - rect.top + + pathRef.current.add(new paper.Point(x, y)) + // console.log(pathRef) + }, [drawMode, isDrawing]) + + const handlePointerUp = useCallback((e: React.PointerEvent) => { + if (!drawMode || !isDrawing || !pathRef.current || !startPointRef.current) { + setIsDrawing(false) + return + } + + e.preventDefault() + e.stopPropagation() + + const rect = drawCanvasRef.current?.getBoundingClientRect() + if (!rect) return + + const endX = e.clientX - rect.left + const endY = e.clientY - rect.top + const endPoint = new paper.Point(endX, endY) + + // Analyze the drawing + const bounds = pathRef.current.bounds + const dx = bounds.width + const dy = bounds.height + const diameter = Math.max(dx, dy) + const strokeLength = pathRef.current.length + const distance = startPointRef.current.getDistance(endPoint) + + console.log('Drawing analysis:', { + diameter, + strokeLength, + distance, + startPoint: startPointRef.current, + endPoint, + bounds + }) + + // Circle detection - relaxed criteria + const circumference = Math.PI * diameter + const isCircle = diameter > 20 && + strokeLength > circumference * 0.3 && + distance < diameter * 0.5 + + if (isCircle) { + console.log('Detected circle, adding node at:', bounds.center) + // Add node at circle center + addNodeWithPos(bounds.center.x, bounds.center.y) + } else { + console.log('Detected line, finding closest nodes') + // Find closest nodes to start and end + const cy = cyRef.current + if (cy) { + const findClosestNode = ( + pointX: number, + pointY: number + ): NodeSingular | null => { + let minDist = Infinity + let closestNode: NodeSingular | null = null + + cy.nodes().forEach((node) => { + const pos = node.position() + const dist = Math.hypot(pos.x - pointX, pos.y - pointY) + if (dist < minDist) { + minDist = dist + closestNode = node + } + }) + + return minDist < 75 ? closestNode : null // Increased threshold to 75 + } + + const startNode = findClosestNode(startPointRef.current.x, startPointRef.current.y) + const endNode = findClosestNode(endPoint.x, endPoint.y) + + console.log('Found nodes:', { + startNode: startNode?.id(), + endNode: endNode?.id() + }) + + if (startNode !== null && endNode !== null && startNode.id() !== endNode.id()) { + console.log('Adding edge between:', startNode.id(), endNode.id()) + cy.add({ + group: 'edges', + data: { + id: `e-${startNode.id()}-${endNode.id()}-${Date.now()}`, + source: startNode.id(), + target: endNode.id(), + label: '', + }, + }) + syncToGraph() + } + } + } + + // Don't remove the path immediately - keep it visible for a moment + // setTimeout(() => { + if (pathRef.current) { + pathRef.current.remove() + pathRef.current = null + } + startPointRef.current = null + setIsDrawing(false) + // }, 500) // Keep stroke visible for 500ms + }, [drawMode, isDrawing, addNodeWithPos, syncToGraph]) + + const handlePointerLeave = useCallback(() => { + if (pathRef.current) { + pathRef.current.remove() + pathRef.current = null + } + startPointRef.current = null + setIsDrawing(false) + }, []) + + /* -------------------- Cytoscape Selection Handlers -------------------- */ + useEffect(() => { + const cy = cyRef.current + if (!cy) return + + const tapNode = (e: cytoscape.EventObject) => { + const node = e.target as NodeSingular + + if (drawMode) { + if (!fromNode) { + setFromNode(node.id()) + node.addClass('edge-source') + } else { + cy.add({ + group: 'edges', + data: { + id: `e-${fromNode}-${node.id()}-${Date.now()}`, + source: fromNode, + target: node.id(), + label: '' + } + }) + cy.nodes().removeClass('edge-source') + setDrawMode(false) + setFromNode(null) + syncToGraph() + } + return + } + + setSelectedNode(node) + setSelectedEdge(null) + } + + const tapEdge = (e: cytoscape.EventObject) => { + setSelectedEdge(e.target as EdgeSingular) + setSelectedNode(null) + } + + const tapBlank = () => { + setSelectedNode(null) + setSelectedEdge(null) + } + + cy.on('tap', 'node', tapNode) + cy.on('tap', 'edge', tapEdge) + cy.on('tap', tapBlank) + + return () => { + cy.off('tap', 'node', tapNode) + cy.off('tap', 'edge', tapEdge) + cy.off('tap', tapBlank) + } + }, [drawMode, fromNode, syncToGraph]) + + /* -------------------- Render -------------------- */ + return ( +
+ {/* -------------------- Item Properties Panel -------------------- */} +
+
Item Properties
+ + + + + + + + {drawMode && ( +
+ Draw Mode Active:
+ • Draw a circle to create a node
+ • Draw a line between nodes to create an edge
+ • Or click nodes to connect them +
+ )} + + {selectedNode && ( +
+ + { + selectedNode.data('displayLabel', e.target.value); + syncToGraph() + }} + /> +
+ )} + + {selectedEdge && ( +
+ + { + selectedEdge.data('label', e.target.value); + syncToGraph() + }} + /> +
+ )} + + {(selectedNode || selectedEdge) && ( + + )} +
+ + {/* -------------------- Cytoscape + Paper Canvas -------------------- */} +
+
+ +
+
+ ) +} \ No newline at end of file diff --git a/src/types/Graph/components/ConfigPanel.tsx b/src/types/Graph/components/ConfigPanel.tsx new file mode 100644 index 0000000..893d927 --- /dev/null +++ b/src/types/Graph/components/ConfigPanel.tsx @@ -0,0 +1,77 @@ +// import React from 'react' + +// import { FSAConfig } from '../type' + +// interface EvaluationConfigPanelProps { +// config: FSAConfig +// setConfig: (config: FSAConfig) => void +// configOpen: boolean +// setConfigOpen: React.Dispatch> +// classes: Record +// } + +// export default function EvaluationConfigPanel({ +// config, +// setConfig, +// configOpen, +// setConfigOpen, +// classes +// }: EvaluationConfigPanelProps) { +// return ( +//
+//
setConfigOpen((o) => !o)} +// > +// Evaluation Config +// {configOpen ? '▾' : '▸'} +//
+ +// {configOpen && ( +//
+// {Object.entries(config).map(([key, value]) => ( +//
+// + +// {typeof value === 'boolean' ? ( +// +// setConfig({ +// ...config, +// [key]: e.target.checked, +// }) +// } +// /> +// ) : typeof value === 'number' ? ( +// +// setConfig({ +// ...config, +// [key]: Number(e.target.value), +// }) +// } +// /> +// ) : ( +// +// setConfig({ +// ...config, +// [key]: e.target.value, +// }) +// } +// /> +// )} +//
+// ))} +//
+// )} +//
+// ) +// } diff --git a/src/types/Graph/components/GraphFeedbackPanel.tsx b/src/types/Graph/components/GraphFeedbackPanel.tsx new file mode 100644 index 0000000..aeef5dd --- /dev/null +++ b/src/types/Graph/components/GraphFeedbackPanel.tsx @@ -0,0 +1,256 @@ +// import React, { useMemo } from 'react' + +// import { +// CheckPhase, +// FSAFeedbackSchema, +// type FSAFeedback, +// } from '../type' + +// interface FSAFeedbackPanelProps { +// feedback: FSAFeedback | null +// phase: CheckPhase +// } + +// export function FSAFeedbackPanel({ +// feedback, +// phase, +// }: FSAFeedbackPanelProps) { + +// const parsed = useMemo( +// () => FSAFeedbackSchema.safeParse(feedback), +// [feedback], +// ) + +// if (!feedback || !parsed.success) { +// return ( +//
+// {phase === CheckPhase.PreviewError +// ? 'Preview errors found' +// : 'No feedback yet'} +//
+// ) +// } + +// const safeFeedback = parsed.data + +// return ( +//
+//
+// { +// phase == CheckPhase.PreviewError ? "Errors in Preview" : "Errors in Evaluation" +// } +//
+// {/* ================= Summary ================= */} +// {safeFeedback.summary && ( +//
+// {safeFeedback.summary} +//
+// )} + +// {/* ================= Errors ================= */} +// {safeFeedback.errors.length > 0 && ( +// +// )} + +// {/* ================= Warnings ================= */} +// {safeFeedback.warnings.length > 0 && ( +// +// )} + +// {/* ================= Structural Info ================= */} +// {safeFeedback.structural && ( +//
+// +// +// +// + +// {safeFeedback.structural.unreachable_states.length > 0 && ( +// +// )} + +// {safeFeedback.structural.dead_states.length > 0 && ( +// +// )} +//
+// )} + +// {/* ================= Language ================= */} +// {safeFeedback.language && ( +//
+// + +// {!safeFeedback.language.are_equivalent && +// safeFeedback.language.counterexample && ( +// +// )} +//
+// )} + +// {/* ================= Test Results ================= */} +// {safeFeedback.test_results.length > 0 && ( +//
+// {safeFeedback.test_results.map((t, i) => ( +//
+// {JSON.stringify(t.input)} +// {t.passed ? '✓' : '✗'} +//
+// ))} +//
+// )} + +// {/* ================= Hints ================= */} +// {safeFeedback.hints.length > 0 && ( +//
+//
    +// {safeFeedback.hints.map((h, i) => ( +//
  • {h}
  • +// ))} +//
+//
+// )} +//
+// ) +// } + +// /* =========================== +// Helper components +// =========================== */ + +// function FeedbackSection({ +// title, +// items, +// accent, +// }: { +// title: string +// items: any[] +// accent: string +// }) { +// return ( +//
+//
+// {items.map((e, i) => ( +//
+//
{e.message}
+ +//
+// {e.code} · {e.severity} +//
+ +// {e.suggestion && ( +//
+// 💡 {e.suggestion} +//
+// )} +//
+// ))} +//
+//
+// ) +// } + + +// function Section({ +// title, +// children, +// }: { +// title: string +// children: React.ReactNode +// }) { +// return ( +//
+//
+// {title} +//
+// {children} +//
+// ) +// } + +// function KV({ label, value }: { label: string; value: React.ReactNode }) { +// return ( +//
+// {label}: +// {value} +//
+// ) +// } + +// function bool(v: boolean) { +// return v ? 'Yes' : 'No' +// } diff --git a/src/types/FSA/components/ItemPropertiesPanel.tsx b/src/types/Graph/components/ItemPropertiesPanel.tsx similarity index 51% rename from src/types/FSA/components/ItemPropertiesPanel.tsx rename to src/types/Graph/components/ItemPropertiesPanel.tsx index 9bd3fdf..787b55d 100644 --- a/src/types/FSA/components/ItemPropertiesPanel.tsx +++ b/src/types/Graph/components/ItemPropertiesPanel.tsx @@ -1,15 +1,13 @@ import type { Core, NodeSingular, EdgeSingular } from 'cytoscape' import React from 'react' -import { CheckPhase, FSA, FSAFeedback } from '../type' - -import { FSAFeedbackPanel } from './FSAFeedbackPanel' +import { Graph } from '../type' interface ItemPropertiesPanelProps { cyRef: React.MutableRefObject classes: Record - addState: () => void + addNode: () => void drawMode: boolean setDrawMode: React.Dispatch> @@ -21,19 +19,16 @@ interface ItemPropertiesPanelProps { selectedEdge: EdgeSingular | null setSelectedEdge: (e: EdgeSingular | null) => void - answer: FSA - handleChange: (fsa: FSA) => void + graph: Graph + handleChange: (graph: Graph) => void syncToBackend: () => void - feedback: FSAFeedback | null - previewFeedback: FSAFeedback | null - phase: CheckPhase } export default function ItemPropertiesPanel({ cyRef, classes, - addState, + addNode, drawMode, setDrawMode, setFromNode, @@ -41,20 +36,17 @@ export default function ItemPropertiesPanel({ setSelectedNode, selectedEdge, setSelectedEdge, - answer, + graph, handleChange, syncToBackend, - feedback, - previewFeedback, - phase }: ItemPropertiesPanelProps): JSX.Element { return (
Item Properties
{/* -------------------- Actions -------------------- */} - {/* -------------------- Node Properties -------------------- */} {selectedNode && ( - <> -
- - { - selectedNode.data('displayLabel', e.target.value) - // syncToBackend() - }} - /> -
- - {/* Initial State (unique) */} -
- { - handleChange({ - ...answer, - initial_state: e.target.checked ? selectedNode.id() : answer.initial_state, - }) - // syncToBackend() - }} - /> - -
- - {/* Accepting State (multiple allowed) */} -
- { - handleChange({ - ...answer, - accept_states: e.target.checked - ? [...answer.accept_states, selectedNode.id()] - : answer.accept_states.filter( - (id) => id !== selectedNode.id(), - ), - }) - // syncToBackend() - }} - /> - -
- +
+ + { + selectedNode.data('displayLabel', e.target.value) + syncToBackend() + }} + /> +
)} {/* -------------------- Edge Properties -------------------- */} {selectedEdge && (
- + )} -
) } diff --git a/src/types/Graph/index.tsx b/src/types/Graph/index.tsx new file mode 100644 index 0000000..e121d67 --- /dev/null +++ b/src/types/Graph/index.tsx @@ -0,0 +1,120 @@ +import { + BaseResponseAreaProps, + BaseResponseAreaWizardProps, +} from '../base-props.type' +import { ResponseAreaTub } from '../response-area-tub' + +import { GraphEditor } from './Graph.component' +import { Graph, CompressedGraph, CompressedGraphSchema } from './type' +import { Edge, Node } from './type' + +export class GraphResponseAreaTub extends ResponseAreaTub { + public readonly responseType = 'HANDDRAWNGRAPH' + public readonly displayWideInput = true + + protected answerSchema = CompressedGraphSchema + + protected answer: CompressedGraph = { + nodes: [], + edges: [], + directed: false, + weighted: false, + multigraph: false, + name:'', + metadata: {}, + } + + private previewFeedback: any = null + + public readonly delegateFeedback = false + public readonly delegateLivePreview = true + + initWithConfig = () => {} + + /* -------------------- Custom Check -------------------- */ + customCheck(): void { + if (this.previewFeedback) { + throw new Error('preview failed') + } + this.previewFeedback = null + } + + /* -------------------- Input -------------------- */ + InputComponent = (props: BaseResponseAreaProps) => { + const parsed = this.answerSchema.safeParse(props.answer) + const compressedAnswer = parsed.success ? parsed.data : this.answer + + const graph: Graph = { + nodes: compressedAnswer.nodes.map((e) => JSON.parse(e)), + edges: compressedAnswer.edges.map((e) => JSON.parse(e)), + directed: compressedAnswer.directed, + weighted: compressedAnswer.weighted, + multigraph: compressedAnswer.multigraph, + name: compressedAnswer.name, + metadata: compressedAnswer.metadata, + } + + return ( + { + const compressed: CompressedGraph = { + ...compressedAnswer, + nodes: val.nodes.map((n) => JSON.stringify(n)), + edges: val.edges.map((e) => JSON.stringify(e)), + directed: val.directed, + weighted: val.weighted, + multigraph: val.multigraph, + name: val.name, + metadata: val.metadata, + } + + // 🔑 Keep instance state valid + this.answer = compressed + + props.handleChange(compressed) + }} + /> + ) + } + + /* -------------------- Wizard -------------------- */ + WizardComponent = (props: BaseResponseAreaWizardProps) => { + const graph: Graph = { + nodes: this.answer.nodes.map((e) => JSON.parse(e)), + edges: this.answer.edges.map((e) => JSON.parse(e)), + directed: this.answer.directed, + weighted: this.answer.weighted, + multigraph: this.answer.multigraph, + name: this.answer.name, + metadata: this.answer.metadata, + } + + return ( + { + const compressed: CompressedGraph = { + ...this.answer, + nodes: val.nodes.map((n: Node) => JSON.stringify(n)), + edges: val.edges.map((e: Edge) => JSON.stringify(e)), + directed: val.directed, + weighted: val.weighted, + multigraph: val.multigraph, + name: val.name, + metadata: val.metadata, + } + + // 🔑 Wizard MUST update instance state first + this.answer = compressed + + // 🔑 Wizard MUST emit full payload + props.handleChange({ + responseType: this.responseType, + answer: compressed, + }) + }} + /> + ) + } +} diff --git a/src/types/FSA/styles.ts b/src/types/Graph/styles.ts similarity index 100% rename from src/types/FSA/styles.ts rename to src/types/Graph/styles.ts diff --git a/src/types/Graph/type.ts b/src/types/Graph/type.ts new file mode 100644 index 0000000..f13ac74 --- /dev/null +++ b/src/types/Graph/type.ts @@ -0,0 +1,75 @@ +import { z } from "zod"; + +// ----------------------------- +// Node Schema +// ----------------------------- +export const NodeSchema = z.object({ + id: z.string(), + label: z.string().optional(), + x: z.number().optional(), + y: z.number().optional(), + partition: z.union([z.literal(0), z.literal(1)]).optional(), + color: z.number().optional(), + weight: z.number().optional(), + metadata: z.record(z.any()).optional().default({}), +}); + +export type Node = z.infer + +// ----------------------------- +// Edge Schema +// ----------------------------- +export const EdgeSchema = z.object({ + source: z.string(), + target: z.string(), + weight: z.number().optional().default(1), + capacity: z.number().optional(), + flow: z.number().optional(), + label: z.string().optional(), + id: z.string().optional(), + color: z.string().optional(), + metadata: z.record(z.any()).optional().default({}), +}); + +export type Edge = z.infer + +// ----------------------------- +// Graph Schema +// ----------------------------- +export const GraphSchema = z.object({ + nodes: z.array(NodeSchema), + edges: z.array(EdgeSchema).default([]), + directed: z.boolean().default(false), + weighted: z.boolean().default(false), + multigraph: z.boolean().default(false), + name: z.string().optional(), + metadata: z.record(z.any()).optional().default({}), +}); + +export type Graph = z.infer + +// ----------------------------- +// Compressed Graph: JSON-stringified nodes/edges +// ----------------------------- +export const CompressedGraphSchema = z.object({ + nodes: z.array(z.string()), // JSON.stringify(nodes) + edges: z.array(z.string()), // JSON.stringify(edges) + directed: z.boolean().default(false), + weighted: z.boolean().default(false), + multigraph: z.boolean().default(false), + name: z.string().optional(), + metadata: z.record(z.any()).optional().default({}), +}); + +export type CompressedGraph = z.infer + +// ----------------------------- +// Example: compress function +// ----------------------------- +export function compressGraph(graph: z.infer) { + return { + ...graph, + nodes: JSON.stringify(graph.nodes), + edges: JSON.stringify(graph.edges), + }; +} diff --git a/src/types/Graph/utils.ts b/src/types/Graph/utils.ts new file mode 100644 index 0000000..ac41a20 --- /dev/null +++ b/src/types/Graph/utils.ts @@ -0,0 +1,13 @@ +// import { CheckPhase, FSAFeedback } from "./type" + +// export function mergeFeedback( +// feedback: FSAFeedback | null, +// previewFeedback: FSAFeedback | null, +// phase: CheckPhase, +// ): FSAFeedback | null { +// if (phase === CheckPhase.PreviewError) { +// return previewFeedback +// } + +// return feedback +// } \ No newline at end of file diff --git a/src/types/index.ts b/src/types/index.ts index 63e11e1..4afea89 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -3,7 +3,7 @@ import { z } from 'zod' import { CodeResponseAreaTub } from './Code' import { EssayResponseAreaTub } from './Essay' -import { FSAResponseAreaTub } from './FSA' +import { GraphResponseAreaTub } from './Graph' import { MatrixResponseAreaTub } from './Matrix' import { MultipleChoiceResponseAreaTub } from './MultipleChoice' import { NumberResponseAreaTub } from './NumberInput' @@ -100,8 +100,8 @@ const createReponseAreaTub = (type: string): ResponseAreaTub => { return new EssayResponseAreaTub() case 'CODE': return new CodeResponseAreaTub() - case 'FSA': - return new FSAResponseAreaTub() + case 'HANDDRAWNGRAPH': + return new GraphResponseAreaTub() case 'VOID': return new VoidResponseAreaTub() default: diff --git a/yarn.lock b/yarn.lock index 62bcefd..628dd87 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5498,6 +5498,11 @@ pako@~1.0.2: resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== +paper@^0.12.18: + version "0.12.18" + resolved "https://registry.npmmirror.com/paper/-/paper-0.12.18.tgz#e024056217a35c36e2b5fda4629310fdc7025c91" + integrity sha512-ZSLIEejQTJZuYHhSSqAf4jXOnii0kPhCJGAnYAANtdS72aNwXJ9cP95tZHgq1tnNpvEwgQhggy+4OarviqTCGw== + parent-module@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" From 3117859d954354547fbed3ceba00e3aba7602a00 Mon Sep 17 00:00:00 2001 From: Harry Date: Tue, 17 Feb 2026 00:02:17 +0000 Subject: [PATCH 14/36] implemented feedback panel --- src/types/Graph/Graph.component.tsx | 17 +- .../Graph/components/GraphFeedbackPanel.tsx | 427 +++++++----------- src/types/Graph/index.tsx | 49 +- src/types/Graph/type.ts | 33 ++ src/types/Graph/validateGraph.ts | 104 +++++ src/types/index.ts | 3 +- 6 files changed, 369 insertions(+), 264 deletions(-) create mode 100644 src/types/Graph/validateGraph.ts diff --git a/src/types/Graph/Graph.component.tsx b/src/types/Graph/Graph.component.tsx index 096f21e..b11115e 100644 --- a/src/types/Graph/Graph.component.tsx +++ b/src/types/Graph/Graph.component.tsx @@ -3,7 +3,8 @@ import cytoscape, { Core, NodeSingular, EdgeSingular } from 'cytoscape' import paper from 'paper' import React, { useEffect, useRef, useState, useCallback } from 'react' -import { Graph, Node, Edge } from './type' +import { GraphFeedbackPanel } from './components/GraphFeedbackPanel' +import { Graph, Node, Edge, GraphFeedback, CheckPhase } from './type' /* ----------------------------- Local Styles ----------------------------- */ export const useLocalStyles = makeStyles()((theme) => ({ @@ -76,9 +77,16 @@ export const useLocalStyles = makeStyles()((theme) => ({ interface GraphEditorProps { graph: Graph onChange: (graph: Graph) => void + feedback?: GraphFeedback | null + phase?: CheckPhase } -export const GraphEditor: React.FC = ({ graph, onChange }) => { +export const GraphEditor: React.FC = ({ + graph, + onChange, + feedback = null, + phase = CheckPhase.Idle +}) => { const { classes } = useLocalStyles() const cyRef = useRef(null) @@ -578,6 +586,11 @@ export const GraphEditor: React.FC = ({ graph, onChange }) => Delete Selected )} + + {/* -------------------- Validation Feedback Panel -------------------- */} +
+ +
{/* -------------------- Cytoscape + Paper Canvas -------------------- */} diff --git a/src/types/Graph/components/GraphFeedbackPanel.tsx b/src/types/Graph/components/GraphFeedbackPanel.tsx index aeef5dd..8d89634 100644 --- a/src/types/Graph/components/GraphFeedbackPanel.tsx +++ b/src/types/Graph/components/GraphFeedbackPanel.tsx @@ -1,256 +1,171 @@ -// import React, { useMemo } from 'react' - -// import { -// CheckPhase, -// FSAFeedbackSchema, -// type FSAFeedback, -// } from '../type' - -// interface FSAFeedbackPanelProps { -// feedback: FSAFeedback | null -// phase: CheckPhase -// } - -// export function FSAFeedbackPanel({ -// feedback, -// phase, -// }: FSAFeedbackPanelProps) { - -// const parsed = useMemo( -// () => FSAFeedbackSchema.safeParse(feedback), -// [feedback], -// ) - -// if (!feedback || !parsed.success) { -// return ( -//
-// {phase === CheckPhase.PreviewError -// ? 'Preview errors found' -// : 'No feedback yet'} -//
-// ) -// } - -// const safeFeedback = parsed.data - -// return ( -//
-//
-// { -// phase == CheckPhase.PreviewError ? "Errors in Preview" : "Errors in Evaluation" -// } -//
-// {/* ================= Summary ================= */} -// {safeFeedback.summary && ( -//
-// {safeFeedback.summary} -//
-// )} - -// {/* ================= Errors ================= */} -// {safeFeedback.errors.length > 0 && ( -// -// )} - -// {/* ================= Warnings ================= */} -// {safeFeedback.warnings.length > 0 && ( -// -// )} - -// {/* ================= Structural Info ================= */} -// {safeFeedback.structural && ( -//
-// -// -// -// - -// {safeFeedback.structural.unreachable_states.length > 0 && ( -// -// )} - -// {safeFeedback.structural.dead_states.length > 0 && ( -// -// )} -//
-// )} - -// {/* ================= Language ================= */} -// {safeFeedback.language && ( -//
-// - -// {!safeFeedback.language.are_equivalent && -// safeFeedback.language.counterexample && ( -// -// )} -//
-// )} - -// {/* ================= Test Results ================= */} -// {safeFeedback.test_results.length > 0 && ( -//
-// {safeFeedback.test_results.map((t, i) => ( -//
-// {JSON.stringify(t.input)} -// {t.passed ? '✓' : '✗'} -//
-// ))} -//
-// )} - -// {/* ================= Hints ================= */} -// {safeFeedback.hints.length > 0 && ( -//
-//
    -// {safeFeedback.hints.map((h, i) => ( -//
  • {h}
  • -// ))} -//
-//
-// )} -//
-// ) -// } - -// /* =========================== -// Helper components -// =========================== */ - -// function FeedbackSection({ -// title, -// items, -// accent, -// }: { -// title: string -// items: any[] -// accent: string -// }) { -// return ( -//
-//
-// {items.map((e, i) => ( -//
-//
{e.message}
- -//
-// {e.code} · {e.severity} -//
- -// {e.suggestion && ( -//
-// 💡 {e.suggestion} -//
-// )} -//
-// ))} -//
-//
-// ) -// } - - -// function Section({ -// title, -// children, -// }: { -// title: string -// children: React.ReactNode -// }) { -// return ( -//
-//
-// {title} -//
-// {children} -//
-// ) -// } - -// function KV({ label, value }: { label: string; value: React.ReactNode }) { -// return ( -//
-// {label}: -// {value} -//
-// ) -// } - -// function bool(v: boolean) { -// return v ? 'Yes' : 'No' -// } +import * as React from 'react' +import { useMemo } from 'react' + +import { + CheckPhase, + GraphFeedbackSchema, + type GraphFeedback, +} from '../type' + +interface GraphFeedbackPanelProps { + feedback: GraphFeedback | null + phase: CheckPhase +} + +export function GraphFeedbackPanel({ + feedback, + phase, +}: GraphFeedbackPanelProps) { + + const parsed = useMemo( + () => GraphFeedbackSchema.safeParse(feedback), + [feedback], + ) + + if (!feedback || !parsed.success) { + return ( +
+ {phase === CheckPhase.PreviewError + ? 'Preview errors found' + : 'No feedback yet'} +
+ ) + } + + const safeFeedback = parsed.data + + // Separate errors and warnings + const errors = safeFeedback.errors.filter(e => e.type === 'error') + const warnings = safeFeedback.errors.filter(e => e.type === 'warning') + + return ( +
+
+ { + phase === CheckPhase.PreviewError ? "Errors in Preview" : "Errors in Evaluation" + } +
+ + {/* ================= Errors ================= */} + {errors.length > 0 && ( + + )} + + {/* ================= Warnings ================= */} + {warnings.length > 0 && ( + + )} + + {/* ================= Validation Status ================= */} + {safeFeedback.valid && errors.length === 0 && ( +
+ ✓ Graph structure is valid +
+ )} +
+ ) +} + +/* =========================== + Helper components +=========================== */ + +function FeedbackSection({ + title, + items, + accent, +}: { + title: string + items: any[] + accent: string +}) { + return ( +
+
+ {items.map((e, i) => ( +
+
{e.message}
+ + {e.field && ( +
+ Field: {e.field} +
+ )} +
+ ))} +
+
+ ) +} + + +function Section({ + title, + children, +}: { + title: string + children: React.ReactNode +}) { + return ( +
+
+ {title} +
+ {children} +
+ ) +} diff --git a/src/types/Graph/index.tsx b/src/types/Graph/index.tsx index e121d67..e8c0b5f 100644 --- a/src/types/Graph/index.tsx +++ b/src/types/Graph/index.tsx @@ -5,8 +5,9 @@ import { import { ResponseAreaTub } from '../response-area-tub' import { GraphEditor } from './Graph.component' -import { Graph, CompressedGraph, CompressedGraphSchema } from './type' +import { Graph, CompressedGraph, CompressedGraphSchema, GraphFeedback, CheckPhase } from './type' import { Edge, Node } from './type' +import { validateGraph } from './validateGraph' export class GraphResponseAreaTub extends ResponseAreaTub { public readonly responseType = 'HANDDRAWNGRAPH' @@ -24,7 +25,8 @@ export class GraphResponseAreaTub extends ResponseAreaTub { metadata: {}, } - private previewFeedback: any = null + private previewFeedback: GraphFeedback | null = null + private phase: CheckPhase = CheckPhase.Idle public readonly delegateFeedback = false public readonly delegateLivePreview = true @@ -33,17 +35,39 @@ export class GraphResponseAreaTub extends ResponseAreaTub { /* -------------------- Custom Check -------------------- */ customCheck(): void { + // Block submission if preview validation fails if (this.previewFeedback) { throw new Error('preview failed') } + + // Preview passed — ensure it's cleared this.previewFeedback = null } /* -------------------- Input -------------------- */ InputComponent = (props: BaseResponseAreaProps) => { + // Ensure a valid compressed graph answer const parsed = this.answerSchema.safeParse(props.answer) const compressedAnswer = parsed.success ? parsed.data : this.answer + /* ---------- Extract submitted feedback ---------- */ + const submittedFeedback: GraphFeedback | null = (() => { + const raw = props.feedback?.feedback + if (!raw) return null + + try { + const jsonPart = raw.split('
')[1]?.trim() + if (!jsonPart) return null + return JSON.parse(jsonPart) + } catch { + return null + } + })() + + /* ---------- Effective feedback ---------- */ + const effectiveFeedback = this.previewFeedback ?? submittedFeedback + + // Decompress to Graph format for editor const graph: Graph = { nodes: compressedAnswer.nodes.map((e) => JSON.parse(e)), edges: compressedAnswer.edges.map((e) => JSON.parse(e)), @@ -57,6 +81,8 @@ export class GraphResponseAreaTub extends ResponseAreaTub { return ( { const compressed: CompressedGraph = { ...compressedAnswer, @@ -69,9 +95,20 @@ export class GraphResponseAreaTub extends ResponseAreaTub { metadata: val.metadata, } - // 🔑 Keep instance state valid + // Keep instance state valid this.answer = compressed + // Validate the graph + const preview = validateGraph(val) + + if (preview.errors.filter((e) => e.type === 'error').length > 0) { + this.previewFeedback = preview + this.phase = CheckPhase.PreviewError + } else { + this.previewFeedback = null + this.phase = CheckPhase.Idle + } + props.handleChange(compressed) }} /> @@ -93,6 +130,8 @@ export class GraphResponseAreaTub extends ResponseAreaTub { return ( { const compressed: CompressedGraph = { ...this.answer, @@ -105,10 +144,10 @@ export class GraphResponseAreaTub extends ResponseAreaTub { metadata: val.metadata, } - // 🔑 Wizard MUST update instance state first + // Wizard must update instance state first this.answer = compressed - // 🔑 Wizard MUST emit full payload + // Wizard must emit full payload props.handleChange({ responseType: this.responseType, answer: compressed, diff --git a/src/types/Graph/type.ts b/src/types/Graph/type.ts index f13ac74..da9579a 100644 --- a/src/types/Graph/type.ts +++ b/src/types/Graph/type.ts @@ -73,3 +73,36 @@ export function compressGraph(graph: z.infer) { edges: JSON.stringify(graph.edges), }; } + +// ----------------------------- +// Validation & Feedback Types +// ----------------------------- +export enum CheckPhase { + Idle = 'idle', + PreviewError = 'preview_error', + Evaluated = 'evaluated', +} + +export interface ValidationError { + type: 'error' | 'warning' + message: string + field?: string +} + +export interface GraphFeedback { + valid: boolean + errors: ValidationError[] + phase: CheckPhase +} + +export const GraphFeedbackSchema = z.object({ + valid: z.boolean(), + errors: z.array( + z.object({ + type: z.enum(['error', 'warning']), + message: z.string(), + field: z.string().optional(), + }) + ), + phase: z.nativeEnum(CheckPhase), +}) diff --git a/src/types/Graph/validateGraph.ts b/src/types/Graph/validateGraph.ts new file mode 100644 index 0000000..ee765f1 --- /dev/null +++ b/src/types/Graph/validateGraph.ts @@ -0,0 +1,104 @@ +import { Graph } from './type' +import { GraphFeedback, ValidationError, CheckPhase } from './type' + +/** + * Validates a graph for common errors + */ +export function validateGraph(graph: Graph): GraphFeedback { + const errors: ValidationError[] = [] + + // Check if graph has nodes + if (graph.nodes.length === 0) { + errors.push({ + type: 'error', + message: 'Graph must have at least one node', + field: 'nodes', + }) + } + + // Check for duplicate node IDs + const nodeIds = new Set() + const duplicateIds = new Set() + + graph.nodes.forEach((node) => { + if (nodeIds.has(node.id)) { + duplicateIds.add(node.id) + } + nodeIds.add(node.id) + }) + + if (duplicateIds.size > 0) { + errors.push({ + type: 'error', + message: `Duplicate node IDs found: ${Array.from(duplicateIds).join(', ')}`, + field: 'nodes', + }) + } + + // Check for edges with invalid node references + graph.edges.forEach((edge, index) => { + if (!nodeIds.has(edge.source)) { + errors.push({ + type: 'error', + message: `Edge ${index + 1}: source node "${edge.source}" does not exist`, + field: 'edges', + }) + } + if (!nodeIds.has(edge.target)) { + errors.push({ + type: 'error', + message: `Edge ${index + 1}: target node "${edge.target}" does not exist`, + field: 'edges', + }) + } + }) + + // Check for self-loops (if not allowed) + const selfLoops = graph.edges.filter((edge) => edge.source === edge.target) + if (selfLoops.length > 0) { + errors.push({ + type: 'warning', + message: `Graph contains ${selfLoops.length} self-loop(s)`, + field: 'edges', + }) + } + + // Check if weighted graph has weights on all edges + if (graph.weighted) { + const edgesWithoutWeight = graph.edges.filter( + (edge) => edge.weight === undefined || edge.weight === null + ) + if (edgesWithoutWeight.length > 0) { + errors.push({ + type: 'warning', + message: `${edgesWithoutWeight.length} edge(s) are missing weights in a weighted graph`, + field: 'edges', + }) + } + } + + // Check for disconnected nodes (nodes with no edges) + const connectedNodes = new Set() + graph.edges.forEach((edge) => { + connectedNodes.add(edge.source) + connectedNodes.add(edge.target) + }) + + const disconnectedNodes = graph.nodes.filter( + (node) => !connectedNodes.has(node.id) + ) + + if (disconnectedNodes.length > 0 && graph.edges.length > 0) { + errors.push({ + type: 'warning', + message: `${disconnectedNodes.length} node(s) are not connected to the graph`, + field: 'nodes', + }) + } + + return { + valid: errors.filter((e) => e.type === 'error').length === 0, + errors, + phase: errors.length > 0 ? CheckPhase.PreviewError : CheckPhase.Idle, + } +} diff --git a/src/types/index.ts b/src/types/index.ts index 4afea89..92295d6 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -52,7 +52,8 @@ if (typeof window !== 'undefined') { } class VoidResponseAreaTub extends ResponseAreaTub { - public readonly responseType = 'VOID' + // public readonly responseType = 'VOID' + public readonly responseType = 'SANDBOX' protected answerSchema = z.any() From 605e6e62ec92df0f1a2af205d05371552e70813e Mon Sep 17 00:00:00 2001 From: Harry Date: Wed, 18 Feb 2026 01:44:40 +0000 Subject: [PATCH 15/36] fixed panels --- package.json | 2 +- src/types/Graph/Graph.component.tsx | 317 ++- src/types/Graph/index.tsx | 121 +- yarn.lock | 2841 ++++++++++++++------------- 4 files changed, 1766 insertions(+), 1515 deletions(-) diff --git a/package.json b/package.json index dda9aa2..1a326b3 100644 --- a/package.json +++ b/package.json @@ -80,7 +80,7 @@ "prettier": "^3.3.3", "typescript": "^5.4.5", "typescript-eslint": "^7.11.0", - "vite": "^7.0.0" + "vite": "^5.4.11" }, "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" } diff --git a/src/types/Graph/Graph.component.tsx b/src/types/Graph/Graph.component.tsx index b11115e..ea49978 100644 --- a/src/types/Graph/Graph.component.tsx +++ b/src/types/Graph/Graph.component.tsx @@ -94,8 +94,8 @@ export const GraphEditor: React.FC = ({ const drawCanvasRef = useRef(null) const [drawMode, setDrawMode] = useState(false) - const [selectedNode, setSelectedNode] = useState(null) - const [selectedEdge, setSelectedEdge] = useState(null) + const [selectedNodeId, setSelectedNodeId] = useState(null) + const [selectedEdgeId, setSelectedEdgeId] = useState(null) const [fromNode, setFromNode] = useState(null) const [nodeCounter, setNodeCounter] = useState(graph.nodes.length) const [isDrawing, setIsDrawing] = useState(false) @@ -103,11 +103,11 @@ export const GraphEditor: React.FC = ({ // Drawing state const pathRef = useRef(null) const startPointRef = useRef(null) - const paperInitializedRef = useRef(false) + const paperProjectRef = useRef(null) - /* -------------------- Initialize Cytoscape -------------------- */ + /* -------------------- Initialize Cytoscape (once) -------------------- */ useEffect(() => { - if (!containerRef.current) return + if (!containerRef.current || cyRef.current) return const cy: Core = cytoscape({ container: containerRef.current, @@ -127,6 +127,14 @@ export const GraphEditor: React.FC = ({ 'font-size': '14px' } }, + { + selector: 'node:selected', + style: { + 'background-color': '#e3f2fd', + 'border-width': 3, + 'border-color': '#1976d2' + } + }, { selector: 'edge', style: { @@ -138,6 +146,14 @@ export const GraphEditor: React.FC = ({ 'font-size': '12px' } }, + { + selector: 'edge:selected', + style: { + 'line-color': '#1976d2', + 'target-arrow-color': '#1976d2', + 'width': 3 + } + }, { selector: '.edge-source', style: { @@ -149,32 +165,78 @@ export const GraphEditor: React.FC = ({ }) cyRef.current = cy - - // Load initial nodes - graph.nodes.forEach((n: Node) => - cy.add({ - group: 'nodes', - data: { id: n.id, displayLabel: n.label ?? n.id }, - position: { x: n.x ?? 100, y: n.y ?? 100 } - }), - ) - - // Load initial edges - graph.edges.forEach((e: Edge) => - cy.add({ - group: 'edges', - data: { - id: e.id ?? `e-${e.source}-${e.target}`, - source: e.source, - target: e.target, - label: e.label ?? '' - } - }), - ) + console.log('Cytoscape initialized') return () => { cy.destroy() + cyRef.current = null } + }, []) + + /* -------------------- Update Cytoscape from Graph -------------------- */ + useEffect(() => { + const cy = cyRef.current + if (!cy) return + + console.log('Updating cytoscape from graph', graph) + + // Get existing elements + const existingNodeIds = new Set(cy.nodes().map(n => n.id())) + const existingEdgeIds = new Set(cy.edges().map(e => e.id())) + + // Add/Update nodes + graph.nodes.forEach((n: Node) => { + if (existingNodeIds.has(n.id)) { + // Update existing node + const node = cy.getElementById(n.id) + node.data('displayLabel', n.label ?? n.id) + node.position({ x: n.x ?? node.position().x, y: n.y ?? node.position().y }) + } else { + // Add new node + cy.add({ + group: 'nodes', + data: { id: n.id, displayLabel: n.label ?? n.id }, + position: { x: n.x ?? 100, y: n.y ?? 100 } + }) + } + }) + + // Remove nodes not in graph + const graphNodeIds = new Set(graph.nodes.map(n => n.id)) + cy.nodes().forEach(node => { + if (!graphNodeIds.has(node.id())) { + node.remove() + } + }) + + // Add/Update edges + graph.edges.forEach((e: Edge) => { + const edgeId = e.id ?? `e-${e.source}-${e.target}` + if (existingEdgeIds.has(edgeId)) { + // Update existing edge + const edge = cy.getElementById(edgeId) + edge.data('label', e.label ?? '') + } else { + // Add new edge + cy.add({ + group: 'edges', + data: { + id: edgeId, + source: e.source, + target: e.target, + label: e.label ?? '' + } + }) + } + }) + + // Remove edges not in graph + const graphEdgeIds = new Set(graph.edges.map(e => e.id ?? `e-${e.source}-${e.target}`)) + cy.edges().forEach(edge => { + if (!graphEdgeIds.has(edge.id())) { + edge.remove() + } + }) }, [graph]) /* -------------------- Sync to Graph -------------------- */ @@ -237,13 +299,12 @@ export const GraphEditor: React.FC = ({ /* -------------------- Paper.js Setup -------------------- */ useEffect(() => { const canvas = drawCanvasRef.current - if (!canvas) return + if (!canvas || paperProjectRef.current) return - // Initialize Paper.js only once - if (!paperInitializedRef.current) { - paper.setup(canvas) - paperInitializedRef.current = true - } + // Create a new Paper.js project for this instance + const paperProject = new paper.Project(canvas) + paperProjectRef.current = paperProject + console.log('Paper.js project created') // Setup canvas size const updateCanvasSize = () => { @@ -257,8 +318,10 @@ export const GraphEditor: React.FC = ({ canvas.height = height // Update Paper.js view - paper.view.viewSize = new paper.Size(width, height) - paper.view.update() + if (paperProjectRef.current) { + paperProjectRef.current.view.viewSize = new paper.Size(width, height) + paperProjectRef.current.view.update() + } } updateCanvasSize() @@ -271,12 +334,19 @@ export const GraphEditor: React.FC = ({ return () => { resizeObserver.disconnect() + if (paperProjectRef.current) { + paperProjectRef.current.remove() + paperProjectRef.current = null + } } }, []) /* -------------------- Drawing Handlers -------------------- */ const handlePointerDown = useCallback((e: React.PointerEvent) => { - if (!drawMode) return + if (!drawMode || !paperProjectRef.current) return + + // Activate this project before drawing + paperProjectRef.current.activate() e.preventDefault() e.stopPropagation() @@ -375,27 +445,36 @@ export const GraphEditor: React.FC = ({ let closestNode: NodeSingular | null = null cy.nodes().forEach((node) => { - const pos = node.position() + // Use rendered position (viewport coordinates) instead of graph position + const pos = node.renderedPosition() const dist = Math.hypot(pos.x - pointX, pos.y - pointY) + console.log(` Node ${node.id()}: rendered pos (${pos.x}, ${pos.y}), dist: ${dist}`) if (dist < minDist) { minDist = dist closestNode = node } }) + console.log(` Closest node at distance ${minDist}, threshold: 75`) return minDist < 75 ? closestNode : null // Increased threshold to 75 } + console.log('Start point:', startPointRef.current.x, startPointRef.current.y) + console.log('End point:', endPoint.x, endPoint.y) + const startNode = findClosestNode(startPointRef.current.x, startPointRef.current.y) const endNode = findClosestNode(endPoint.x, endPoint.y) console.log('Found nodes:', { startNode: startNode?.id(), - endNode: endNode?.id() + startDist: startNode ? Math.hypot(startNode.position().x - startPointRef.current.x, startNode.position().y - startPointRef.current.y) : 'N/A', + endNode: endNode?.id(), + endDist: endNode ? Math.hypot(endNode.position().x - endPoint.x, endNode.position().y - endPoint.y) : 'N/A', + totalNodes: cy.nodes().length }) if (startNode !== null && endNode !== null && startNode.id() !== endNode.id()) { - console.log('Adding edge between:', startNode.id(), endNode.id()) + console.log('✓ Adding edge between:', startNode.id(), endNode.id()) cy.add({ group: 'edges', data: { @@ -406,6 +485,12 @@ export const GraphEditor: React.FC = ({ }, }) syncToGraph() + } else { + console.log('✗ Cannot create edge:', { + hasStartNode: startNode !== null, + hasEndNode: endNode !== null, + sameNode: startNode?.id() === endNode?.id() + }) } } } @@ -460,18 +545,26 @@ export const GraphEditor: React.FC = ({ return } - setSelectedNode(node) - setSelectedEdge(null) + console.log('Node clicked:', node.id()) + console.log('Setting selectedNodeId to:', node.id()) + setSelectedNodeId(node.id()) + setSelectedEdgeId(null) } const tapEdge = (e: cytoscape.EventObject) => { - setSelectedEdge(e.target as EdgeSingular) - setSelectedNode(null) + const edge = e.target as EdgeSingular + console.log('Edge clicked:', edge.id()) + setSelectedEdgeId(edge.id()) + setSelectedNodeId(null) } - const tapBlank = () => { - setSelectedNode(null) - setSelectedEdge(null) + const tapBlank = (e: cytoscape.EventObject) => { + // Only clear selection if we clicked the background (not a node or edge) + if (e.target === cy) { + console.log('Blank clicked - clearing selection') + setSelectedNodeId(null) + setSelectedEdgeId(null) + } } cy.on('tap', 'node', tapNode) @@ -486,6 +579,15 @@ export const GraphEditor: React.FC = ({ }, [drawMode, fromNode, syncToGraph]) /* -------------------- Render -------------------- */ + // Get fresh node/edge references from IDs + const selectedNode = selectedNodeId && cyRef.current ? cyRef.current.$id(selectedNodeId) : null + const selectedEdge = selectedEdgeId && cyRef.current ? cyRef.current.$id(selectedEdgeId) : null + + console.log('GraphEditor render - selectedNodeId:', selectedNodeId, 'selectedEdgeId:', selectedEdgeId) + console.log('selectedNode:', selectedNode?.id(), 'length:', selectedNode?.length, 'selectedEdge:', selectedEdge?.id(), 'length:', selectedEdge?.length) + console.log('Should show node panel:', !!(selectedNode && selectedNode.length > 0)) + console.log('Should show edge panel:', !!(selectedEdge && selectedEdge.length > 0)) + return (
{/* -------------------- Item Properties Panel -------------------- */} @@ -544,48 +646,101 @@ export const GraphEditor: React.FC = ({
)} - {selectedNode && ( -
- - { - selectedNode.data('displayLabel', e.target.value); + {/* Debug info */} +
+ Debug: nodeId={selectedNodeId || 'none'}, edgeId={selectedEdgeId || 'none'}
+ Node exists: {selectedNode ? 'yes' : 'no'}, Length: {selectedNode?.length || 0} +
+ + {selectedNode && selectedNode.length > 0 ? ( + <> +
+ Selected Node: {selectedNode.id()} +
+
+ + { + selectedNode.data('displayLabel', e.target.value); + syncToGraph() + }} + /> +
+ + + ) : selectedNodeId ? ( +
+ Node "{selectedNodeId}" not found in graph
- )} + ) : null} - {selectedEdge && ( -
- - { - selectedEdge.data('label', e.target.value); - syncToGraph() - }} - /> + {!selectedNodeId && !selectedEdgeId && ( +
+ Click a node or edge to select it
)} - {(selectedNode || selectedEdge) && ( - - )} + {selectedEdge && selectedEdge.length > 0 ? ( + <> +
+ Selected Edge +
+
+ + { + selectedEdge.data('label', e.target.value); + syncToGraph() + }} + /> +
+ + + ) : null} {/* -------------------- Validation Feedback Panel -------------------- */}
diff --git a/src/types/Graph/index.tsx b/src/types/Graph/index.tsx index e8c0b5f..c0c228e 100644 --- a/src/types/Graph/index.tsx +++ b/src/types/Graph/index.tsx @@ -14,7 +14,9 @@ export class GraphResponseAreaTub extends ResponseAreaTub { public readonly displayWideInput = true protected answerSchema = CompressedGraphSchema + protected configSchema = CompressedGraphSchema + // Correct answer for grading (teacher sets in "answer" panel) protected answer: CompressedGraph = { nodes: [], edges: [], @@ -25,13 +27,77 @@ export class GraphResponseAreaTub extends ResponseAreaTub { metadata: {}, } + // Initial state shown to students (teacher sets in "preview" panel) + protected config: CompressedGraph = { + nodes: [], + edges: [], + directed: false, + weighted: false, + multigraph: false, + name:'', + metadata: {}, + } + private previewFeedback: GraphFeedback | null = null private phase: CheckPhase = CheckPhase.Idle public readonly delegateFeedback = false public readonly delegateLivePreview = true - initWithConfig = () => {} + initWithDefault = () => { + this.config = { + nodes: [], + edges: [], + directed: false, + weighted: false, + multigraph: false, + name:'', + metadata: {}, + } + this.answer = { + nodes: [], + edges: [], + directed: false, + weighted: false, + multigraph: false, + name:'', + metadata: {}, + } + } + + // Override extractConfig to handle missing/invalid config gracefully + protected extractConfig = (provided: any): void => { + if (!provided || typeof provided !== 'object') { + // No config provided - use empty graph as default + this.config = { + nodes: [], + edges: [], + directed: false, + weighted: false, + multigraph: false, + name:'', + metadata: {}, + } + return + } + + const parsedConfig = this.configSchema?.safeParse(provided) + if (!parsedConfig || !parsedConfig.success) { + // Invalid config - use empty graph as default + this.config = { + nodes: [], + edges: [], + directed: false, + weighted: false, + multigraph: false, + name:'', + metadata: {}, + } + return + } + + this.config = parsedConfig.data + } /* -------------------- Custom Check -------------------- */ customCheck(): void { @@ -46,9 +112,23 @@ export class GraphResponseAreaTub extends ResponseAreaTub { /* -------------------- Input -------------------- */ InputComponent = (props: BaseResponseAreaProps) => { - // Ensure a valid compressed graph answer - const parsed = this.answerSchema.safeParse(props.answer) - const compressedAnswer = parsed.success ? parsed.data : this.answer + // In teacher preview mode, edit the initial config + // In student mode, start with config and save to props.answer + const isTeacherPreview = props.isTeacherMode && props.hasPreview + + // Determine the source of truth for the graph data + const initialGraph: CompressedGraph = (() => { + if (props.answer) { + // If props.answer exists, use it (parent component's state) + const parsed = this.answerSchema.safeParse(props.answer) + if (parsed.success) { + return parsed.data + } + } + + // Fallback to config (initial state) or answer (for teacher answer panel) + return isTeacherPreview ? this.config : (this.config ?? this.answer) + })() /* ---------- Extract submitted feedback ---------- */ const submittedFeedback: GraphFeedback | null = (() => { @@ -69,23 +149,23 @@ export class GraphResponseAreaTub extends ResponseAreaTub { // Decompress to Graph format for editor const graph: Graph = { - nodes: compressedAnswer.nodes.map((e) => JSON.parse(e)), - edges: compressedAnswer.edges.map((e) => JSON.parse(e)), - directed: compressedAnswer.directed, - weighted: compressedAnswer.weighted, - multigraph: compressedAnswer.multigraph, - name: compressedAnswer.name, - metadata: compressedAnswer.metadata, + nodes: initialGraph.nodes.map((e) => JSON.parse(e)), + edges: initialGraph.edges.map((e) => JSON.parse(e)), + directed: initialGraph.directed, + weighted: initialGraph.weighted, + multigraph: initialGraph.multigraph, + name: initialGraph.name, + metadata: initialGraph.metadata, } return ( { const compressed: CompressedGraph = { - ...compressedAnswer, nodes: val.nodes.map((n) => JSON.stringify(n)), edges: val.edges.map((e) => JSON.stringify(e)), directed: val.directed, @@ -95,8 +175,10 @@ export class GraphResponseAreaTub extends ResponseAreaTub { metadata: val.metadata, } - // Keep instance state valid - this.answer = compressed + if (isTeacherPreview) { + // Teacher is editing the initial config in preview section + this.config = compressed + } // Validate the graph const preview = validateGraph(val) @@ -117,7 +199,9 @@ export class GraphResponseAreaTub extends ResponseAreaTub { /* -------------------- Wizard -------------------- */ WizardComponent = (props: BaseResponseAreaWizardProps) => { - const graph: Graph = { + // Wizard shows correct answer for grading + // The separate "Response Area Preview" section handles the initial state (config) + const answerGraph: Graph = { nodes: this.answer.nodes.map((e) => JSON.parse(e)), edges: this.answer.edges.map((e) => JSON.parse(e)), directed: this.answer.directed, @@ -129,12 +213,12 @@ export class GraphResponseAreaTub extends ResponseAreaTub { return ( { const compressed: CompressedGraph = { - ...this.answer, nodes: val.nodes.map((n: Node) => JSON.stringify(n)), edges: val.edges.map((e: Edge) => JSON.stringify(e)), directed: val.directed, @@ -144,12 +228,11 @@ export class GraphResponseAreaTub extends ResponseAreaTub { metadata: val.metadata, } - // Wizard must update instance state first this.answer = compressed - // Wizard must emit full payload props.handleChange({ responseType: this.responseType, + config: this.config, answer: compressed, }) }} diff --git a/yarn.lock b/yarn.lock index 628dd87..1e001b9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,66 +2,58 @@ # yarn lockfile v1 -"@ampproject/remapping@^2.2.0": - version "2.3.0" - resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4" - integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw== - dependencies: - "@jridgewell/gen-mapping" "^0.3.5" - "@jridgewell/trace-mapping" "^0.3.24" - -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.27.1.tgz#200f715e66d52a23b221a9435534a91cc13ad5be" - integrity sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg== +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.28.6", "@babel/code-frame@^7.29.0": + version "7.29.0" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.29.0.tgz#7cd7a59f15b3cc0dcd803038f7792712a7d0b15c" + integrity sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw== dependencies: - "@babel/helper-validator-identifier" "^7.27.1" + "@babel/helper-validator-identifier" "^7.28.5" js-tokens "^4.0.0" picocolors "^1.1.1" -"@babel/compat-data@^7.27.2": - version "7.28.0" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.28.0.tgz#9fc6fd58c2a6a15243cd13983224968392070790" - integrity sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw== +"@babel/compat-data@^7.28.6": + version "7.29.0" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.29.0.tgz#00d03e8c0ac24dd9be942c5370990cbe1f17d88d" + integrity sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg== -"@babel/core@^7.27.4": - version "7.28.0" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.28.0.tgz#55dad808d5bf3445a108eefc88ea3fdf034749a4" - integrity sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ== - dependencies: - "@ampproject/remapping" "^2.2.0" - "@babel/code-frame" "^7.27.1" - "@babel/generator" "^7.28.0" - "@babel/helper-compilation-targets" "^7.27.2" - "@babel/helper-module-transforms" "^7.27.3" - "@babel/helpers" "^7.27.6" - "@babel/parser" "^7.28.0" - "@babel/template" "^7.27.2" - "@babel/traverse" "^7.28.0" - "@babel/types" "^7.28.0" +"@babel/core@^7.28.0": + version "7.29.0" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.29.0.tgz#5286ad785df7f79d656e88ce86e650d16ca5f322" + integrity sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA== + dependencies: + "@babel/code-frame" "^7.29.0" + "@babel/generator" "^7.29.0" + "@babel/helper-compilation-targets" "^7.28.6" + "@babel/helper-module-transforms" "^7.28.6" + "@babel/helpers" "^7.28.6" + "@babel/parser" "^7.29.0" + "@babel/template" "^7.28.6" + "@babel/traverse" "^7.29.0" + "@babel/types" "^7.29.0" + "@jridgewell/remapping" "^2.3.5" convert-source-map "^2.0.0" debug "^4.1.0" gensync "^1.0.0-beta.2" json5 "^2.2.3" semver "^6.3.1" -"@babel/generator@^7.28.0": - version "7.28.0" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.28.0.tgz#9cc2f7bd6eb054d77dc66c2664148a0c5118acd2" - integrity sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg== +"@babel/generator@^7.29.0": + version "7.29.1" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.29.1.tgz#d09876290111abbb00ef962a7b83a5307fba0d50" + integrity sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw== dependencies: - "@babel/parser" "^7.28.0" - "@babel/types" "^7.28.0" + "@babel/parser" "^7.29.0" + "@babel/types" "^7.29.0" "@jridgewell/gen-mapping" "^0.3.12" "@jridgewell/trace-mapping" "^0.3.28" jsesc "^3.0.2" -"@babel/helper-compilation-targets@^7.27.2": - version "7.27.2" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz#46a0f6efab808d51d29ce96858dd10ce8732733d" - integrity sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ== +"@babel/helper-compilation-targets@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz#32c4a3f41f12ed1532179b108a4d746e105c2b25" + integrity sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA== dependencies: - "@babel/compat-data" "^7.27.2" + "@babel/compat-data" "^7.28.6" "@babel/helper-validator-option" "^7.27.1" browserslist "^4.24.0" lru-cache "^5.1.1" @@ -72,57 +64,57 @@ resolved "https://registry.yarnpkg.com/@babel/helper-globals/-/helper-globals-7.28.0.tgz#b9430df2aa4e17bc28665eadeae8aa1d985e6674" integrity sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw== -"@babel/helper-module-imports@^7.16.7", "@babel/helper-module-imports@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz#7ef769a323e2655e126673bb6d2d6913bbead204" - integrity sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w== +"@babel/helper-module-imports@^7.16.7", "@babel/helper-module-imports@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz#60632cbd6ffb70b22823187201116762a03e2d5c" + integrity sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw== dependencies: - "@babel/traverse" "^7.27.1" - "@babel/types" "^7.27.1" + "@babel/traverse" "^7.28.6" + "@babel/types" "^7.28.6" -"@babel/helper-module-transforms@^7.27.3": - version "7.27.3" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz#db0bbcfba5802f9ef7870705a7ef8788508ede02" - integrity sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg== +"@babel/helper-module-transforms@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz#9312d9d9e56edc35aeb6e95c25d4106b50b9eb1e" + integrity sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA== dependencies: - "@babel/helper-module-imports" "^7.27.1" - "@babel/helper-validator-identifier" "^7.27.1" - "@babel/traverse" "^7.27.3" + "@babel/helper-module-imports" "^7.28.6" + "@babel/helper-validator-identifier" "^7.28.5" + "@babel/traverse" "^7.28.6" "@babel/helper-plugin-utils@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz#ddb2f876534ff8013e6c2b299bf4d39b3c51d44c" - integrity sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw== + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz#6f13ea251b68c8532e985fd532f28741a8af9ac8" + integrity sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug== "@babel/helper-string-parser@^7.27.1": version "7.27.1" resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz#54da796097ab19ce67ed9f88b47bb2ec49367687" integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA== -"@babel/helper-validator-identifier@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz#a7054dcc145a967dd4dc8fee845a57c1316c9df8" - integrity sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow== +"@babel/helper-validator-identifier@^7.28.5": + version "7.28.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz#010b6938fab7cb7df74aa2bbc06aa503b8fe5fb4" + integrity sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q== "@babel/helper-validator-option@^7.27.1": version "7.27.1" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz#fa52f5b1e7db1ab049445b421c4471303897702f" integrity sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg== -"@babel/helpers@^7.27.6": - version "7.27.6" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.27.6.tgz#6456fed15b2cb669d2d1fabe84b66b34991d812c" - integrity sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug== +"@babel/helpers@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.28.6.tgz#fca903a313ae675617936e8998b814c415cbf5d7" + integrity sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw== dependencies: - "@babel/template" "^7.27.2" - "@babel/types" "^7.27.6" + "@babel/template" "^7.28.6" + "@babel/types" "^7.28.6" -"@babel/parser@^7.1.0", "@babel/parser@^7.20.7", "@babel/parser@^7.27.2", "@babel/parser@^7.28.0": - version "7.28.0" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.28.0.tgz#979829fbab51a29e13901e5a80713dbcb840825e" - integrity sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g== +"@babel/parser@^7.1.0", "@babel/parser@^7.20.7", "@babel/parser@^7.28.6", "@babel/parser@^7.29.0": + version "7.29.0" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.29.0.tgz#669ef345add7d057e92b7ed15f0bac07611831b6" + integrity sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww== dependencies: - "@babel/types" "^7.28.0" + "@babel/types" "^7.29.0" "@babel/plugin-transform-react-jsx-self@^7.27.1": version "7.27.1" @@ -138,40 +130,40 @@ dependencies: "@babel/helper-plugin-utils" "^7.27.1" -"@babel/runtime@^7.12.5", "@babel/runtime@^7.18.3", "@babel/runtime@^7.21.0", "@babel/runtime@^7.23.8", "@babel/runtime@^7.23.9", "@babel/runtime@^7.25.7", "@babel/runtime@^7.27.6", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.7": - version "7.27.6" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.27.6.tgz#ec4070a04d76bae8ddbb10770ba55714a417b7c6" - integrity sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q== +"@babel/runtime@^7.12.5", "@babel/runtime@^7.18.3", "@babel/runtime@^7.21.0", "@babel/runtime@^7.23.8", "@babel/runtime@^7.23.9", "@babel/runtime@^7.25.7", "@babel/runtime@^7.28.6", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.7": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.28.6.tgz#d267a43cb1836dc4d182cce93ae75ba954ef6d2b" + integrity sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA== -"@babel/template@^7.27.2": - version "7.27.2" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.27.2.tgz#fa78ceed3c4e7b63ebf6cb39e5852fca45f6809d" - integrity sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw== +"@babel/template@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.28.6.tgz#0e7e56ecedb78aeef66ce7972b082fce76a23e57" + integrity sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ== dependencies: - "@babel/code-frame" "^7.27.1" - "@babel/parser" "^7.27.2" - "@babel/types" "^7.27.1" + "@babel/code-frame" "^7.28.6" + "@babel/parser" "^7.28.6" + "@babel/types" "^7.28.6" -"@babel/traverse@^7.27.1", "@babel/traverse@^7.27.3", "@babel/traverse@^7.28.0": - version "7.28.0" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.28.0.tgz#518aa113359b062042379e333db18380b537e34b" - integrity sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg== +"@babel/traverse@^7.28.6", "@babel/traverse@^7.29.0": + version "7.29.0" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.29.0.tgz#f323d05001440253eead3c9c858adbe00b90310a" + integrity sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA== dependencies: - "@babel/code-frame" "^7.27.1" - "@babel/generator" "^7.28.0" + "@babel/code-frame" "^7.29.0" + "@babel/generator" "^7.29.0" "@babel/helper-globals" "^7.28.0" - "@babel/parser" "^7.28.0" - "@babel/template" "^7.27.2" - "@babel/types" "^7.28.0" + "@babel/parser" "^7.29.0" + "@babel/template" "^7.28.6" + "@babel/types" "^7.29.0" debug "^4.3.1" -"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.27.1", "@babel/types@^7.27.6", "@babel/types@^7.28.0": - version "7.28.0" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.28.0.tgz#2fd0159a6dc7353933920c43136335a9b264d950" - integrity sha512-jYnje+JyZG5YThjHiF28oT4SIZLnYOcSBb6+SDaFIyzDVSkXQmQQYclJ2R+YxcdmK0AX6x1E5OQNtuh3jHDrUg== +"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.28.2", "@babel/types@^7.28.6", "@babel/types@^7.29.0": + version "7.29.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.29.0.tgz#9f5b1e838c446e72cf3cd4b918152b8c605e37c7" + integrity sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A== dependencies: "@babel/helper-string-parser" "^7.27.1" - "@babel/helper-validator-identifier" "^7.27.1" + "@babel/helper-validator-identifier" "^7.28.5" "@date-io/core@^2.17.0": version "2.17.0" @@ -186,24 +178,24 @@ "@date-io/core" "^2.17.0" "@emnapi/core@^1.4.3": - version "1.4.4" - resolved "https://registry.yarnpkg.com/@emnapi/core/-/core-1.4.4.tgz#76620673f3033626c6d79b1420d69f06a6bb153c" - integrity sha512-A9CnAbC6ARNMKcIcrQwq6HeHCjpcBZ5wSx4U01WXCqEKlrzB9F9315WDNHkrs2xbx7YjjSxbUYxuN6EQzpcY2g== + version "1.8.1" + resolved "https://registry.yarnpkg.com/@emnapi/core/-/core-1.8.1.tgz#fd9efe721a616288345ffee17a1f26ac5dd01349" + integrity sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg== dependencies: - "@emnapi/wasi-threads" "1.0.3" + "@emnapi/wasi-threads" "1.1.0" tslib "^2.4.0" "@emnapi/runtime@^1.4.3": - version "1.4.4" - resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-1.4.4.tgz#19a8f00719c51124e2d0fbf4aaad3fa7b0c92524" - integrity sha512-hHyapA4A3gPaDCNfiqyZUStTMqIkKRshqPIuDOXv1hcBnD4U3l8cP0T1HMCfGRxQ6V64TGCcoswChANyOAwbQg== + version "1.8.1" + resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-1.8.1.tgz#550fa7e3c0d49c5fb175a116e8cd70614f9a22a5" + integrity sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg== dependencies: tslib "^2.4.0" -"@emnapi/wasi-threads@1.0.3": - version "1.0.3" - resolved "https://registry.yarnpkg.com/@emnapi/wasi-threads/-/wasi-threads-1.0.3.tgz#83fa228bde0e71668aad6db1af4937473d1d3ab1" - integrity sha512-8K5IFFsQqF9wQNJptGbS6FNKgUTsSRYnTqNCG1vPP8jFdjSv18n2mQfJpkt2Oibo9iBEzcDnDxNwKTzC7svlJw== +"@emnapi/wasi-threads@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz#60b2102fddc9ccb78607e4a3cf8403ea69be41bf" + integrity sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ== dependencies: tslib "^2.4.0" @@ -241,9 +233,9 @@ integrity sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g== "@emotion/is-prop-valid@^1.3.0": - version "1.3.1" - resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-1.3.1.tgz#8d5cf1132f836d7adbe42cf0b49df7816fc88240" - integrity sha512-/ACwoqx7XQi9knQs/G0qKvv5teDMhD7bXYns9N/wM8ah8iNb8jZ2uNO0YOgiq2o2poIvVtJS2YALasQuMSQ7Kw== + version "1.4.0" + resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-1.4.0.tgz#e9ad47adff0b5c94c72db3669ce46de33edf28c0" + integrity sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw== dependencies: "@emotion/memoize" "^0.9.0" @@ -324,185 +316,167 @@ resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz#5e13fac887f08c44f76b0ccaf3370eb00fec9bb6" integrity sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg== -"@esbuild/aix-ppc64@0.25.6": - version "0.25.6" - resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.6.tgz#164b19122e2ed54f85469df9dea98ddb01d5e79e" - integrity sha512-ShbM/3XxwuxjFiuVBHA+d3j5dyac0aEVVq1oluIDf71hUw0aRF59dV/efUsIwFnR6m8JNM2FjZOzmaZ8yG61kw== - -"@esbuild/android-arm64@0.25.6": - version "0.25.6" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.25.6.tgz#8f539e7def848f764f6432598e51cc3820fde3a5" - integrity sha512-hd5zdUarsK6strW+3Wxi5qWws+rJhCCbMiC9QZyzoxfk5uHRIE8T287giQxzVpEvCwuJ9Qjg6bEjcRJcgfLqoA== - -"@esbuild/android-arm@0.25.6": - version "0.25.6" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.25.6.tgz#4ceb0f40113e9861169be83e2a670c260dd234ff" - integrity sha512-S8ToEOVfg++AU/bHwdksHNnyLyVM+eMVAOf6yRKFitnwnbwwPNqKr3srzFRe7nzV69RQKb5DgchIX5pt3L53xg== - -"@esbuild/android-x64@0.25.6": - version "0.25.6" - resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.25.6.tgz#ad4f280057622c25fe985c08999443a195dc63a8" - integrity sha512-0Z7KpHSr3VBIO9A/1wcT3NTy7EB4oNC4upJ5ye3R7taCc2GUdeynSLArnon5G8scPwaU866d3H4BCrE5xLW25A== - -"@esbuild/darwin-arm64@0.25.6": - version "0.25.6" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.6.tgz#d1f04027396b3d6afc96bacd0d13167dfd9f01f7" - integrity sha512-FFCssz3XBavjxcFxKsGy2DYK5VSvJqa6y5HXljKzhRZ87LvEi13brPrf/wdyl/BbpbMKJNOr1Sd0jtW4Ge1pAA== - -"@esbuild/darwin-x64@0.25.6": - version "0.25.6" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.25.6.tgz#2b4a6cedb799f635758d7832d75b23772c8ef68f" - integrity sha512-GfXs5kry/TkGM2vKqK2oyiLFygJRqKVhawu3+DOCk7OxLy/6jYkWXhlHwOoTb0WqGnWGAS7sooxbZowy+pK9Yg== - -"@esbuild/freebsd-arm64@0.25.6": - version "0.25.6" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.6.tgz#a26266cc97dd78dc3c3f3d6788b1b83697b1055d" - integrity sha512-aoLF2c3OvDn2XDTRvn8hN6DRzVVpDlj2B/F66clWd/FHLiHaG3aVZjxQX2DYphA5y/evbdGvC6Us13tvyt4pWg== - -"@esbuild/freebsd-x64@0.25.6": - version "0.25.6" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.6.tgz#9feb8e826735c568ebfd94859b22a3fbb6a9bdd2" - integrity sha512-2SkqTjTSo2dYi/jzFbU9Plt1vk0+nNg8YC8rOXXea+iA3hfNJWebKYPs3xnOUf9+ZWhKAaxnQNUf2X9LOpeiMQ== - -"@esbuild/linux-arm64@0.25.6": - version "0.25.6" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.25.6.tgz#c07cbed8e249f4c28e7f32781d36fc4695293d28" - integrity sha512-b967hU0gqKd9Drsh/UuAm21Khpoh6mPBSgz8mKRq4P5mVK8bpA+hQzmm/ZwGVULSNBzKdZPQBRT3+WuVavcWsQ== - -"@esbuild/linux-arm@0.25.6": - version "0.25.6" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.25.6.tgz#d6e2cd8ef3196468065d41f13fa2a61aaa72644a" - integrity sha512-SZHQlzvqv4Du5PrKE2faN0qlbsaW/3QQfUUc6yO2EjFcA83xnwm91UbEEVx4ApZ9Z5oG8Bxz4qPE+HFwtVcfyw== - -"@esbuild/linux-ia32@0.25.6": - version "0.25.6" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.25.6.tgz#3e682bd47c4eddcc4b8f1393dfc8222482f17997" - integrity sha512-aHWdQ2AAltRkLPOsKdi3xv0mZ8fUGPdlKEjIEhxCPm5yKEThcUjHpWB1idN74lfXGnZ5SULQSgtr5Qos5B0bPw== - -"@esbuild/linux-loong64@0.25.6": - version "0.25.6" - resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.25.6.tgz#473f5ea2e52399c08ad4cd6b12e6dbcddd630f05" - integrity sha512-VgKCsHdXRSQ7E1+QXGdRPlQ/e08bN6WMQb27/TMfV+vPjjTImuT9PmLXupRlC90S1JeNNW5lzkAEO/McKeJ2yg== - -"@esbuild/linux-mips64el@0.25.6": - version "0.25.6" - resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.6.tgz#9960631c9fd61605b0939c19043acf4ef2b51718" - integrity sha512-WViNlpivRKT9/py3kCmkHnn44GkGXVdXfdc4drNmRl15zVQ2+D2uFwdlGh6IuK5AAnGTo2qPB1Djppj+t78rzw== - -"@esbuild/linux-ppc64@0.25.6": - version "0.25.6" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.6.tgz#477cbf8bb04aa034b94f362c32c86b5c31db8d3e" - integrity sha512-wyYKZ9NTdmAMb5730I38lBqVu6cKl4ZfYXIs31Baf8aoOtB4xSGi3THmDYt4BTFHk7/EcVixkOV2uZfwU3Q2Jw== - -"@esbuild/linux-riscv64@0.25.6": - version "0.25.6" - resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.6.tgz#bcdb46c8fb8e93aa779e9a0a62cd4ac00dcac626" - integrity sha512-KZh7bAGGcrinEj4qzilJ4hqTY3Dg2U82c8bv+e1xqNqZCrCyc+TL9AUEn5WGKDzm3CfC5RODE/qc96OcbIe33w== - -"@esbuild/linux-s390x@0.25.6": - version "0.25.6" - resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.25.6.tgz#f412cf5fdf0aea849ff51c73fd817c6c0234d46d" - integrity sha512-9N1LsTwAuE9oj6lHMyyAM+ucxGiVnEqUdp4v7IaMmrwb06ZTEVCIs3oPPplVsnjPfyjmxwHxHMF8b6vzUVAUGw== - -"@esbuild/linux-x64@0.25.6": - version "0.25.6" - resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.25.6.tgz#d8233c09b5ebc0c855712dc5eeb835a3a3341108" - integrity sha512-A6bJB41b4lKFWRKNrWoP2LHsjVzNiaurf7wyj/XtFNTsnPuxwEBWHLty+ZE0dWBKuSK1fvKgrKaNjBS7qbFKig== - -"@esbuild/netbsd-arm64@0.25.6": - version "0.25.6" - resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.6.tgz#f51ae8dd1474172e73cf9cbaf8a38d1c72dd8f1a" - integrity sha512-IjA+DcwoVpjEvyxZddDqBY+uJ2Snc6duLpjmkXm/v4xuS3H+3FkLZlDm9ZsAbF9rsfP3zeA0/ArNDORZgrxR/Q== - -"@esbuild/netbsd-x64@0.25.6": - version "0.25.6" - resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.6.tgz#a267538602c0e50a858cf41dcfe5d8036f8da8e7" - integrity sha512-dUXuZr5WenIDlMHdMkvDc1FAu4xdWixTCRgP7RQLBOkkGgwuuzaGSYcOpW4jFxzpzL1ejb8yF620UxAqnBrR9g== - -"@esbuild/openbsd-arm64@0.25.6": - version "0.25.6" - resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.6.tgz#a51be60c425b85c216479b8c344ad0511635f2d2" - integrity sha512-l8ZCvXP0tbTJ3iaqdNf3pjaOSd5ex/e6/omLIQCVBLmHTlfXW3zAxQ4fnDmPLOB1x9xrcSi/xtCWFwCZRIaEwg== - -"@esbuild/openbsd-x64@0.25.6": - version "0.25.6" - resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.6.tgz#7e4a743c73f75562e29223ba69d0be6c9c9008da" - integrity sha512-hKrmDa0aOFOr71KQ/19JC7az1P0GWtCN1t2ahYAf4O007DHZt/dW8ym5+CUdJhQ/qkZmI1HAF8KkJbEFtCL7gw== - -"@esbuild/openharmony-arm64@0.25.6": - version "0.25.6" - resolved "https://registry.yarnpkg.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.6.tgz#2087a5028f387879154ebf44bdedfafa17682e5b" - integrity sha512-+SqBcAWoB1fYKmpWoQP4pGtx+pUUC//RNYhFdbcSA16617cchuryuhOCRpPsjCblKukAckWsV+aQ3UKT/RMPcA== - -"@esbuild/sunos-x64@0.25.6": - version "0.25.6" - resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.25.6.tgz#56531f861723ea0dc6283a2bb8837304223cb736" - integrity sha512-dyCGxv1/Br7MiSC42qinGL8KkG4kX0pEsdb0+TKhmJZgCUDBGmyo1/ArCjNGiOLiIAgdbWgmWgib4HoCi5t7kA== - -"@esbuild/win32-arm64@0.25.6": - version "0.25.6" - resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.25.6.tgz#f4989f033deac6fae323acff58764fa8bc01436e" - integrity sha512-42QOgcZeZOvXfsCBJF5Afw73t4veOId//XD3i+/9gSkhSV6Gk3VPlWncctI+JcOyERv85FUo7RxuxGy+z8A43Q== - -"@esbuild/win32-ia32@0.25.6": - version "0.25.6" - resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.25.6.tgz#b260e9df71e3939eb33925076d39f63cec7d1525" - integrity sha512-4AWhgXmDuYN7rJI6ORB+uU9DHLq/erBbuMoAuB4VWJTu5KtCgcKYPynF0YI1VkBNuEfjNlLrFr9KZPJzrtLkrQ== - -"@esbuild/win32-x64@0.25.6": - version "0.25.6" - resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.25.6.tgz#4276edd5c105bc28b11c6a1f76fb9d29d1bd25c1" - integrity sha512-NgJPHHbEpLQgDH2MjQu90pzW/5vvXIZ7KOnPyNBm92A6WgZ/7b6fJyUBjoumLqeOQQGqY2QjQxRo97ah4Sj0cA== - -"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": - version "4.7.0" - resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz#607084630c6c033992a082de6e6fbc1a8b52175a" - integrity sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw== +"@esbuild/aix-ppc64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz#c7184a326533fcdf1b8ee0733e21c713b975575f" + integrity sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ== + +"@esbuild/android-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz#09d9b4357780da9ea3a7dfb833a1f1ff439b4052" + integrity sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A== + +"@esbuild/android-arm@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz#9b04384fb771926dfa6d7ad04324ecb2ab9b2e28" + integrity sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg== + +"@esbuild/android-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz#29918ec2db754cedcb6c1b04de8cd6547af6461e" + integrity sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA== + +"@esbuild/darwin-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz#e495b539660e51690f3928af50a76fb0a6ccff2a" + integrity sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ== + +"@esbuild/darwin-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz#c13838fa57372839abdddc91d71542ceea2e1e22" + integrity sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw== + +"@esbuild/freebsd-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz#646b989aa20bf89fd071dd5dbfad69a3542e550e" + integrity sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g== + +"@esbuild/freebsd-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz#aa615cfc80af954d3458906e38ca22c18cf5c261" + integrity sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ== + +"@esbuild/linux-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz#70ac6fa14f5cb7e1f7f887bcffb680ad09922b5b" + integrity sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q== + +"@esbuild/linux-arm@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz#fc6fd11a8aca56c1f6f3894f2bea0479f8f626b9" + integrity sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA== + +"@esbuild/linux-ia32@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz#3271f53b3f93e3d093d518d1649d6d68d346ede2" + integrity sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg== + +"@esbuild/linux-loong64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz#ed62e04238c57026aea831c5a130b73c0f9f26df" + integrity sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg== + +"@esbuild/linux-mips64el@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz#e79b8eb48bf3b106fadec1ac8240fb97b4e64cbe" + integrity sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg== + +"@esbuild/linux-ppc64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz#5f2203860a143b9919d383ef7573521fb154c3e4" + integrity sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w== + +"@esbuild/linux-riscv64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz#07bcafd99322d5af62f618cb9e6a9b7f4bb825dc" + integrity sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA== + +"@esbuild/linux-s390x@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz#b7ccf686751d6a3e44b8627ababc8be3ef62d8de" + integrity sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A== + +"@esbuild/linux-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz#6d8f0c768e070e64309af8004bb94e68ab2bb3b0" + integrity sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ== + +"@esbuild/netbsd-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz#bbe430f60d378ecb88decb219c602667387a6047" + integrity sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg== + +"@esbuild/openbsd-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz#99d1cf2937279560d2104821f5ccce220cb2af70" + integrity sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow== + +"@esbuild/sunos-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz#08741512c10d529566baba837b4fe052c8f3487b" + integrity sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg== + +"@esbuild/win32-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz#675b7385398411240735016144ab2e99a60fc75d" + integrity sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A== + +"@esbuild/win32-ia32@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz#1bfc3ce98aa6ca9a0969e4d2af72144c59c1193b" + integrity sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA== + +"@esbuild/win32-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz#acad351d582d157bb145535db2a6ff53dd514b5c" + integrity sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw== + +"@eslint-community/eslint-utils@^4.4.0", "@eslint-community/eslint-utils@^4.8.0": + version "4.9.1" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz#4e90af67bc51ddee6cdef5284edf572ec376b595" + integrity sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ== dependencies: eslint-visitor-keys "^3.4.3" "@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.12.1": - version "4.12.1" - resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0" - integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ== + version "4.12.2" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.2.tgz#bccdf615bcf7b6e8db830ec0b8d21c9a25de597b" + integrity sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew== "@eslint/compat@^1.0.1": - version "1.3.1" - resolved "https://registry.yarnpkg.com/@eslint/compat/-/compat-1.3.1.tgz#19f62ee48896d38f8eab591bee2a25dd69abc273" - integrity sha512-k8MHony59I5EPic6EQTCNOuPoVBnoYXkP+20xvwFjN7t0qI3ImyvyBgg+hIVPwC8JaxVjjUZld+cLfBLFDLucg== + version "1.4.1" + resolved "https://registry.yarnpkg.com/@eslint/compat/-/compat-1.4.1.tgz#81eaabb3e0b080350582c1a8092a2d355fabf03e" + integrity sha512-cfO82V9zxxGBxcQDr1lfaYB7wykTa0b00mGa36FrJl7iTFd0Z2cHfEYuxcBRP/iNijCsWsEkA+jzT8hGYmv33w== + dependencies: + "@eslint/core" "^0.17.0" -"@eslint/config-array@^0.21.0": - version "0.21.0" - resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.21.0.tgz#abdbcbd16b124c638081766392a4d6b509f72636" - integrity sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ== +"@eslint/config-array@^0.21.1": + version "0.21.1" + resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.21.1.tgz#7d1b0060fea407f8301e932492ba8c18aff29713" + integrity sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA== dependencies: - "@eslint/object-schema" "^2.1.6" + "@eslint/object-schema" "^2.1.7" debug "^4.3.1" minimatch "^3.1.2" -"@eslint/config-helpers@^0.3.0": - version "0.3.0" - resolved "https://registry.yarnpkg.com/@eslint/config-helpers/-/config-helpers-0.3.0.tgz#3e09a90dfb87e0005c7694791e58e97077271286" - integrity sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw== - -"@eslint/core@^0.14.0": - version "0.14.0" - resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.14.0.tgz#326289380968eaf7e96f364e1e4cf8f3adf2d003" - integrity sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg== +"@eslint/config-helpers@^0.4.2": + version "0.4.2" + resolved "https://registry.yarnpkg.com/@eslint/config-helpers/-/config-helpers-0.4.2.tgz#1bd006ceeb7e2e55b2b773ab318d300e1a66aeda" + integrity sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw== dependencies: - "@types/json-schema" "^7.0.15" + "@eslint/core" "^0.17.0" -"@eslint/core@^0.15.1": - version "0.15.1" - resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.15.1.tgz#d530d44209cbfe2f82ef86d6ba08760196dd3b60" - integrity sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA== +"@eslint/core@^0.17.0": + version "0.17.0" + resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.17.0.tgz#77225820413d9617509da9342190a2019e78761c" + integrity sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ== dependencies: "@types/json-schema" "^7.0.15" "@eslint/eslintrc@^3.3.1": - version "3.3.1" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.3.1.tgz#e55f7f1dd400600dd066dbba349c4c0bac916964" - integrity sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ== + version "3.3.3" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.3.3.tgz#26393a0806501b5e2b6a43aa588a4d8df67880ac" + integrity sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ== dependencies: ajv "^6.12.4" debug "^4.3.2" @@ -510,26 +484,26 @@ globals "^14.0.0" ignore "^5.2.0" import-fresh "^3.2.1" - js-yaml "^4.1.0" + js-yaml "^4.1.1" minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@eslint/js@9.30.1", "@eslint/js@^9.3.0": - version "9.30.1" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.30.1.tgz#ebe9dd52a38345784c486300175a28c6013c088d" - integrity sha512-zXhuECFlyep42KZUhWjfvsmXGX39W8K8LFb8AWXM9gSV9dQB+MrJGLKvW6Zw0Ggnbpw0VHTtrhFXYe3Gym18jg== +"@eslint/js@9.39.2", "@eslint/js@^9.3.0": + version "9.39.2" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.39.2.tgz#2d4b8ec4c3ea13c1b3748e0c97ecd766bdd80599" + integrity sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA== -"@eslint/object-schema@^2.1.6": - version "2.1.6" - resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.6.tgz#58369ab5b5b3ca117880c0f6c0b0f32f6950f24f" - integrity sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA== +"@eslint/object-schema@^2.1.7": + version "2.1.7" + resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.7.tgz#6e2126a1347e86a4dedf8706ec67ff8e107ebbad" + integrity sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA== -"@eslint/plugin-kit@^0.3.1": - version "0.3.3" - resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.3.3.tgz#32926b59bd407d58d817941e48b2a7049359b1fd" - integrity sha512-1+WqvgNMhmlAambTvT3KPtCl/Ibr68VldY2XY40SL1CE0ZXiakFR/cbTspaF5HsnpDMvcYYoJHfl4980NBjGag== +"@eslint/plugin-kit@^0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz#9779e3fd9b7ee33571a57435cf4335a1794a6cb2" + integrity sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA== dependencies: - "@eslint/core" "^0.15.1" + "@eslint/core" "^0.17.0" levn "^0.4.1" "@fast-csv/format@4.3.5": @@ -557,27 +531,27 @@ lodash.isundefined "^3.0.1" lodash.uniq "^4.5.0" -"@floating-ui/core@^1.7.2": - version "1.7.2" - resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.7.2.tgz#3d1c35263950b314b6d5a72c8bfb9e3c1551aefd" - integrity sha512-wNB5ooIKHQc+Kui96jE/n69rHFWAVoxn5CAzL1Xdd8FG03cgY3MLO+GF9U3W737fYDSgPWA6MReKhBQBop6Pcw== +"@floating-ui/core@^1.7.4": + version "1.7.4" + resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.7.4.tgz#4a006a6e01565c0f87ba222c317b056a2cffd2f4" + integrity sha512-C3HlIdsBxszvm5McXlB8PeOEWfBhcGBTZGkGlWc2U0KFY5IwG5OQEuQ8rq52DZmcHDlPLd+YFBK+cZcytwIFWg== dependencies: "@floating-ui/utils" "^0.2.10" -"@floating-ui/dom@^1.7.2": - version "1.7.2" - resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.7.2.tgz#3540b051cf5ce0d4f4db5fb2507a76e8ea5b4a45" - integrity sha512-7cfaOQuCS27HD7DX+6ib2OrnW+b4ZBwDNnCcT0uTyidcmyWb03FnQqJybDBoCnpdxwBSfA94UAYlRCt7mV+TbA== +"@floating-ui/dom@^1.7.5": + version "1.7.5" + resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.7.5.tgz#60bfc83a4d1275b2a90db76bf42ca2a5f2c231c2" + integrity sha512-N0bD2kIPInNHUHehXhMke1rBGs1dwqvC9O9KYMyyjK7iXt7GAhnro7UlcuYcGdS/yYOlq0MAVgrow8IbWJwyqg== dependencies: - "@floating-ui/core" "^1.7.2" + "@floating-ui/core" "^1.7.4" "@floating-ui/utils" "^0.2.10" "@floating-ui/react-dom@^2.0.0", "@floating-ui/react-dom@^2.0.8": - version "2.1.4" - resolved "https://registry.yarnpkg.com/@floating-ui/react-dom/-/react-dom-2.1.4.tgz#a0689be8978352fff2be2dfdd718cf668c488ec3" - integrity sha512-JbbpPhp38UmXDDAu60RJmbeme37Jbgsm7NrHGgzYYFKmblzRUh6Pa641dII6LsjwF4XlScDrde2UAzDo/b9KPw== + version "2.1.7" + resolved "https://registry.yarnpkg.com/@floating-ui/react-dom/-/react-dom-2.1.7.tgz#529475cc16ee4976ba3387968117e773d9aa703e" + integrity sha512-0tLRojf/1Go2JgEVm+3Frg9A3IW8bJgKgdO0BN5RkF//ufuz2joZM63Npau2ff3J6lUVYgDSNzNkR+aH3IVfjg== dependencies: - "@floating-ui/dom" "^1.7.2" + "@floating-ui/dom" "^1.7.5" "@floating-ui/utils@^0.2.10": version "0.2.10" @@ -590,24 +564,19 @@ integrity sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA== "@humanfs/node@^0.16.6": - version "0.16.6" - resolved "https://registry.yarnpkg.com/@humanfs/node/-/node-0.16.6.tgz#ee2a10eaabd1131987bf0488fd9b820174cd765e" - integrity sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw== + version "0.16.7" + resolved "https://registry.yarnpkg.com/@humanfs/node/-/node-0.16.7.tgz#822cb7b3a12c5a240a24f621b5a2413e27a45f26" + integrity sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ== dependencies: "@humanfs/core" "^0.19.1" - "@humanwhocodes/retry" "^0.3.0" + "@humanwhocodes/retry" "^0.4.0" "@humanwhocodes/module-importer@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== -"@humanwhocodes/retry@^0.3.0": - version "0.3.1" - resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.3.1.tgz#c72a5c76a9fbaf3488e231b13dc52c0da7bab42a" - integrity sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA== - -"@humanwhocodes/retry@^0.4.2": +"@humanwhocodes/retry@^0.4.0", "@humanwhocodes/retry@^0.4.2": version "0.4.3" resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.4.3.tgz#c2b9d2e374ee62c586d3adbea87199b1d7a7a6ba" integrity sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ== @@ -625,35 +594,43 @@ wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" "@jridgewell/gen-mapping@^0.3.12", "@jridgewell/gen-mapping@^0.3.5": - version "0.3.12" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz#2234ce26c62889f03db3d7fea43c1932ab3e927b" - integrity sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg== + version "0.3.13" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz#6342a19f44347518c93e43b1ac69deb3c4656a1f" + integrity sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA== dependencies: "@jridgewell/sourcemap-codec" "^1.5.0" "@jridgewell/trace-mapping" "^0.3.24" +"@jridgewell/remapping@^2.3.5": + version "2.3.5" + resolved "https://registry.yarnpkg.com/@jridgewell/remapping/-/remapping-2.3.5.tgz#375c476d1972947851ba1e15ae8f123047445aa1" + integrity sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ== + dependencies: + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.24" + "@jridgewell/resolve-uri@^3.1.0": version "3.1.2" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== "@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.0": - version "1.5.4" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz#7358043433b2e5da569aa02cbc4c121da3af27d7" - integrity sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw== + version "1.5.5" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba" + integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og== "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.28": - version "0.3.29" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz#a58d31eaadaf92c6695680b2e1d464a9b8fbf7fc" - integrity sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ== + version "0.3.31" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz#db15d6781c931f3a251a3dac39501c98a6082fd0" + integrity sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw== dependencies: "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" "@monaco-editor/loader@^1.5.0": - version "1.5.0" - resolved "https://registry.yarnpkg.com/@monaco-editor/loader/-/loader-1.5.0.tgz#dcdbc7fe7e905690fb449bed1c251769f325c55d" - integrity sha512-hKoGSM+7aAc7eRTRjpqAZucPmoNOC4UUbknb/VNoTkEIkCPhqV8LfbsgM1webRM7S/z21eHEx9Fkwx8Z/C/+Xw== + version "1.7.0" + resolved "https://registry.yarnpkg.com/@monaco-editor/loader/-/loader-1.7.0.tgz#967aaa4601b19e913627688dfe8159d57549e793" + integrity sha512-gIwR1HrJrrx+vfyOhYmCZ0/JcWqG5kbfG7+d3f/C1LXk2EvzAbHSg3MQ5lO2sMlo9izoAZ04shohfKLVT6crVA== dependencies: state-local "^1.0.6" @@ -761,12 +738,12 @@ csstype "^3.1.3" prop-types "^15.8.1" -"@mui/types@^7.4.4": - version "7.4.4" - resolved "https://registry.yarnpkg.com/@mui/types/-/types-7.4.4.tgz#0c5cd56905231e27096b41d096f1c948c26bdd5d" - integrity sha512-p63yhbX52MO/ajXC7hDHJA5yjzJekvWD3q4YDLl1rSg+OXLczMYPvTuSuviPRCgRX8+E42RXz1D/dz9SxPSlWg== +"@mui/types@^7.4.11": + version "7.4.11" + resolved "https://registry.yarnpkg.com/@mui/types/-/types-7.4.11.tgz#d9c8e028a354a52fe86cb8ffbc306faf4c090608" + integrity sha512-fZ2xO9D08IKOxO2oUBi1nnVKH6oJUD+64cnv4YAaFoC0E5+i1+S5AHbNqqvZlYYsbPEQ6qEVwuBqY3jl5W4G+Q== dependencies: - "@babel/runtime" "^7.27.6" + "@babel/runtime" "^7.28.6" "@mui/types@~7.2.15": version "7.2.24" @@ -774,16 +751,16 @@ integrity sha512-3c8tRt/CbWZ+pEg7QpSwbdxOk36EfmhbKf6AGZsD1EcLDLTSZoxxJ86FVtcjxvjuhdyBiWKSTGZFaXCnidO2kw== "@mui/utils@^5.16.6 || ^6.0.0 || ^7.0.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-7.2.0.tgz#31062697b41aa8ea8ef04e3d3fadca1dec3e1de1" - integrity sha512-O0i1GQL6MDzhKdy9iAu5Yr0Sz1wZjROH1o3aoztuivdCXqEeQYnEjTDiRLGuFxI9zrUbTHBwobMyQH5sNtyacw== + version "7.3.8" + resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-7.3.8.tgz#0d6dff4c623682b603f27d6ff37839b3d16832de" + integrity sha512-kZRcE2620CBGr+XI8YMmwPj6WIPwSF7uMJjvSfqd8zXVvlz0MCJbzRRUGNf8NgflCLthdji2DdS643TeyJ3+nA== dependencies: - "@babel/runtime" "^7.27.6" - "@mui/types" "^7.4.4" + "@babel/runtime" "^7.28.6" + "@mui/types" "^7.4.11" "@types/prop-types" "^15.7.15" clsx "^2.1.1" prop-types "^15.8.1" - react-is "^19.1.0" + react-is "^19.2.3" "@mui/utils@^5.17.1": version "5.17.1" @@ -798,25 +775,25 @@ react-is "^19.0.0" "@mui/x-data-grid-generator@^7.7.0": - version "7.29.8" - resolved "https://registry.yarnpkg.com/@mui/x-data-grid-generator/-/x-data-grid-generator-7.29.8.tgz#a449342ff95c3a1f6f3d175ba41259bf88317af2" - integrity sha512-bp/xMUJAxJ1ujnys6otEuKe08yqWq01E+0N4DZ5Zj1Aou7+epXqPMQR4Dy91mhq42LIMwSC82RVxkXvvkmYpaQ== + version "7.29.12" + resolved "https://registry.yarnpkg.com/@mui/x-data-grid-generator/-/x-data-grid-generator-7.29.12.tgz#b84a8c61548d50c25b31a429ed0e1f3b12129628" + integrity sha512-byV9OMwaNrWZ1J5DN2q9lNN3Qu4OuurXPX1iQytA9iGXt0aQbxJkKRdhKQzWya1C1uKDN4mZy2FMtTSjVg1Kig== dependencies: "@babel/runtime" "^7.25.7" - "@mui/x-data-grid-premium" "7.29.8" + "@mui/x-data-grid-premium" "7.29.12" chance "^1.1.12" clsx "^2.1.1" lru-cache "^11.0.1" -"@mui/x-data-grid-premium@7.29.8": - version "7.29.8" - resolved "https://registry.yarnpkg.com/@mui/x-data-grid-premium/-/x-data-grid-premium-7.29.8.tgz#f091909cb21d3b142ed99483ed19fa2e046e31e3" - integrity sha512-4JzWK0GS3vqbbuSWpCLgQ0WozjY0ImFCq6Um2KLZepyhYxW0u8GYi3w1C6wbS9nomUEZVQB7n1+OFKJUW7Bm9g== +"@mui/x-data-grid-premium@7.29.12": + version "7.29.12" + resolved "https://registry.yarnpkg.com/@mui/x-data-grid-premium/-/x-data-grid-premium-7.29.12.tgz#9ba4efc1909700d182291cd1f4627db8d943b5b2" + integrity sha512-tiNWWE5aTIYYdcoxUv8XQxo5MzzQwd61LxItVslMKYh42Wd00TD6UZVAk4KmSkqMjJ7W7VRx3YYbrbJtRHPKVQ== dependencies: "@babel/runtime" "^7.25.7" "@mui/utils" "^5.16.6 || ^6.0.0 || ^7.0.0" - "@mui/x-data-grid" "7.29.8" - "@mui/x-data-grid-pro" "7.29.8" + "@mui/x-data-grid" "7.29.12" + "@mui/x-data-grid-pro" "7.29.12" "@mui/x-internals" "7.29.0" "@mui/x-license" "7.29.1" "@types/format-util" "^1.0.4" @@ -825,14 +802,14 @@ prop-types "^15.8.1" reselect "^5.1.1" -"@mui/x-data-grid-pro@7.29.8": - version "7.29.8" - resolved "https://registry.yarnpkg.com/@mui/x-data-grid-pro/-/x-data-grid-pro-7.29.8.tgz#705ac30f7ad3cb39f88f69030d545eea2fb16d50" - integrity sha512-0hwICtGqjrKt/aU80pd6QjZBWd04uxEBDjxiFTMmsbjmSqlpSZLSRe2gVQyDvZkonR7Qvfr7uhjso5T1009ehw== +"@mui/x-data-grid-pro@7.29.12": + version "7.29.12" + resolved "https://registry.yarnpkg.com/@mui/x-data-grid-pro/-/x-data-grid-pro-7.29.12.tgz#54163074687eb607f8eb9958d01f2317ee0253ac" + integrity sha512-HoBaqJYqxaVZCkv3NK1qdca0Sv2er6L2Wrt51gyfMBBuM+liSpQ0L4HqX42bzIst+ScAoBpBAn6ILm9RQ0rqLw== dependencies: "@babel/runtime" "^7.25.7" "@mui/utils" "^5.16.6 || ^6.0.0 || ^7.0.0" - "@mui/x-data-grid" "7.29.8" + "@mui/x-data-grid" "7.29.12" "@mui/x-internals" "7.29.0" "@mui/x-license" "7.29.1" "@types/format-util" "^1.0.4" @@ -840,10 +817,10 @@ prop-types "^15.8.1" reselect "^5.1.1" -"@mui/x-data-grid@7.29.8", "@mui/x-data-grid@^7.7.0": - version "7.29.8" - resolved "https://registry.yarnpkg.com/@mui/x-data-grid/-/x-data-grid-7.29.8.tgz#ea3776c02c6aa44ce6b18aef2924270a9a4d7e66" - integrity sha512-m4Dp1Vig8gFiBlcEOWimUku182cEw5YrAyXS3PfTSdbxa/20bFw7a8mlHdxO9ChHQRMf6TqbisdRm23CDIIdog== +"@mui/x-data-grid@7.29.12", "@mui/x-data-grid@^7.7.0": + version "7.29.12" + resolved "https://registry.yarnpkg.com/@mui/x-data-grid/-/x-data-grid-7.29.12.tgz#a083213decd52f6a986c8290e3ba0176905d545e" + integrity sha512-MaEC7ubr/je8jVWjdRU7LxBXAzlOZwFEdNdvlDUJIYkRa3TRCQ1HsY8Gd8Od0jnlnMYn9M4BrEfOrq9VRnt4bw== dependencies: "@babel/runtime" "^7.25.7" "@mui/utils" "^5.16.6 || ^6.0.0 || ^7.0.0" @@ -884,70 +861,70 @@ "@mui/x-internals" "7.29.0" "@napi-rs/wasm-runtime@^0.2.11": - version "0.2.11" - resolved "https://registry.yarnpkg.com/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.11.tgz#192c1610e1625048089ab4e35bc0649ce478500e" - integrity sha512-9DPkXtvHydrcOsopiYpUgPHpmj0HWZKMUnL2dZqpvC42lsratuBG06V5ipyno0fUek5VlFsNQ+AcFATSrJXgMA== + version "0.2.12" + resolved "https://registry.yarnpkg.com/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz#3e78a8b96e6c33a6c517e1894efbd5385a7cb6f2" + integrity sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ== dependencies: "@emnapi/core" "^1.4.3" "@emnapi/runtime" "^1.4.3" - "@tybys/wasm-util" "^0.9.0" + "@tybys/wasm-util" "^0.10.0" -"@next/env@14.2.30": - version "14.2.30" - resolved "https://registry.yarnpkg.com/@next/env/-/env-14.2.30.tgz#f955b57975751584722b6b0a2a8cf2bdcc4ffae3" - integrity sha512-KBiBKrDY6kxTQWGzKjQB7QirL3PiiOkV7KW98leHFjtVRKtft76Ra5qSA/SL75xT44dp6hOcqiiJ6iievLOYug== +"@next/env@14.2.35": + version "14.2.35" + resolved "https://registry.yarnpkg.com/@next/env/-/env-14.2.35.tgz#e979016d0ca8500a47d41ffd02625fe29b8df35a" + integrity sha512-DuhvCtj4t9Gwrx80dmz2F4t/zKQ4ktN8WrMwOuVzkJfBilwAwGr6v16M5eI8yCuZ63H9TTuEU09Iu2HqkzFPVQ== "@next/eslint-plugin-next@^14.2.3": - version "14.2.30" - resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-14.2.30.tgz#852651bc31a7a01d84a54d95903f48cde6c8bc01" - integrity sha512-mvVsMIutMxQ4NGZEMZ1kiBNc+la8Xmlk30bKUmCPQz2eFkmsLv54Mha8QZarMaCtSPkkFA1TMD+FIZk0l/PpzA== + version "14.2.35" + resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-14.2.35.tgz#96eaf075dfe8cb8b4604af716c783444ba0ccc96" + integrity sha512-Jw9A3ICz2183qSsqwi7fgq4SBPiNfmOLmTPXKvlnzstUwyvBrtySiY+8RXJweNAs9KThb1+bYhZh9XWcNOr2zQ== dependencies: glob "10.3.10" -"@next/swc-darwin-arm64@14.2.30": - version "14.2.30" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.30.tgz#8179a35a068bc6f43a9ab6439875f6e330d02e52" - integrity sha512-EAqfOTb3bTGh9+ewpO/jC59uACadRHM6TSA9DdxJB/6gxOpyV+zrbqeXiFTDy9uV6bmipFDkfpAskeaDcO+7/g== - -"@next/swc-darwin-x64@14.2.30": - version "14.2.30" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.30.tgz#87c08d805c0546a73c25a0538a81f8b5f43bd0e9" - integrity sha512-TyO7Wz1IKE2kGv8dwQ0bmPL3s44EKVencOqwIY69myoS3rdpO1NPg5xPM5ymKu7nfX4oYJrpMxv8G9iqLsnL4A== - -"@next/swc-linux-arm64-gnu@14.2.30": - version "14.2.30" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.30.tgz#eed26d87d96d9ef6fffbde98ceed2c75108a9911" - integrity sha512-I5lg1fgPJ7I5dk6mr3qCH1hJYKJu1FsfKSiTKoYwcuUf53HWTrEkwmMI0t5ojFKeA6Vu+SfT2zVy5NS0QLXV4Q== - -"@next/swc-linux-arm64-musl@14.2.30": - version "14.2.30" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.30.tgz#54b38b43c8acf3d3e0b71ae208a0bfca5a9b8563" - integrity sha512-8GkNA+sLclQyxgzCDs2/2GSwBc92QLMrmYAmoP2xehe5MUKBLB2cgo34Yu242L1siSkwQkiV4YLdCnjwc/Micw== - -"@next/swc-linux-x64-gnu@14.2.30": - version "14.2.30" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.30.tgz#0ee0419da4dc1211a4c925b0841419cd07aa6c59" - integrity sha512-8Ly7okjssLuBoe8qaRCcjGtcMsv79hwzn/63wNeIkzJVFVX06h5S737XNr7DZwlsbTBDOyI6qbL2BJB5n6TV/w== - -"@next/swc-linux-x64-musl@14.2.30": - version "14.2.30" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.30.tgz#e88463d8c10dd600087b062f2dea59a515cd66f6" - integrity sha512-dBmV1lLNeX4mR7uI7KNVHsGQU+OgTG5RGFPi3tBJpsKPvOPtg9poyav/BYWrB3GPQL4dW5YGGgalwZ79WukbKQ== - -"@next/swc-win32-arm64-msvc@14.2.30": - version "14.2.30" - resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.30.tgz#6975cbbab74d519b06d93210ed86cd4f3dbc1c4d" - integrity sha512-6MMHi2Qc1Gkq+4YLXAgbYslE1f9zMGBikKMdmQRHXjkGPot1JY3n5/Qrbg40Uvbi8//wYnydPnyvNhI1DMUW1g== - -"@next/swc-win32-ia32-msvc@14.2.30": - version "14.2.30" - resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.30.tgz#08ad4de2e082bc6b07d41099b4310daec7885748" - integrity sha512-pVZMnFok5qEX4RT59mK2hEVtJX+XFfak+/rjHpyFh7juiT52r177bfFKhnlafm0UOSldhXjj32b+LZIOdswGTg== - -"@next/swc-win32-x64-msvc@14.2.30": - version "14.2.30" - resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.30.tgz#94d3ddcc1e97572a0514a6180c8e3bb415e1dc98" - integrity sha512-4KCo8hMZXMjpTzs3HOqOGYYwAXymXIy7PEPAXNEcEOyKqkjiDlECumrWziy+JEF0Oi4ILHGxzgQ3YiMGG2t/Lg== +"@next/swc-darwin-arm64@14.2.33": + version "14.2.33" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.33.tgz#9e74a4223f1e5e39ca4f9f85709e0d95b869b298" + integrity sha512-HqYnb6pxlsshoSTubdXKu15g3iivcbsMXg4bYpjL2iS/V6aQot+iyF4BUc2qA/J/n55YtvE4PHMKWBKGCF/+wA== + +"@next/swc-darwin-x64@14.2.33": + version "14.2.33" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.33.tgz#fcf0c45938da9b0cc2ec86357d6aefca90bd17f3" + integrity sha512-8HGBeAE5rX3jzKvF593XTTFg3gxeU4f+UWnswa6JPhzaR6+zblO5+fjltJWIZc4aUalqTclvN2QtTC37LxvZAA== + +"@next/swc-linux-arm64-gnu@14.2.33": + version "14.2.33" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.33.tgz#837f91a740eb4420c06f34c4677645315479d9be" + integrity sha512-JXMBka6lNNmqbkvcTtaX8Gu5by9547bukHQvPoLe9VRBx1gHwzf5tdt4AaezW85HAB3pikcvyqBToRTDA4DeLw== + +"@next/swc-linux-arm64-musl@14.2.33": + version "14.2.33" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.33.tgz#dc8903469e5c887b25e3c2217a048bd30c58d3d4" + integrity sha512-Bm+QulsAItD/x6Ih8wGIMfRJy4G73tu1HJsrccPW6AfqdZd0Sfm5Imhgkgq2+kly065rYMnCOxTBvmvFY1BKfg== + +"@next/swc-linux-x64-gnu@14.2.33": + version "14.2.33" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.33.tgz#344438be592b6b28cc540194274561e41f9933e5" + integrity sha512-FnFn+ZBgsVMbGDsTqo8zsnRzydvsGV8vfiWwUo1LD8FTmPTdV+otGSWKc4LJec0oSexFnCYVO4hX8P8qQKaSlg== + +"@next/swc-linux-x64-musl@14.2.33": + version "14.2.33" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.33.tgz#3379fad5e0181000b2a4fac0b80f7ca4ffe795c8" + integrity sha512-345tsIWMzoXaQndUTDv1qypDRiebFxGYx9pYkhwY4hBRaOLt8UGfiWKr9FSSHs25dFIf8ZqIFaPdy5MljdoawA== + +"@next/swc-win32-arm64-msvc@14.2.33": + version "14.2.33" + resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.33.tgz#bca8f4dde34656aef8e99f1e5696de255c2f00e5" + integrity sha512-nscpt0G6UCTkrT2ppnJnFsYbPDQwmum4GNXYTeoTIdsmMydSKFz9Iny2jpaRupTb+Wl298+Rh82WKzt9LCcqSQ== + +"@next/swc-win32-ia32-msvc@14.2.33": + version "14.2.33" + resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.33.tgz#a69c581483ea51dd3b8907ce33bb101fe07ec1df" + integrity sha512-pc9LpGNKhJ0dXQhZ5QMmYxtARwwmWLpeocFmVG5Z0DzWq5Uf0izcI8tLc+qOpqxO1PWqZ5A7J1blrUIKrIFc7Q== + +"@next/swc-win32-x64-msvc@14.2.33": + version "14.2.33" + resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.33.tgz#f1a40062530c17c35a86d8c430b3ae465eb7cea1" + integrity sha512-nOjfZMy8B94MdisuzZo9/57xuFVLHJaDj5e/xrduJp9CV2/HrfxTRH2fbyLe+K9QT41WBLUd4iXX3R7jBp0EUg== "@nivo/annotations@0.88.0": version "0.88.0" @@ -1168,10 +1145,10 @@ resolved "https://registry.yarnpkg.com/@radix-ui/number/-/number-1.1.1.tgz#7b2c9225fbf1b126539551f5985769d0048d9090" integrity sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g== -"@radix-ui/primitive@1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@radix-ui/primitive/-/primitive-1.1.2.tgz#83f415c4425f21e3d27914c12b3272a32e3dae65" - integrity sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA== +"@radix-ui/primitive@1.1.3": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@radix-ui/primitive/-/primitive-1.1.3.tgz#e2dbc13bdc5e4168f4334f75832d7bdd3e2de5ba" + integrity sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg== "@radix-ui/react-accessible-icon@1.1.7": version "1.1.7" @@ -1180,13 +1157,13 @@ dependencies: "@radix-ui/react-visually-hidden" "1.2.3" -"@radix-ui/react-accordion@1.2.11": - version "1.2.11" - resolved "https://registry.yarnpkg.com/@radix-ui/react-accordion/-/react-accordion-1.2.11.tgz#7837dd4d44aeed56aabad2b098727b8b4f89ae4c" - integrity sha512-l3W5D54emV2ues7jjeG1xcyN7S3jnK3zE2zHqgn0CmMsy9lNJwmgcrmaxS+7ipw15FAivzKNzH3d5EcGoFKw0A== +"@radix-ui/react-accordion@1.2.12": + version "1.2.12" + resolved "https://registry.yarnpkg.com/@radix-ui/react-accordion/-/react-accordion-1.2.12.tgz#1fd70d4ef36018012b9e03324ff186de7a29c13f" + integrity sha512-T4nygeh9YE9dLRPhAHSeOZi7HBXo+0kYIPJXayZfvWOWA0+n3dESrZbjfDPUABkUNym6Hd+f2IR113To8D2GPA== dependencies: - "@radix-ui/primitive" "1.1.2" - "@radix-ui/react-collapsible" "1.1.11" + "@radix-ui/primitive" "1.1.3" + "@radix-ui/react-collapsible" "1.1.12" "@radix-ui/react-collection" "1.1.7" "@radix-ui/react-compose-refs" "1.1.2" "@radix-ui/react-context" "1.1.2" @@ -1195,15 +1172,15 @@ "@radix-ui/react-primitive" "2.1.3" "@radix-ui/react-use-controllable-state" "1.2.2" -"@radix-ui/react-alert-dialog@1.1.14": - version "1.1.14" - resolved "https://registry.yarnpkg.com/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.1.14.tgz#b38853b6859b9c7351d9aa52dd504a6bb2ea283c" - integrity sha512-IOZfZ3nPvN6lXpJTBCunFQPRSvK8MDgSc1FB85xnIpUKOw9en0dJj8JmCAxV7BiZdtYlUpmrQjoTFkVYtdoWzQ== +"@radix-ui/react-alert-dialog@1.1.15": + version "1.1.15" + resolved "https://registry.yarnpkg.com/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.1.15.tgz#fa751d0fdd9aa2a90961c9901dba18e638dd4b41" + integrity sha512-oTVLkEw5GpdRe29BqJ0LSDFWI3qu0vR1M0mUkOQWDIUnY/QIkLpgDMWuKxP94c2NAC2LGcgVhG1ImF3jkZ5wXw== dependencies: - "@radix-ui/primitive" "1.1.2" + "@radix-ui/primitive" "1.1.3" "@radix-ui/react-compose-refs" "1.1.2" "@radix-ui/react-context" "1.1.2" - "@radix-ui/react-dialog" "1.1.14" + "@radix-ui/react-dialog" "1.1.15" "@radix-ui/react-primitive" "2.1.3" "@radix-ui/react-slot" "1.2.3" @@ -1232,30 +1209,30 @@ "@radix-ui/react-use-is-hydrated" "0.1.0" "@radix-ui/react-use-layout-effect" "1.1.1" -"@radix-ui/react-checkbox@1.3.2": - version "1.3.2" - resolved "https://registry.yarnpkg.com/@radix-ui/react-checkbox/-/react-checkbox-1.3.2.tgz#28097244d968aa8f93249b0d3df02a172fd4bee5" - integrity sha512-yd+dI56KZqawxKZrJ31eENUwqc1QSqg4OZ15rybGjF2ZNwMO+wCyHzAVLRp9qoYJf7kYy0YpZ2b0JCzJ42HZpA== +"@radix-ui/react-checkbox@1.3.3": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@radix-ui/react-checkbox/-/react-checkbox-1.3.3.tgz#db45ca8a6d5c056a92f74edbb564acee05318b79" + integrity sha512-wBbpv+NQftHDdG86Qc0pIyXk5IR3tM8Vd0nWLKDcX8nNn4nXFOFwsKuqw2okA/1D/mpaAkmuyndrPJTYDNZtFw== dependencies: - "@radix-ui/primitive" "1.1.2" + "@radix-ui/primitive" "1.1.3" "@radix-ui/react-compose-refs" "1.1.2" "@radix-ui/react-context" "1.1.2" - "@radix-ui/react-presence" "1.1.4" + "@radix-ui/react-presence" "1.1.5" "@radix-ui/react-primitive" "2.1.3" "@radix-ui/react-use-controllable-state" "1.2.2" "@radix-ui/react-use-previous" "1.1.1" "@radix-ui/react-use-size" "1.1.1" -"@radix-ui/react-collapsible@1.1.11": - version "1.1.11" - resolved "https://registry.yarnpkg.com/@radix-ui/react-collapsible/-/react-collapsible-1.1.11.tgz#a2d132d5baa6f14551f15b1fff29f925cae46b83" - integrity sha512-2qrRsVGSCYasSz1RFOorXwl0H7g7J1frQtgpQgYrt+MOidtPAINHn9CPovQXb83r8ahapdx3Tu0fa/pdFFSdPg== +"@radix-ui/react-collapsible@1.1.12": + version "1.1.12" + resolved "https://registry.yarnpkg.com/@radix-ui/react-collapsible/-/react-collapsible-1.1.12.tgz#e2cc69a4490a2920f97c3c3150b0bf21281e3c49" + integrity sha512-Uu+mSh4agx2ib1uIGPP4/CKNULyajb3p92LsVXmH2EHVMTfZWpll88XJ0j4W0z3f8NK1eYl1+Mf/szHPmcHzyA== dependencies: - "@radix-ui/primitive" "1.1.2" + "@radix-ui/primitive" "1.1.3" "@radix-ui/react-compose-refs" "1.1.2" "@radix-ui/react-context" "1.1.2" "@radix-ui/react-id" "1.1.1" - "@radix-ui/react-presence" "1.1.4" + "@radix-ui/react-presence" "1.1.5" "@radix-ui/react-primitive" "2.1.3" "@radix-ui/react-use-controllable-state" "1.2.2" "@radix-ui/react-use-layout-effect" "1.1.1" @@ -1275,14 +1252,14 @@ resolved "https://registry.yarnpkg.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz#a2c4c47af6337048ee78ff6dc0d090b390d2bb30" integrity sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg== -"@radix-ui/react-context-menu@2.2.15": - version "2.2.15" - resolved "https://registry.yarnpkg.com/@radix-ui/react-context-menu/-/react-context-menu-2.2.15.tgz#d61a7c8badbcad72fadb9ed87efaa21df60b0a99" - integrity sha512-UsQUMjcYTsBjTSXw0P3GO0werEQvUY2plgRQuKoCTtkNr45q1DiL51j4m7gxhABzZ0BadoXNsIbg7F3KwiUBbw== +"@radix-ui/react-context-menu@2.2.16": + version "2.2.16" + resolved "https://registry.yarnpkg.com/@radix-ui/react-context-menu/-/react-context-menu-2.2.16.tgz#e7bf94a457b68af08f24ad696949144530faab50" + integrity sha512-O8morBEW+HsVG28gYDZPTrT9UUovQUlJue5YO836tiTJhuIWBm/zQHc7j388sHWtdH/xUZurK9olD2+pcqx5ww== dependencies: - "@radix-ui/primitive" "1.1.2" + "@radix-ui/primitive" "1.1.3" "@radix-ui/react-context" "1.1.2" - "@radix-ui/react-menu" "2.1.15" + "@radix-ui/react-menu" "2.1.16" "@radix-ui/react-primitive" "2.1.3" "@radix-ui/react-use-callback-ref" "1.1.1" "@radix-ui/react-use-controllable-state" "1.2.2" @@ -1292,20 +1269,20 @@ resolved "https://registry.yarnpkg.com/@radix-ui/react-context/-/react-context-1.1.2.tgz#61628ef269a433382c364f6f1e3788a6dc213a36" integrity sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA== -"@radix-ui/react-dialog@1.1.14": - version "1.1.14" - resolved "https://registry.yarnpkg.com/@radix-ui/react-dialog/-/react-dialog-1.1.14.tgz#4c69c80c258bc6561398cfce055202ea11075107" - integrity sha512-+CpweKjqpzTmwRwcYECQcNYbI8V9VSQt0SNFKeEBLgfucbsLssU6Ppq7wUdNXEGb573bMjFhVjKVll8rmV6zMw== +"@radix-ui/react-dialog@1.1.15": + version "1.1.15" + resolved "https://registry.yarnpkg.com/@radix-ui/react-dialog/-/react-dialog-1.1.15.tgz#1de3d7a7e9a17a9874d29c07f5940a18a119b632" + integrity sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw== dependencies: - "@radix-ui/primitive" "1.1.2" + "@radix-ui/primitive" "1.1.3" "@radix-ui/react-compose-refs" "1.1.2" "@radix-ui/react-context" "1.1.2" - "@radix-ui/react-dismissable-layer" "1.1.10" - "@radix-ui/react-focus-guards" "1.1.2" + "@radix-ui/react-dismissable-layer" "1.1.11" + "@radix-ui/react-focus-guards" "1.1.3" "@radix-ui/react-focus-scope" "1.1.7" "@radix-ui/react-id" "1.1.1" "@radix-ui/react-portal" "1.1.9" - "@radix-ui/react-presence" "1.1.4" + "@radix-ui/react-presence" "1.1.5" "@radix-ui/react-primitive" "2.1.3" "@radix-ui/react-slot" "1.2.3" "@radix-ui/react-use-controllable-state" "1.2.2" @@ -1317,34 +1294,34 @@ resolved "https://registry.yarnpkg.com/@radix-ui/react-direction/-/react-direction-1.1.1.tgz#39e5a5769e676c753204b792fbe6cf508e550a14" integrity sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw== -"@radix-ui/react-dismissable-layer@1.1.10": - version "1.1.10" - resolved "https://registry.yarnpkg.com/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.10.tgz#429b9bada3672c6895a5d6a642aca6ecaf4f18c3" - integrity sha512-IM1zzRV4W3HtVgftdQiiOmA0AdJlCtMLe00FXaHwgt3rAnNsIyDqshvkIW3hj/iu5hu8ERP7KIYki6NkqDxAwQ== +"@radix-ui/react-dismissable-layer@1.1.11": + version "1.1.11" + resolved "https://registry.yarnpkg.com/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz#e33ab6f6bdaa00f8f7327c408d9f631376b88b37" + integrity sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg== dependencies: - "@radix-ui/primitive" "1.1.2" + "@radix-ui/primitive" "1.1.3" "@radix-ui/react-compose-refs" "1.1.2" "@radix-ui/react-primitive" "2.1.3" "@radix-ui/react-use-callback-ref" "1.1.1" "@radix-ui/react-use-escape-keydown" "1.1.1" -"@radix-ui/react-dropdown-menu@2.1.15": - version "2.1.15" - resolved "https://registry.yarnpkg.com/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.15.tgz#f507320de8e11bc1e671a6ec0c27a7a89e725131" - integrity sha512-mIBnOjgwo9AH3FyKaSWoSu/dYj6VdhJ7frEPiGTeXCdUFHjl9h3mFh2wwhEtINOmYXWhdpf1rY2minFsmaNgVQ== +"@radix-ui/react-dropdown-menu@2.1.16": + version "2.1.16" + resolved "https://registry.yarnpkg.com/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.16.tgz#5ee045c62bad8122347981c479d92b1ff24c7254" + integrity sha512-1PLGQEynI/3OX/ftV54COn+3Sud/Mn8vALg2rWnBLnRaGtJDduNW/22XjlGgPdpcIbiQxjKtb7BkcjP00nqfJw== dependencies: - "@radix-ui/primitive" "1.1.2" + "@radix-ui/primitive" "1.1.3" "@radix-ui/react-compose-refs" "1.1.2" "@radix-ui/react-context" "1.1.2" "@radix-ui/react-id" "1.1.1" - "@radix-ui/react-menu" "2.1.15" + "@radix-ui/react-menu" "2.1.16" "@radix-ui/react-primitive" "2.1.3" "@radix-ui/react-use-controllable-state" "1.2.2" -"@radix-ui/react-focus-guards@1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.2.tgz#4ec9a7e50925f7fb661394460045b46212a33bed" - integrity sha512-fyjAACV62oPV925xFCrH8DR5xWhg9KYtJT4s3u54jxp+L/hbpTY2kIeEFFbFe+a/HCE94zGQMZLIpVTPVZDhaA== +"@radix-ui/react-focus-guards@1.1.3": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz#2a5669e464ad5fde9f86d22f7fdc17781a4dfa7f" + integrity sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw== "@radix-ui/react-focus-scope@1.1.7": version "1.1.7" @@ -1355,30 +1332,30 @@ "@radix-ui/react-primitive" "2.1.3" "@radix-ui/react-use-callback-ref" "1.1.1" -"@radix-ui/react-form@0.1.7": - version "0.1.7" - resolved "https://registry.yarnpkg.com/@radix-ui/react-form/-/react-form-0.1.7.tgz#acbf7ef57bbed1e76ea1f2d3b18eff465519435c" - integrity sha512-IXLKFnaYvFg/KkeV5QfOX7tRnwHXp127koOFUjLWMTrRv5Rny3DQcAtIFFeA/Cli4HHM8DuJCXAUsgnFVJndlw== +"@radix-ui/react-form@0.1.8": + version "0.1.8" + resolved "https://registry.yarnpkg.com/@radix-ui/react-form/-/react-form-0.1.8.tgz#daec0fde305a70edf1a97b932b5e02a4cbf5b68e" + integrity sha512-QM70k4Zwjttifr5a4sZFts9fn8FzHYvQ5PiB19O2HsYibaHSVt9fH9rzB0XZo/YcM+b7t/p7lYCT/F5eOeF5yQ== dependencies: - "@radix-ui/primitive" "1.1.2" + "@radix-ui/primitive" "1.1.3" "@radix-ui/react-compose-refs" "1.1.2" "@radix-ui/react-context" "1.1.2" "@radix-ui/react-id" "1.1.1" "@radix-ui/react-label" "2.1.7" "@radix-ui/react-primitive" "2.1.3" -"@radix-ui/react-hover-card@1.1.14": - version "1.1.14" - resolved "https://registry.yarnpkg.com/@radix-ui/react-hover-card/-/react-hover-card-1.1.14.tgz#a557cda6470e214e744e46ede839496e8b291843" - integrity sha512-CPYZ24Mhirm+g6D8jArmLzjYu4Eyg3TTUHswR26QgzXBHBe64BO/RHOJKzmF/Dxb4y4f9PKyJdwm/O/AhNkb+Q== +"@radix-ui/react-hover-card@1.1.15": + version "1.1.15" + resolved "https://registry.yarnpkg.com/@radix-ui/react-hover-card/-/react-hover-card-1.1.15.tgz#9bc7ed55c37a9032acdfcc7cfa5c73b117cffe5e" + integrity sha512-qgTkjNT1CfKMoP0rcasmlH2r1DAiYicWsDsufxl940sT2wHNEWWv6FMWIQXWhVdmC1d/HYfbhQx60KYyAtKxjg== dependencies: - "@radix-ui/primitive" "1.1.2" + "@radix-ui/primitive" "1.1.3" "@radix-ui/react-compose-refs" "1.1.2" "@radix-ui/react-context" "1.1.2" - "@radix-ui/react-dismissable-layer" "1.1.10" - "@radix-ui/react-popper" "1.2.7" + "@radix-ui/react-dismissable-layer" "1.1.11" + "@radix-ui/react-popper" "1.2.8" "@radix-ui/react-portal" "1.1.9" - "@radix-ui/react-presence" "1.1.4" + "@radix-ui/react-presence" "1.1.5" "@radix-ui/react-primitive" "2.1.3" "@radix-ui/react-use-controllable-state" "1.2.2" @@ -1396,59 +1373,59 @@ dependencies: "@radix-ui/react-primitive" "2.1.3" -"@radix-ui/react-menu@2.1.15": - version "2.1.15" - resolved "https://registry.yarnpkg.com/@radix-ui/react-menu/-/react-menu-2.1.15.tgz#a1a8f06cab3c309f9998cdbd2b3ad279e42ed483" - integrity sha512-tVlmA3Vb9n8SZSd+YSbuFR66l87Wiy4du+YE+0hzKQEANA+7cWKH1WgqcEX4pXqxUFQKrWQGHdvEfw00TjFiew== +"@radix-ui/react-menu@2.1.16": + version "2.1.16" + resolved "https://registry.yarnpkg.com/@radix-ui/react-menu/-/react-menu-2.1.16.tgz#528a5a973c3a7413d3d49eb9ccd229aa52402911" + integrity sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg== dependencies: - "@radix-ui/primitive" "1.1.2" + "@radix-ui/primitive" "1.1.3" "@radix-ui/react-collection" "1.1.7" "@radix-ui/react-compose-refs" "1.1.2" "@radix-ui/react-context" "1.1.2" "@radix-ui/react-direction" "1.1.1" - "@radix-ui/react-dismissable-layer" "1.1.10" - "@radix-ui/react-focus-guards" "1.1.2" + "@radix-ui/react-dismissable-layer" "1.1.11" + "@radix-ui/react-focus-guards" "1.1.3" "@radix-ui/react-focus-scope" "1.1.7" "@radix-ui/react-id" "1.1.1" - "@radix-ui/react-popper" "1.2.7" + "@radix-ui/react-popper" "1.2.8" "@radix-ui/react-portal" "1.1.9" - "@radix-ui/react-presence" "1.1.4" + "@radix-ui/react-presence" "1.1.5" "@radix-ui/react-primitive" "2.1.3" - "@radix-ui/react-roving-focus" "1.1.10" + "@radix-ui/react-roving-focus" "1.1.11" "@radix-ui/react-slot" "1.2.3" "@radix-ui/react-use-callback-ref" "1.1.1" aria-hidden "^1.2.4" react-remove-scroll "^2.6.3" -"@radix-ui/react-menubar@1.1.15": - version "1.1.15" - resolved "https://registry.yarnpkg.com/@radix-ui/react-menubar/-/react-menubar-1.1.15.tgz#e597bdf53c9ddeaadf2efadc480cc58a411fa914" - integrity sha512-Z71C7LGD+YDYo3TV81paUs8f3Zbmkvg6VLRQpKYfzioOE6n7fOhA3ApK/V/2Odolxjoc4ENk8AYCjohCNayd5A== +"@radix-ui/react-menubar@1.1.16": + version "1.1.16" + resolved "https://registry.yarnpkg.com/@radix-ui/react-menubar/-/react-menubar-1.1.16.tgz#5edf7ea2ff7aa7e3ba896b35cf577f122160121c" + integrity sha512-EB1FktTz5xRRi2Er974AUQZWg2yVBb1yjip38/lgwtCVRd3a+maUoGHN/xs9Yv8SY8QwbSEb+YrxGadVWbEutA== dependencies: - "@radix-ui/primitive" "1.1.2" + "@radix-ui/primitive" "1.1.3" "@radix-ui/react-collection" "1.1.7" "@radix-ui/react-compose-refs" "1.1.2" "@radix-ui/react-context" "1.1.2" "@radix-ui/react-direction" "1.1.1" "@radix-ui/react-id" "1.1.1" - "@radix-ui/react-menu" "2.1.15" + "@radix-ui/react-menu" "2.1.16" "@radix-ui/react-primitive" "2.1.3" - "@radix-ui/react-roving-focus" "1.1.10" + "@radix-ui/react-roving-focus" "1.1.11" "@radix-ui/react-use-controllable-state" "1.2.2" -"@radix-ui/react-navigation-menu@1.2.13": - version "1.2.13" - resolved "https://registry.yarnpkg.com/@radix-ui/react-navigation-menu/-/react-navigation-menu-1.2.13.tgz#8d49ce275bf4f49a8642be520074b5a7438a5fb0" - integrity sha512-WG8wWfDiJlSF5hELjwfjSGOXcBR/ZMhBFCGYe8vERpC39CQYZeq1PQ2kaYHdye3V95d06H89KGMsVCIE4LWo3g== +"@radix-ui/react-navigation-menu@1.2.14": + version "1.2.14" + resolved "https://registry.yarnpkg.com/@radix-ui/react-navigation-menu/-/react-navigation-menu-1.2.14.tgz#4e6d1172be3c89752e564f8721706f78574ad7dd" + integrity sha512-YB9mTFQvCOAQMHU+C/jVl96WmuWeltyUEpRJJky51huhds5W2FQr1J8D/16sQlf0ozxkPK8uF3niQMdUwZPv5w== dependencies: - "@radix-ui/primitive" "1.1.2" + "@radix-ui/primitive" "1.1.3" "@radix-ui/react-collection" "1.1.7" "@radix-ui/react-compose-refs" "1.1.2" "@radix-ui/react-context" "1.1.2" "@radix-ui/react-direction" "1.1.1" - "@radix-ui/react-dismissable-layer" "1.1.10" + "@radix-ui/react-dismissable-layer" "1.1.11" "@radix-ui/react-id" "1.1.1" - "@radix-ui/react-presence" "1.1.4" + "@radix-ui/react-presence" "1.1.5" "@radix-ui/react-primitive" "2.1.3" "@radix-ui/react-use-callback-ref" "1.1.1" "@radix-ui/react-use-controllable-state" "1.2.2" @@ -1456,30 +1433,30 @@ "@radix-ui/react-use-previous" "1.1.1" "@radix-ui/react-visually-hidden" "1.2.3" -"@radix-ui/react-one-time-password-field@0.1.7": - version "0.1.7" - resolved "https://registry.yarnpkg.com/@radix-ui/react-one-time-password-field/-/react-one-time-password-field-0.1.7.tgz#4778008364be4c83c9a00665c5f8ff93ceb47f87" - integrity sha512-w1vm7AGI8tNXVovOK7TYQHrAGpRF7qQL+ENpT1a743De5Zmay2RbWGKAiYDKIyIuqptns+znCKwNztE2xl1n0Q== +"@radix-ui/react-one-time-password-field@0.1.8": + version "0.1.8" + resolved "https://registry.yarnpkg.com/@radix-ui/react-one-time-password-field/-/react-one-time-password-field-0.1.8.tgz#edb7476d29478477ffc837f7deacec3a1ae08a24" + integrity sha512-ycS4rbwURavDPVjCb5iS3aG4lURFDILi6sKI/WITUMZ13gMmn/xGjpLoqBAalhJaDk8I3UbCM5GzKHrnzwHbvg== dependencies: "@radix-ui/number" "1.1.1" - "@radix-ui/primitive" "1.1.2" + "@radix-ui/primitive" "1.1.3" "@radix-ui/react-collection" "1.1.7" "@radix-ui/react-compose-refs" "1.1.2" "@radix-ui/react-context" "1.1.2" "@radix-ui/react-direction" "1.1.1" "@radix-ui/react-primitive" "2.1.3" - "@radix-ui/react-roving-focus" "1.1.10" + "@radix-ui/react-roving-focus" "1.1.11" "@radix-ui/react-use-controllable-state" "1.2.2" "@radix-ui/react-use-effect-event" "0.0.2" "@radix-ui/react-use-is-hydrated" "0.1.0" "@radix-ui/react-use-layout-effect" "1.1.1" -"@radix-ui/react-password-toggle-field@0.1.2": - version "0.1.2" - resolved "https://registry.yarnpkg.com/@radix-ui/react-password-toggle-field/-/react-password-toggle-field-0.1.2.tgz#369679f34145bdcde716ac349e206e25c908555a" - integrity sha512-F90uYnlBsLPU1UbSLciLsWQmk8+hdWa6SFw4GXaIdNWxFxI5ITKVdAG64f+Twaa9ic6xE7pqxPyUmodrGjT4pQ== +"@radix-ui/react-password-toggle-field@0.1.3": + version "0.1.3" + resolved "https://registry.yarnpkg.com/@radix-ui/react-password-toggle-field/-/react-password-toggle-field-0.1.3.tgz#3d47de91c0f8e79d697cefde2ef8146816712031" + integrity sha512-/UuCrDBWravcaMix4TdT+qlNdVwOM1Nck9kWx/vafXsdfj1ChfhOdfi3cy9SGBpWgTXwYCuboT/oYpJy3clqfw== dependencies: - "@radix-ui/primitive" "1.1.2" + "@radix-ui/primitive" "1.1.3" "@radix-ui/react-compose-refs" "1.1.2" "@radix-ui/react-context" "1.1.2" "@radix-ui/react-id" "1.1.1" @@ -1488,31 +1465,31 @@ "@radix-ui/react-use-effect-event" "0.0.2" "@radix-ui/react-use-is-hydrated" "0.1.0" -"@radix-ui/react-popover@1.1.14": - version "1.1.14" - resolved "https://registry.yarnpkg.com/@radix-ui/react-popover/-/react-popover-1.1.14.tgz#5496d1986f0287cdfc77e73f70a887e4cb77ad08" - integrity sha512-ODz16+1iIbGUfFEfKx2HTPKizg2MN39uIOV8MXeHnmdd3i/N9Wt7vU46wbHsqA0xoaQyXVcs0KIlBdOA2Y95bw== +"@radix-ui/react-popover@1.1.15": + version "1.1.15" + resolved "https://registry.yarnpkg.com/@radix-ui/react-popover/-/react-popover-1.1.15.tgz#9c852f93990a687ebdc949b2c3de1f37cdc4c5d5" + integrity sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA== dependencies: - "@radix-ui/primitive" "1.1.2" + "@radix-ui/primitive" "1.1.3" "@radix-ui/react-compose-refs" "1.1.2" "@radix-ui/react-context" "1.1.2" - "@radix-ui/react-dismissable-layer" "1.1.10" - "@radix-ui/react-focus-guards" "1.1.2" + "@radix-ui/react-dismissable-layer" "1.1.11" + "@radix-ui/react-focus-guards" "1.1.3" "@radix-ui/react-focus-scope" "1.1.7" "@radix-ui/react-id" "1.1.1" - "@radix-ui/react-popper" "1.2.7" + "@radix-ui/react-popper" "1.2.8" "@radix-ui/react-portal" "1.1.9" - "@radix-ui/react-presence" "1.1.4" + "@radix-ui/react-presence" "1.1.5" "@radix-ui/react-primitive" "2.1.3" "@radix-ui/react-slot" "1.2.3" "@radix-ui/react-use-controllable-state" "1.2.2" aria-hidden "^1.2.4" react-remove-scroll "^2.6.3" -"@radix-ui/react-popper@1.2.7": - version "1.2.7" - resolved "https://registry.yarnpkg.com/@radix-ui/react-popper/-/react-popper-1.2.7.tgz#531cf2eebb3d3270d58f7d8136e4517646429978" - integrity sha512-IUFAccz1JyKcf/RjB552PlWwxjeCJB8/4KxT7EhBHOJM+mN7LdW+B3kacJXILm32xawcMMjb2i0cIZpo+f9kiQ== +"@radix-ui/react-popper@1.2.8": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@radix-ui/react-popper/-/react-popper-1.2.8.tgz#a79f39cdd2b09ab9fb50bf95250918422c4d9602" + integrity sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw== dependencies: "@floating-ui/react-dom" "^2.0.0" "@radix-ui/react-arrow" "1.1.7" @@ -1533,10 +1510,10 @@ "@radix-ui/react-primitive" "2.1.3" "@radix-ui/react-use-layout-effect" "1.1.1" -"@radix-ui/react-presence@1.1.4": - version "1.1.4" - resolved "https://registry.yarnpkg.com/@radix-ui/react-presence/-/react-presence-1.1.4.tgz#253ac0ad4946c5b4a9c66878335f5cf07c967ced" - integrity sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA== +"@radix-ui/react-presence@1.1.5": + version "1.1.5" + resolved "https://registry.yarnpkg.com/@radix-ui/react-presence/-/react-presence-1.1.5.tgz#5d8f28ac316c32f078afce2996839250c10693db" + integrity sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ== dependencies: "@radix-ui/react-compose-refs" "1.1.2" "@radix-ui/react-use-layout-effect" "1.1.1" @@ -1556,28 +1533,28 @@ "@radix-ui/react-context" "1.1.2" "@radix-ui/react-primitive" "2.1.3" -"@radix-ui/react-radio-group@1.3.7": - version "1.3.7" - resolved "https://registry.yarnpkg.com/@radix-ui/react-radio-group/-/react-radio-group-1.3.7.tgz#49f822d97c26c4745976108a301ba2e8545d8928" - integrity sha512-9w5XhD0KPOrm92OTTE0SysH3sYzHsSTHNvZgUBo/VZ80VdYyB5RneDbc0dKpURS24IxkoFRu/hI0i4XyfFwY6g== +"@radix-ui/react-radio-group@1.3.8": + version "1.3.8" + resolved "https://registry.yarnpkg.com/@radix-ui/react-radio-group/-/react-radio-group-1.3.8.tgz#93f102b5b948d602c2f2adb1bc5c347cbaf64bd9" + integrity sha512-VBKYIYImA5zsxACdisNQ3BjCBfmbGH3kQlnFVqlWU4tXwjy7cGX8ta80BcrO+WJXIn5iBylEH3K6ZTlee//lgQ== dependencies: - "@radix-ui/primitive" "1.1.2" + "@radix-ui/primitive" "1.1.3" "@radix-ui/react-compose-refs" "1.1.2" "@radix-ui/react-context" "1.1.2" "@radix-ui/react-direction" "1.1.1" - "@radix-ui/react-presence" "1.1.4" + "@radix-ui/react-presence" "1.1.5" "@radix-ui/react-primitive" "2.1.3" - "@radix-ui/react-roving-focus" "1.1.10" + "@radix-ui/react-roving-focus" "1.1.11" "@radix-ui/react-use-controllable-state" "1.2.2" "@radix-ui/react-use-previous" "1.1.1" "@radix-ui/react-use-size" "1.1.1" -"@radix-ui/react-roving-focus@1.1.10": - version "1.1.10" - resolved "https://registry.yarnpkg.com/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.10.tgz#46030496d2a490c4979d29a7e1252465e51e4b0b" - integrity sha512-dT9aOXUen9JSsxnMPv/0VqySQf5eDQ6LCk5Sw28kamz8wSOW2bJdlX2Bg5VUIIcV+6XlHpWTIuTPCf/UNIyq8Q== +"@radix-ui/react-roving-focus@1.1.11": + version "1.1.11" + resolved "https://registry.yarnpkg.com/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.11.tgz#ef54384b7361afc6480dcf9907ef2fedb5080fd9" + integrity sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA== dependencies: - "@radix-ui/primitive" "1.1.2" + "@radix-ui/primitive" "1.1.3" "@radix-ui/react-collection" "1.1.7" "@radix-ui/react-compose-refs" "1.1.2" "@radix-ui/react-context" "1.1.2" @@ -1587,37 +1564,37 @@ "@radix-ui/react-use-callback-ref" "1.1.1" "@radix-ui/react-use-controllable-state" "1.2.2" -"@radix-ui/react-scroll-area@1.2.9": - version "1.2.9" - resolved "https://registry.yarnpkg.com/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.9.tgz#90c49bd3231d7f0796d5d12dabc065afa829cf07" - integrity sha512-YSjEfBXnhUELsO2VzjdtYYD4CfQjvao+lhhrX5XsHD7/cyUNzljF1FHEbgTPN7LH2MClfwRMIsYlqTYpKTTe2A== +"@radix-ui/react-scroll-area@1.2.10": + version "1.2.10" + resolved "https://registry.yarnpkg.com/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.10.tgz#e4fd3b4a79bb77bec1a52f0c8f26d8f3f1ca4b22" + integrity sha512-tAXIa1g3sM5CGpVT0uIbUx/U3Gs5N8T52IICuCtObaos1S8fzsrPXG5WObkQN3S6NVl6wKgPhAIiBGbWnvc97A== dependencies: "@radix-ui/number" "1.1.1" - "@radix-ui/primitive" "1.1.2" + "@radix-ui/primitive" "1.1.3" "@radix-ui/react-compose-refs" "1.1.2" "@radix-ui/react-context" "1.1.2" "@radix-ui/react-direction" "1.1.1" - "@radix-ui/react-presence" "1.1.4" + "@radix-ui/react-presence" "1.1.5" "@radix-ui/react-primitive" "2.1.3" "@radix-ui/react-use-callback-ref" "1.1.1" "@radix-ui/react-use-layout-effect" "1.1.1" -"@radix-ui/react-select@2.2.5": - version "2.2.5" - resolved "https://registry.yarnpkg.com/@radix-ui/react-select/-/react-select-2.2.5.tgz#9e2fa5b8f4cc99b86ef5bba3cb9b73828afb51f0" - integrity sha512-HnMTdXEVuuyzx63ME0ut4+sEMYW6oouHWNGUZc7ddvUWIcfCva/AMoqEW/3wnEllriMWBa0RHspCYnfCWJQYmA== +"@radix-ui/react-select@2.2.6": + version "2.2.6" + resolved "https://registry.yarnpkg.com/@radix-ui/react-select/-/react-select-2.2.6.tgz#022cf8dab16bf05d0d1b4df9e53e4bea1b744fd9" + integrity sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ== dependencies: "@radix-ui/number" "1.1.1" - "@radix-ui/primitive" "1.1.2" + "@radix-ui/primitive" "1.1.3" "@radix-ui/react-collection" "1.1.7" "@radix-ui/react-compose-refs" "1.1.2" "@radix-ui/react-context" "1.1.2" "@radix-ui/react-direction" "1.1.1" - "@radix-ui/react-dismissable-layer" "1.1.10" - "@radix-ui/react-focus-guards" "1.1.2" + "@radix-ui/react-dismissable-layer" "1.1.11" + "@radix-ui/react-focus-guards" "1.1.3" "@radix-ui/react-focus-scope" "1.1.7" "@radix-ui/react-id" "1.1.1" - "@radix-ui/react-popper" "1.2.7" + "@radix-ui/react-popper" "1.2.8" "@radix-ui/react-portal" "1.1.9" "@radix-ui/react-primitive" "2.1.3" "@radix-ui/react-slot" "1.2.3" @@ -1636,13 +1613,13 @@ dependencies: "@radix-ui/react-primitive" "2.1.3" -"@radix-ui/react-slider@1.3.5": - version "1.3.5" - resolved "https://registry.yarnpkg.com/@radix-ui/react-slider/-/react-slider-1.3.5.tgz#f9c074dc0dd2850aa42609e72de74642a4851b79" - integrity sha512-rkfe2pU2NBAYfGaxa3Mqosi7VZEWX5CxKaanRv0vZd4Zhl9fvQrg0VM93dv3xGLGfrHuoTRF3JXH8nb9g+B3fw== +"@radix-ui/react-slider@1.3.6": + version "1.3.6" + resolved "https://registry.yarnpkg.com/@radix-ui/react-slider/-/react-slider-1.3.6.tgz#409453110b8f34ca00972750b80cd792f0b23a8c" + integrity sha512-JPYb1GuM1bxfjMRlNLE+BcmBC8onfCi60Blk7OBqi2MLTFdS+8401U4uFjnwkOr49BLmXxLC6JHkvAsx5OJvHw== dependencies: "@radix-ui/number" "1.1.1" - "@radix-ui/primitive" "1.1.2" + "@radix-ui/primitive" "1.1.3" "@radix-ui/react-collection" "1.1.7" "@radix-ui/react-compose-refs" "1.1.2" "@radix-ui/react-context" "1.1.2" @@ -1660,12 +1637,12 @@ dependencies: "@radix-ui/react-compose-refs" "1.1.2" -"@radix-ui/react-switch@1.2.5": - version "1.2.5" - resolved "https://registry.yarnpkg.com/@radix-ui/react-switch/-/react-switch-1.2.5.tgz#56c15a4cd219e00b0745ec6b2ea1c0feeb0b21d0" - integrity sha512-5ijLkak6ZMylXsaImpZ8u4Rlf5grRmoc0p0QeX9VJtlrM4f5m3nCTX8tWga/zOA8PZYIR/t0p2Mnvd7InrJ6yQ== +"@radix-ui/react-switch@1.2.6": + version "1.2.6" + resolved "https://registry.yarnpkg.com/@radix-ui/react-switch/-/react-switch-1.2.6.tgz#ff79acb831f0d5ea9216cfcc5b939912571358e3" + integrity sha512-bByzr1+ep1zk4VubeEVViV592vu2lHE2BZY5OnzehZqOOgogN80+mNtCqPkhn2gklJqOpxWgPoYTSnhBCqpOXQ== dependencies: - "@radix-ui/primitive" "1.1.2" + "@radix-ui/primitive" "1.1.3" "@radix-ui/react-compose-refs" "1.1.2" "@radix-ui/react-context" "1.1.2" "@radix-ui/react-primitive" "2.1.3" @@ -1673,86 +1650,86 @@ "@radix-ui/react-use-previous" "1.1.1" "@radix-ui/react-use-size" "1.1.1" -"@radix-ui/react-tabs@1.1.12": - version "1.1.12" - resolved "https://registry.yarnpkg.com/@radix-ui/react-tabs/-/react-tabs-1.1.12.tgz#99b3522c73db9263f429a6d0f5a9acb88df3b129" - integrity sha512-GTVAlRVrQrSw3cEARM0nAx73ixrWDPNZAruETn3oHCNP6SbZ/hNxdxp+u7VkIEv3/sFoLq1PfcHrl7Pnp0CDpw== +"@radix-ui/react-tabs@1.1.13": + version "1.1.13" + resolved "https://registry.yarnpkg.com/@radix-ui/react-tabs/-/react-tabs-1.1.13.tgz#3537ce379d7e7ff4eeb6b67a0973e139c2ac1f15" + integrity sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A== dependencies: - "@radix-ui/primitive" "1.1.2" + "@radix-ui/primitive" "1.1.3" "@radix-ui/react-context" "1.1.2" "@radix-ui/react-direction" "1.1.1" "@radix-ui/react-id" "1.1.1" - "@radix-ui/react-presence" "1.1.4" + "@radix-ui/react-presence" "1.1.5" "@radix-ui/react-primitive" "2.1.3" - "@radix-ui/react-roving-focus" "1.1.10" + "@radix-ui/react-roving-focus" "1.1.11" "@radix-ui/react-use-controllable-state" "1.2.2" -"@radix-ui/react-toast@1.2.14": - version "1.2.14" - resolved "https://registry.yarnpkg.com/@radix-ui/react-toast/-/react-toast-1.2.14.tgz#ac021bdde74792fe8613c510eb6944f0fbcf57b0" - integrity sha512-nAP5FBxBJGQ/YfUB+r+O6USFVkWq3gAInkxyEnmvEV5jtSbfDhfa4hwX8CraCnbjMLsE7XSf/K75l9xXY7joWg== +"@radix-ui/react-toast@1.2.15": + version "1.2.15" + resolved "https://registry.yarnpkg.com/@radix-ui/react-toast/-/react-toast-1.2.15.tgz#746cf9a81297ddbfba214e5c81245ea3f706f876" + integrity sha512-3OSz3TacUWy4WtOXV38DggwxoqJK4+eDkNMl5Z/MJZaoUPaP4/9lf81xXMe1I2ReTAptverZUpbPY4wWwWyL5g== dependencies: - "@radix-ui/primitive" "1.1.2" + "@radix-ui/primitive" "1.1.3" "@radix-ui/react-collection" "1.1.7" "@radix-ui/react-compose-refs" "1.1.2" "@radix-ui/react-context" "1.1.2" - "@radix-ui/react-dismissable-layer" "1.1.10" + "@radix-ui/react-dismissable-layer" "1.1.11" "@radix-ui/react-portal" "1.1.9" - "@radix-ui/react-presence" "1.1.4" + "@radix-ui/react-presence" "1.1.5" "@radix-ui/react-primitive" "2.1.3" "@radix-ui/react-use-callback-ref" "1.1.1" "@radix-ui/react-use-controllable-state" "1.2.2" "@radix-ui/react-use-layout-effect" "1.1.1" "@radix-ui/react-visually-hidden" "1.2.3" -"@radix-ui/react-toggle-group@1.1.10": - version "1.1.10" - resolved "https://registry.yarnpkg.com/@radix-ui/react-toggle-group/-/react-toggle-group-1.1.10.tgz#4406b3be3869cad497ca7ee4993c3598731f774e" - integrity sha512-kiU694Km3WFLTC75DdqgM/3Jauf3rD9wxeS9XtyWFKsBUeZA337lC+6uUazT7I1DhanZ5gyD5Stf8uf2dbQxOQ== +"@radix-ui/react-toggle-group@1.1.11": + version "1.1.11" + resolved "https://registry.yarnpkg.com/@radix-ui/react-toggle-group/-/react-toggle-group-1.1.11.tgz#e513d6ffdb07509b400ab5b26f2523747c0d51c1" + integrity sha512-5umnS0T8JQzQT6HbPyO7Hh9dgd82NmS36DQr+X/YJ9ctFNCiiQd6IJAYYZ33LUwm8M+taCz5t2ui29fHZc4Y6Q== dependencies: - "@radix-ui/primitive" "1.1.2" + "@radix-ui/primitive" "1.1.3" "@radix-ui/react-context" "1.1.2" "@radix-ui/react-direction" "1.1.1" "@radix-ui/react-primitive" "2.1.3" - "@radix-ui/react-roving-focus" "1.1.10" - "@radix-ui/react-toggle" "1.1.9" + "@radix-ui/react-roving-focus" "1.1.11" + "@radix-ui/react-toggle" "1.1.10" "@radix-ui/react-use-controllable-state" "1.2.2" -"@radix-ui/react-toggle@1.1.9": - version "1.1.9" - resolved "https://registry.yarnpkg.com/@radix-ui/react-toggle/-/react-toggle-1.1.9.tgz#9cb99a29bc7cd15186ba3ba797808a013a726fba" - integrity sha512-ZoFkBBz9zv9GWer7wIjvdRxmh2wyc2oKWw6C6CseWd6/yq1DK/l5lJ+wnsmFwJZbBYqr02mrf8A2q/CVCuM3ZA== +"@radix-ui/react-toggle@1.1.10": + version "1.1.10" + resolved "https://registry.yarnpkg.com/@radix-ui/react-toggle/-/react-toggle-1.1.10.tgz#b04ba0f9609599df666fce5b2f38109a197f08cf" + integrity sha512-lS1odchhFTeZv3xwHH31YPObmJn8gOg7Lq12inrr0+BH/l3Tsq32VfjqH1oh80ARM3mlkfMic15n0kg4sD1poQ== dependencies: - "@radix-ui/primitive" "1.1.2" + "@radix-ui/primitive" "1.1.3" "@radix-ui/react-primitive" "2.1.3" "@radix-ui/react-use-controllable-state" "1.2.2" -"@radix-ui/react-toolbar@1.1.10": - version "1.1.10" - resolved "https://registry.yarnpkg.com/@radix-ui/react-toolbar/-/react-toolbar-1.1.10.tgz#15245475fb7bb3552b76e0577bac034a3d3f253c" - integrity sha512-jiwQsduEL++M4YBIurjSa+voD86OIytCod0/dbIxFZDLD8NfO1//keXYMfsW8BPcfqwoNjt+y06XcJqAb4KR7A== +"@radix-ui/react-toolbar@1.1.11": + version "1.1.11" + resolved "https://registry.yarnpkg.com/@radix-ui/react-toolbar/-/react-toolbar-1.1.11.tgz#2a71f1d91535788f88145d542159e2faaa561db7" + integrity sha512-4ol06/1bLoFu1nwUqzdD4Y5RZ9oDdKeiHIsntug54Hcr1pgaHiPqHFEaXI1IFP/EsOfROQZ8Mig9VTIRza6Tjg== dependencies: - "@radix-ui/primitive" "1.1.2" + "@radix-ui/primitive" "1.1.3" "@radix-ui/react-context" "1.1.2" "@radix-ui/react-direction" "1.1.1" "@radix-ui/react-primitive" "2.1.3" - "@radix-ui/react-roving-focus" "1.1.10" + "@radix-ui/react-roving-focus" "1.1.11" "@radix-ui/react-separator" "1.1.7" - "@radix-ui/react-toggle-group" "1.1.10" + "@radix-ui/react-toggle-group" "1.1.11" -"@radix-ui/react-tooltip@1.2.7": - version "1.2.7" - resolved "https://registry.yarnpkg.com/@radix-ui/react-tooltip/-/react-tooltip-1.2.7.tgz#23612ac7a5e8e1f6829e46d0e0ad94afe3976c72" - integrity sha512-Ap+fNYwKTYJ9pzqW+Xe2HtMRbQ/EeWkj2qykZ6SuEV4iS/o1bZI5ssJbk4D2r8XuDuOBVz/tIx2JObtuqU+5Zw== +"@radix-ui/react-tooltip@1.2.8": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@radix-ui/react-tooltip/-/react-tooltip-1.2.8.tgz#3f50267e25bccfc9e20bb3036bfd9ab4c2c30c2c" + integrity sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg== dependencies: - "@radix-ui/primitive" "1.1.2" + "@radix-ui/primitive" "1.1.3" "@radix-ui/react-compose-refs" "1.1.2" "@radix-ui/react-context" "1.1.2" - "@radix-ui/react-dismissable-layer" "1.1.10" + "@radix-ui/react-dismissable-layer" "1.1.11" "@radix-ui/react-id" "1.1.1" - "@radix-ui/react-popper" "1.2.7" + "@radix-ui/react-popper" "1.2.8" "@radix-ui/react-portal" "1.1.9" - "@radix-ui/react-presence" "1.1.4" + "@radix-ui/react-presence" "1.1.5" "@radix-ui/react-primitive" "2.1.3" "@radix-ui/react-slot" "1.2.3" "@radix-ui/react-use-controllable-state" "1.2.2" @@ -1875,7 +1852,7 @@ "@reactflow/background@11.3.14": version "11.3.14" - resolved "https://registry.npmmirror.com/@reactflow/background/-/background-11.3.14.tgz#778ca30174f3de77fc321459ab3789e66e71a699" + resolved "https://registry.yarnpkg.com/@reactflow/background/-/background-11.3.14.tgz#778ca30174f3de77fc321459ab3789e66e71a699" integrity sha512-Gewd7blEVT5Lh6jqrvOgd4G6Qk17eGKQfsDXgyRSqM+CTwDqRldG2LsWN4sNeno6sbqVIC2fZ+rAUBFA9ZEUDA== dependencies: "@reactflow/core" "11.11.4" @@ -1884,7 +1861,7 @@ "@reactflow/controls@11.2.14": version "11.2.14" - resolved "https://registry.npmmirror.com/@reactflow/controls/-/controls-11.2.14.tgz#508ed2c40d23341b3b0919dd11e76fd49cf850c7" + resolved "https://registry.yarnpkg.com/@reactflow/controls/-/controls-11.2.14.tgz#508ed2c40d23341b3b0919dd11e76fd49cf850c7" integrity sha512-MiJp5VldFD7FrqaBNIrQ85dxChrG6ivuZ+dcFhPQUwOK3HfYgX2RHdBua+gx+40p5Vw5It3dVNp/my4Z3jF0dw== dependencies: "@reactflow/core" "11.11.4" @@ -1893,7 +1870,7 @@ "@reactflow/core@11.11.4": version "11.11.4" - resolved "https://registry.npmmirror.com/@reactflow/core/-/core-11.11.4.tgz#89bd86d1862aa1416f3f49926cede7e8c2aab6a7" + resolved "https://registry.yarnpkg.com/@reactflow/core/-/core-11.11.4.tgz#89bd86d1862aa1416f3f49926cede7e8c2aab6a7" integrity sha512-H4vODklsjAq3AMq6Np4LE12i1I4Ta9PrDHuBR9GmL8uzTt2l2jh4CiQbEMpvMDcp7xi4be0hgXj+Ysodde/i7Q== dependencies: "@types/d3" "^7.4.0" @@ -1908,7 +1885,7 @@ "@reactflow/minimap@11.7.14": version "11.7.14" - resolved "https://registry.npmmirror.com/@reactflow/minimap/-/minimap-11.7.14.tgz#298d7a63cb1da06b2518c99744f716560c88ca73" + resolved "https://registry.yarnpkg.com/@reactflow/minimap/-/minimap-11.7.14.tgz#298d7a63cb1da06b2518c99744f716560c88ca73" integrity sha512-mpwLKKrEAofgFJdkhwR5UQ1JYWlcAAL/ZU/bctBkuNTT1yqV+y0buoNVImsRehVYhJwffSWeSHaBR5/GJjlCSQ== dependencies: "@reactflow/core" "11.11.4" @@ -1921,7 +1898,7 @@ "@reactflow/node-resizer@2.2.14": version "2.2.14" - resolved "https://registry.npmmirror.com/@reactflow/node-resizer/-/node-resizer-2.2.14.tgz#1810c0ce51aeb936f179466a6660d1e02c7a77a8" + resolved "https://registry.yarnpkg.com/@reactflow/node-resizer/-/node-resizer-2.2.14.tgz#1810c0ce51aeb936f179466a6660d1e02c7a77a8" integrity sha512-fwqnks83jUlYr6OHcdFEedumWKChTHRGw/kbCxj0oqBd+ekfs+SIp4ddyNU0pdx96JIm5iNFS0oNrmEiJbbSaA== dependencies: "@reactflow/core" "11.11.4" @@ -1932,7 +1909,7 @@ "@reactflow/node-toolbar@1.3.14": version "1.3.14" - resolved "https://registry.npmmirror.com/@reactflow/node-toolbar/-/node-toolbar-1.3.14.tgz#c6ffc76f82acacdce654f2160dc9852162d6e7c9" + resolved "https://registry.yarnpkg.com/@reactflow/node-toolbar/-/node-toolbar-1.3.14.tgz#c6ffc76f82acacdce654f2160dc9852162d6e7c9" integrity sha512-rbynXQnH/xFNu4P9H+hVqlEUafDCkEoCy0Dg9mG22Sg+rY/0ck6KkrAQrYrTgXusd+cEJOMK0uOOFCK2/5rSGQ== dependencies: "@reactflow/core" "11.11.4" @@ -1944,110 +1921,135 @@ resolved "https://registry.yarnpkg.com/@remirror/core-constants/-/core-constants-3.0.0.tgz#96fdb89d25c62e7b6a5d08caf0ce5114370e3b8f" integrity sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg== -"@rolldown/pluginutils@1.0.0-beta.19": - version "1.0.0-beta.19" - resolved "https://registry.yarnpkg.com/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.19.tgz#fc3b95145a8e7a3bf92754269d8e4f40eea8a244" - integrity sha512-3FL3mnMbPu0muGOCaKAhhFEYmqv9eTfPSJRJmANrCwtgK8VuxpsZDGK+m0LYAGoyO8+0j5uRe4PeyPDK1yA/hA== - -"@rollup/rollup-android-arm-eabi@4.44.2": - version "4.44.2" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.44.2.tgz#6819b7f1e41a49af566f629a1556eaeea774d043" - integrity sha512-g0dF8P1e2QYPOj1gu7s/3LVP6kze9A7m6x0BZ9iTdXK8N5c2V7cpBKHV3/9A4Zd8xxavdhK0t4PnqjkqVmUc9Q== - -"@rollup/rollup-android-arm64@4.44.2": - version "4.44.2" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.44.2.tgz#7bd5591af68c64a75be1779e2b20f187878daba9" - integrity sha512-Yt5MKrOosSbSaAK5Y4J+vSiID57sOvpBNBR6K7xAaQvk3MkcNVV0f9fE20T+41WYN8hDn6SGFlFrKudtx4EoxA== - -"@rollup/rollup-darwin-arm64@4.44.2": - version "4.44.2" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.44.2.tgz#e216c333e448c67973386e46dbfe8e381aafb055" - integrity sha512-EsnFot9ZieM35YNA26nhbLTJBHD0jTwWpPwmRVDzjylQT6gkar+zenfb8mHxWpRrbn+WytRRjE0WKsfaxBkVUA== - -"@rollup/rollup-darwin-x64@4.44.2": - version "4.44.2" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.44.2.tgz#202f80eea3acfe3f67496fedffa006a5f1ce7f5a" - integrity sha512-dv/t1t1RkCvJdWWxQ2lWOO+b7cMsVw5YFaS04oHpZRWehI1h0fV1gF4wgGCTyQHHjJDfbNpwOi6PXEafRBBezw== - -"@rollup/rollup-freebsd-arm64@4.44.2": - version "4.44.2" - resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.44.2.tgz#4880f9769f1a7eec436b9c146e1d714338c26567" - integrity sha512-W4tt4BLorKND4qeHElxDoim0+BsprFTwb+vriVQnFFtT/P6v/xO5I99xvYnVzKWrK6j7Hb0yp3x7V5LUbaeOMg== - -"@rollup/rollup-freebsd-x64@4.44.2": - version "4.44.2" - resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.44.2.tgz#647d6e333349b1c0fb322c2827ba1a53a0f10301" - integrity sha512-tdT1PHopokkuBVyHjvYehnIe20fxibxFCEhQP/96MDSOcyjM/shlTkZZLOufV3qO6/FQOSiJTBebhVc12JyPTA== - -"@rollup/rollup-linux-arm-gnueabihf@4.44.2": - version "4.44.2" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.44.2.tgz#7ba5c97a7224f49618861d093c4a7b40fa50867b" - integrity sha512-+xmiDGGaSfIIOXMzkhJ++Oa0Gwvl9oXUeIiwarsdRXSe27HUIvjbSIpPxvnNsRebsNdUo7uAiQVgBD1hVriwSQ== - -"@rollup/rollup-linux-arm-musleabihf@4.44.2": - version "4.44.2" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.44.2.tgz#f858dcf498299d6c625ec697a5191e0e41423905" - integrity sha512-bDHvhzOfORk3wt8yxIra8N4k/N0MnKInCW5OGZaeDYa/hMrdPaJzo7CSkjKZqX4JFUWjUGm88lI6QJLCM7lDrA== - -"@rollup/rollup-linux-arm64-gnu@4.44.2": - version "4.44.2" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.44.2.tgz#c0f1fc20c50666c61f574536a00cdd486b6aaae1" - integrity sha512-NMsDEsDiYghTbeZWEGnNi4F0hSbGnsuOG+VnNvxkKg0IGDvFh7UVpM/14mnMwxRxUf9AdAVJgHPvKXf6FpMB7A== - -"@rollup/rollup-linux-arm64-musl@4.44.2": - version "4.44.2" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.44.2.tgz#0214efc3e404ddf108e946ad5f7e4ee2792a155a" - integrity sha512-lb5bxXnxXglVq+7imxykIp5xMq+idehfl+wOgiiix0191av84OqbjUED+PRC5OA8eFJYj5xAGcpAZ0pF2MnW+A== - -"@rollup/rollup-linux-loongarch64-gnu@4.44.2": - version "4.44.2" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.44.2.tgz#8303c4ea2ae7bcbb96b2c77cfb53527d964bfceb" - integrity sha512-Yl5Rdpf9pIc4GW1PmkUGHdMtbx0fBLE1//SxDmuf3X0dUC57+zMepow2LK0V21661cjXdTn8hO2tXDdAWAqE5g== - -"@rollup/rollup-linux-powerpc64le-gnu@4.44.2": - version "4.44.2" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.44.2.tgz#4197ffbc61809629094c0fccf825e43a40fbc0ca" - integrity sha512-03vUDH+w55s680YYryyr78jsO1RWU9ocRMaeV2vMniJJW/6HhoTBwyyiiTPVHNWLnhsnwcQ0oH3S9JSBEKuyqw== - -"@rollup/rollup-linux-riscv64-gnu@4.44.2": - version "4.44.2" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.44.2.tgz#bcb99c9004c9b91e3704a6a70c892cb0599b1f42" - integrity sha512-iYtAqBg5eEMG4dEfVlkqo05xMOk6y/JXIToRca2bAWuqjrJYJlx/I7+Z+4hSrsWU8GdJDFPL4ktV3dy4yBSrzg== - -"@rollup/rollup-linux-riscv64-musl@4.44.2": - version "4.44.2" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.44.2.tgz#3e943bae9b8b4637c573c1922392beb8a5e81acb" - integrity sha512-e6vEbgaaqz2yEHqtkPXa28fFuBGmUJ0N2dOJK8YUfijejInt9gfCSA7YDdJ4nYlv67JfP3+PSWFX4IVw/xRIPg== - -"@rollup/rollup-linux-s390x-gnu@4.44.2": - version "4.44.2" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.44.2.tgz#dc43fb467bff9547f5b9937f38668da07fa8fa9f" - integrity sha512-evFOtkmVdY3udE+0QKrV5wBx7bKI0iHz5yEVx5WqDJkxp9YQefy4Mpx3RajIVcM6o7jxTvVd/qpC1IXUhGc1Mw== - -"@rollup/rollup-linux-x64-gnu@4.44.2": - version "4.44.2" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.44.2.tgz#0699c560fa6ce6b846581a7e6c30c85c22a3f0da" - integrity sha512-/bXb0bEsWMyEkIsUL2Yt5nFB5naLAwyOWMEviQfQY1x3l5WsLKgvZf66TM7UTfED6erckUVUJQ/jJ1FSpm3pRQ== - -"@rollup/rollup-linux-x64-musl@4.44.2": - version "4.44.2" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.44.2.tgz#9fb1becedcdc9e227d4748576eb8ba2fad8d2e29" - integrity sha512-3D3OB1vSSBXmkGEZR27uiMRNiwN08/RVAcBKwhUYPaiZ8bcvdeEwWPvbnXvvXHY+A/7xluzcN+kaiOFNiOZwWg== - -"@rollup/rollup-win32-arm64-msvc@4.44.2": - version "4.44.2" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.44.2.tgz#fcf3e62edd76c560252b819f69627685f65887d7" - integrity sha512-VfU0fsMK+rwdK8mwODqYeM2hDrF2WiHaSmCBrS7gColkQft95/8tphyzv2EupVxn3iE0FI78wzffoULH1G+dkw== - -"@rollup/rollup-win32-ia32-msvc@4.44.2": - version "4.44.2" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.44.2.tgz#45a5304491d6da4666f6159be4f739d4d43a283f" - integrity sha512-+qMUrkbUurpE6DVRjiJCNGZBGo9xM4Y0FXU5cjgudWqIBWbcLkjE3XprJUsOFgC6xjBClwVa9k6O3A7K3vxb5Q== - -"@rollup/rollup-win32-x64-msvc@4.44.2": - version "4.44.2" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.44.2.tgz#660018c9696ad4f48abe8c5d56db53c81aadba25" - integrity sha512-3+QZROYfJ25PDcxFF66UEk8jGWigHJeecZILvkPkyQN7oc5BvFo4YEXFkOs154j3FTMp9mn9Ky8RCOwastduEA== +"@rolldown/pluginutils@1.0.0-beta.27": + version "1.0.0-beta.27" + resolved "https://registry.yarnpkg.com/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz#47d2bf4cef6d470b22f5831b420f8964e0bf755f" + integrity sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA== + +"@rollup/rollup-android-arm-eabi@4.57.1": + version "4.57.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.57.1.tgz#add5e608d4e7be55bc3ca3d962490b8b1890e088" + integrity sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg== + +"@rollup/rollup-android-arm64@4.57.1": + version "4.57.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.57.1.tgz#10bd0382b73592beee6e9800a69401a29da625c4" + integrity sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w== + +"@rollup/rollup-darwin-arm64@4.57.1": + version "4.57.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.57.1.tgz#1e99ab04c0b8c619dd7bbde725ba2b87b55bfd81" + integrity sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg== + +"@rollup/rollup-darwin-x64@4.57.1": + version "4.57.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.57.1.tgz#69e741aeb2839d2e8f0da2ce7a33d8bd23632423" + integrity sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w== + +"@rollup/rollup-freebsd-arm64@4.57.1": + version "4.57.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.57.1.tgz#3736c232a999c7bef7131355d83ebdf9651a0839" + integrity sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug== + +"@rollup/rollup-freebsd-x64@4.57.1": + version "4.57.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.57.1.tgz#227dcb8f466684070169942bd3998901c9bfc065" + integrity sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q== + +"@rollup/rollup-linux-arm-gnueabihf@4.57.1": + version "4.57.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.57.1.tgz#ba004b30df31b724f99ce66e7128248bea17cb0c" + integrity sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw== + +"@rollup/rollup-linux-arm-musleabihf@4.57.1": + version "4.57.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.57.1.tgz#6929f3e07be6b6da5991f63c6b68b3e473d0a65a" + integrity sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw== + +"@rollup/rollup-linux-arm64-gnu@4.57.1": + version "4.57.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.57.1.tgz#06e89fd4a25d21fe5575d60b6f913c0e65297bfa" + integrity sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g== + +"@rollup/rollup-linux-arm64-musl@4.57.1": + version "4.57.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.57.1.tgz#fddabf395b90990d5194038e6cd8c00156ed8ac0" + integrity sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q== + +"@rollup/rollup-linux-loong64-gnu@4.57.1": + version "4.57.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.57.1.tgz#04c10bb764bbf09a3c1bd90432e92f58d6603c36" + integrity sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA== + +"@rollup/rollup-linux-loong64-musl@4.57.1": + version "4.57.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.57.1.tgz#f2450361790de80581d8687ea19142d8a4de5c0f" + integrity sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw== + +"@rollup/rollup-linux-ppc64-gnu@4.57.1": + version "4.57.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.57.1.tgz#0474f4667259e407eee1a6d38e29041b708f6a30" + integrity sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w== + +"@rollup/rollup-linux-ppc64-musl@4.57.1": + version "4.57.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.57.1.tgz#9f32074819eeb1ddbe51f50ea9dcd61a6745ec33" + integrity sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw== + +"@rollup/rollup-linux-riscv64-gnu@4.57.1": + version "4.57.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.57.1.tgz#3fdb9d4b1e29fb6b6a6da9f15654d42eb77b99b2" + integrity sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A== + +"@rollup/rollup-linux-riscv64-musl@4.57.1": + version "4.57.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.57.1.tgz#1de780d64e6be0e3e8762035c22e0d8ea68df8ed" + integrity sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw== + +"@rollup/rollup-linux-s390x-gnu@4.57.1": + version "4.57.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.57.1.tgz#1da022ffd2d9e9f0fd8344ea49e113001fbcac64" + integrity sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg== + +"@rollup/rollup-linux-x64-gnu@4.57.1": + version "4.57.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.57.1.tgz#78c16eef9520bd10e1ea7a112593bb58e2842622" + integrity sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg== + +"@rollup/rollup-linux-x64-musl@4.57.1": + version "4.57.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.57.1.tgz#a7598591b4d9af96cb3167b50a5bf1e02dfea06c" + integrity sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw== + +"@rollup/rollup-openbsd-x64@4.57.1": + version "4.57.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.57.1.tgz#c51d48c07cd6c466560e5bed934aec688ce02614" + integrity sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw== + +"@rollup/rollup-openharmony-arm64@4.57.1": + version "4.57.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.57.1.tgz#f09921d0b2a0b60afbf3586d2a7a7f208ba6df17" + integrity sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ== + +"@rollup/rollup-win32-arm64-msvc@4.57.1": + version "4.57.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.57.1.tgz#08d491717135376e4a99529821c94ecd433d5b36" + integrity sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ== + +"@rollup/rollup-win32-ia32-msvc@4.57.1": + version "4.57.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.57.1.tgz#b0c12aac1104a8b8f26a5e0098e5facbb3e3964a" + integrity sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew== + +"@rollup/rollup-win32-x64-gnu@4.57.1": + version "4.57.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.57.1.tgz#b9cccef26f5e6fdc013bf3c0911a3c77428509d0" + integrity sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ== + +"@rollup/rollup-win32-x64-msvc@4.57.1": + version "4.57.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.57.1.tgz#a03348e7b559c792b6277cc58874b89ef46e1e72" + integrity sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA== "@rtsao/scc@^1.1.0": version "1.1.0" @@ -2067,136 +2069,136 @@ "@swc/counter" "^0.1.3" tslib "^2.4.0" -"@tiptap/core@^2.25.0", "@tiptap/core@^2.9.1": - version "2.25.0" - resolved "https://registry.yarnpkg.com/@tiptap/core/-/core-2.25.0.tgz#201cbe1b118fc524472431097c06589cc47b6b64" - integrity sha512-pTLV0+g+SBL49/Y5A9ii7oHwlzIzpgroJVI3AcBk7/SeR7554ZzjxxtJmZkQ9/NxJO+k1jQp9grXaqqOLqC7cA== +"@tiptap/core@^2.27.2", "@tiptap/core@^2.9.1": + version "2.27.2" + resolved "https://registry.yarnpkg.com/@tiptap/core/-/core-2.27.2.tgz#679eef9ce673d7243ce28d303852a98cbd1844be" + integrity sha512-ABL1N6eoxzDzC1bYvkMbvyexHacszsKdVPYqhl5GwHLOvpZcv9VE9QaKwDILTyz5voCA0lGcAAXZp+qnXOk5lQ== -"@tiptap/extension-blockquote@^2.25.0": - version "2.25.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-blockquote/-/extension-blockquote-2.25.0.tgz#1b43dfd31c7cf52aa980c92164d53cbe7b5bf4c9" - integrity sha512-W+sVPlV9XmaNPUkxV2BinNEbk2hr4zw8VgKjqKQS9O0k2YIVRCfQch+4DudSAwBVMrVW97zVAKRNfictGFQ8vQ== +"@tiptap/extension-blockquote@^2.27.2": + version "2.27.2" + resolved "https://registry.yarnpkg.com/@tiptap/extension-blockquote/-/extension-blockquote-2.27.2.tgz#af5fccec360cd94b9d3d8751c868d92e9e70907d" + integrity sha512-oIGZgiAeA4tG3YxbTDfrmENL4/CIwGuP3THtHsNhwRqwsl9SfMk58Ucopi2GXTQSdYXpRJ0ahE6nPqB5D6j/Zw== -"@tiptap/extension-bold@^2.25.0": - version "2.25.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-bold/-/extension-bold-2.25.0.tgz#21fa8d9988dd11326ad8a6f68b1e5ceeabe7fca1" - integrity sha512-3cBX2EtdFR3+EDTkIshhpQpXoZQbFUzxf6u86Qm0qD49JnVOjX9iexnUp8MydXPZA6NVsKeEfMhf18gV7oxTEw== +"@tiptap/extension-bold@^2.27.2": + version "2.27.2" + resolved "https://registry.yarnpkg.com/@tiptap/extension-bold/-/extension-bold-2.27.2.tgz#612104c1e9eaba4c9301b21daa7ef19a9e487051" + integrity sha512-bR7J5IwjCGQ0s3CIxyMvOCnMFMzIvsc5OVZKscTN5UkXzFsaY6muUAIqtKxayBUucjtUskm5qZowJITCeCb1/A== -"@tiptap/extension-bubble-menu@^2.25.0": - version "2.25.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.25.0.tgz#f2b298e0ec4c126be6ae94dada08ceae1dec5798" - integrity sha512-BnbfQWRXJDDy9/x/0Atu2Nka5ZAMyXLDFqzSLMAXqXSQcG6CZRTSNRgOCnjpda6Hq2yCtq7l/YEoXkbHT1ZZdQ== +"@tiptap/extension-bubble-menu@^2.27.2": + version "2.27.2" + resolved "https://registry.yarnpkg.com/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.27.2.tgz#f75eb12a8d2496bcde739b5c20684db635a48b9e" + integrity sha512-VkwlCOcr0abTBGzjPXklJ92FCowG7InU8+Od9FyApdLNmn0utRYGRhw0Zno6VgE9EYr1JY4BRnuSa5f9wlR72w== dependencies: tippy.js "^6.3.7" -"@tiptap/extension-bullet-list@^2.25.0": - version "2.25.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-bullet-list/-/extension-bullet-list-2.25.0.tgz#bfade52251b2f1e5b36500e85f449d51a670d2d4" - integrity sha512-KD+q/q6KIU2anedjtjG8vELkL5rYFdNHWc5XcUJgQoxbOCK3/sBuOgcn9mnFA2eAS6UkraN9Yx0BXEDbXX2HOw== +"@tiptap/extension-bullet-list@^2.27.2": + version "2.27.2" + resolved "https://registry.yarnpkg.com/@tiptap/extension-bullet-list/-/extension-bullet-list-2.27.2.tgz#2347683ab898471ab7df2c3e63b20e8d3d7c46f3" + integrity sha512-gmFuKi97u5f8uFc/GQs+zmezjiulZmFiDYTh3trVoLRoc2SAHOjGEB7qxdx7dsqmMN7gwiAWAEVurLKIi1lnnw== -"@tiptap/extension-code-block@^2.25.0": - version "2.25.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-code-block/-/extension-code-block-2.25.0.tgz#b39448f892eb96f2756969b518c1df4b2337e5fc" - integrity sha512-T4kXbZNZ/NyklzQ/FWmUnjD4hgmJPrIBazzCZ/E/rF/Ag2IvUsztBT0PN3vTa+DAZ+IbM61TjlIpyJs1R7OdbQ== +"@tiptap/extension-code-block@^2.27.2": + version "2.27.2" + resolved "https://registry.yarnpkg.com/@tiptap/extension-code-block/-/extension-code-block-2.27.2.tgz#0a622d5bf92c9db55e9f5eaba1a6a8d7a015b1f1" + integrity sha512-KgvdQHS4jXr79aU3wZOGBIZYYl9vCB7uDEuRFV4so2rYrfmiYMw3T8bTnlNEEGe4RUeAms1i4fdwwvQp9nR1Dw== -"@tiptap/extension-code@^2.25.0", "@tiptap/extension-code@^2.9.1": - version "2.25.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-code/-/extension-code-2.25.0.tgz#3c7f3677f0a0aaaa2a2d21bfd29aee04797b261a" - integrity sha512-rRp6X2aNNnvo7Fbqc3olZ0vLb52FlCPPfetr9gy6/M9uQdVYDhJcFOPuRuXtZ8M8X+WpCZBV29BvZFeDqfw8bw== +"@tiptap/extension-code@^2.27.2", "@tiptap/extension-code@^2.9.1": + version "2.27.2" + resolved "https://registry.yarnpkg.com/@tiptap/extension-code/-/extension-code-2.27.2.tgz#bfbaf07f67232144c6865ffbea20896e02c6fe6f" + integrity sha512-7X9AgwqiIGXoZX7uvdHQsGsjILnN/JaEVtqfXZnPECzKGaWHeK/Ao4sYvIIIffsyZJA8k5DC7ny2/0sAgr2TuA== -"@tiptap/extension-document@^2.25.0": - version "2.25.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-document/-/extension-document-2.25.0.tgz#45b32e8b94c015d7295a4370c89ed2aafb0dec2a" - integrity sha512-3gEZlQKUSIRrC6Az8QS7SJi4CvhMWrA7RBChM1aRl9vMNN8Ul7dZZk5StYJGPjL/koTiceMqx9pNmTCBprsbvQ== +"@tiptap/extension-document@^2.27.2": + version "2.27.2" + resolved "https://registry.yarnpkg.com/@tiptap/extension-document/-/extension-document-2.27.2.tgz#697ee04c03c7b37bc37d942d60fcc5fa304988b5" + integrity sha512-CFhAYsPnyYnosDC4639sCJnBUnYH4Cat9qH5NZWHVvdgtDwu8GZgZn2eSzaKSYXWH1vJ9DSlCK+7UyC3SNXIBA== -"@tiptap/extension-dropcursor@^2.25.0": - version "2.25.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-dropcursor/-/extension-dropcursor-2.25.0.tgz#e4b82e110dba395a6bdb91ac44b6e9ccc9c5169b" - integrity sha512-eSHqp+iUI2mGVwvIyENP02hi5TSyQ+bdwNwIck6bdzjRvXakm72+8uPfVSLGxRKAQZ0RFtmux8ISazgUqF/oSw== +"@tiptap/extension-dropcursor@^2.27.2": + version "2.27.2" + resolved "https://registry.yarnpkg.com/@tiptap/extension-dropcursor/-/extension-dropcursor-2.27.2.tgz#c0f62e32a6c7bc7dc8cc6b6edd84d9173bc1db16" + integrity sha512-oEu/OrktNoQXq1x29NnH/GOIzQZm8ieTQl3FK27nxfBPA89cNoH4mFEUmBL5/OFIENIjiYG3qWpg6voIqzswNw== -"@tiptap/extension-floating-menu@^2.25.0": - version "2.25.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-floating-menu/-/extension-floating-menu-2.25.0.tgz#2ab22de58c493b74e8b65435926170a5ee4061de" - integrity sha512-hPZ5SNpI14smTz4GpWQXTnxmeICINYiABSgXcsU5V66tik9OtxKwoCSR/gpU35esaAFUVRdjW7+sGkACLZD5AQ== +"@tiptap/extension-floating-menu@^2.27.2": + version "2.27.2" + resolved "https://registry.yarnpkg.com/@tiptap/extension-floating-menu/-/extension-floating-menu-2.27.2.tgz#b04e8f542d3900db1d845a03a0f5ab079a06daaf" + integrity sha512-GUN6gPIGXS7ngRJOwdSmtBRBDt9Kt9CM/9pSwKebhLJ+honFoNA+Y6IpVyDvvDMdVNgBchiJLs6qA5H97gAePQ== dependencies: tippy.js "^6.3.7" -"@tiptap/extension-gapcursor@^2.25.0": - version "2.25.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-gapcursor/-/extension-gapcursor-2.25.0.tgz#0773e91e1a48122472f3a712b51a908a117ee54d" - integrity sha512-s/3WDbgkvLac88h5iYJLPJCDw8tMhlss1hk9GAo+zzP4h0xfazYie09KrA0CBdfaSOFyeJK3wedzjKZBtdgX4w== +"@tiptap/extension-gapcursor@^2.27.2": + version "2.27.2" + resolved "https://registry.yarnpkg.com/@tiptap/extension-gapcursor/-/extension-gapcursor-2.27.2.tgz#2e82dd87cb2dfcca90f0abb3b43f1f6748a54e2c" + integrity sha512-/c9VF1HBxj+AP54XGVgCmD9bEGYc5w5OofYCFQgM7l7PB1J00A4vOke0oPkHJnqnOOyPlFaxO/7N6l3XwFcnKA== -"@tiptap/extension-hard-break@^2.25.0": - version "2.25.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-hard-break/-/extension-hard-break-2.25.0.tgz#bc9af1582f0c46ec1effb0c9f9e6ff4dfe0b40af" - integrity sha512-h8be5Zdtsl5GQHxRXvYlGfIJsLvdbexflSTr12gr4kvcQqTdtrsqyu2eksfAK+p2szbiwP2G4VZlH0LNS47UXQ== +"@tiptap/extension-hard-break@^2.27.2": + version "2.27.2" + resolved "https://registry.yarnpkg.com/@tiptap/extension-hard-break/-/extension-hard-break-2.27.2.tgz#250200feb316cfb40ed8e9188ee6684c2811b475" + integrity sha512-kSRVGKlCYK6AGR0h8xRkk0WOFGXHIIndod3GKgWU49APuIGDiXd8sziXsSlniUsWmqgDmDXcNnSzPcV7AQ8YNg== -"@tiptap/extension-heading@^2.25.0": - version "2.25.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-heading/-/extension-heading-2.25.0.tgz#930c64e5bfcae10a73b34c740df49a5ad8177b5d" - integrity sha512-IrRKRRr7Bhpnq5aue1v5/e5N/eNdVV/THsgqqpLZO48pgN8Wv+TweOZe1Ntg/v8L4QSBC8iGMxxhiJZT8AzSkA== +"@tiptap/extension-heading@^2.27.2": + version "2.27.2" + resolved "https://registry.yarnpkg.com/@tiptap/extension-heading/-/extension-heading-2.27.2.tgz#10afd812475c6a3f62a26bd1975998bfa94cb9fb" + integrity sha512-iM3yeRWuuQR/IRQ1djwNooJGfn9Jts9zF43qZIUf+U2NY8IlvdNsk2wTOdBgh6E0CamrStPxYGuln3ZS4fuglw== "@tiptap/extension-highlight@^2.9.1": - version "2.25.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-highlight/-/extension-highlight-2.25.0.tgz#5a8547650924eebecdb935900a085849622b02b4" - integrity sha512-YuDZUFTil06wmuIMod1z2zbLGIwDwcoRV21f2wZBl3SryzppX/B1S1fGgLdnOo4M8ryykSKxWdpjMSOYCAdsjA== + version "2.27.2" + resolved "https://registry.yarnpkg.com/@tiptap/extension-highlight/-/extension-highlight-2.27.2.tgz#5647a82ac2e1c04532e0d8dbc15946f58d6151ae" + integrity sha512-ZjlktDdMjruMJFAVz0TbQf0v92Jqkc7Ri1iZJqBXuLid+r+GxUzl2CVAV7qq5yagkGQgvAG+WGsMk880HgR3MA== -"@tiptap/extension-history@^2.25.0": - version "2.25.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-history/-/extension-history-2.25.0.tgz#c215b0a73c1eaa934d495866e5457fb1a2ccda58" - integrity sha512-y3uJkJv+UngDaDYfcVJ4kx8ivc3Etk5ow6N+47AMCRjUUweQ/CLiJwJ2C7nL7L82zOzVbb/NoR/B3UeE4ts/wQ== +"@tiptap/extension-history@^2.27.2": + version "2.27.2" + resolved "https://registry.yarnpkg.com/@tiptap/extension-history/-/extension-history-2.27.2.tgz#43c6d976c521dc1cf2d4a0707df7d8328be0e9a9" + integrity sha512-+hSyqERoFNTWPiZx4/FCyZ/0eFqB9fuMdTB4AC/q9iwu3RNWAQtlsJg5230bf/qmyO6bZxRUc0k8p4hrV6ybAw== -"@tiptap/extension-horizontal-rule@^2.25.0": - version "2.25.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-2.25.0.tgz#ec3675ebc1e65d5cdb39154620c5a4a512dee709" - integrity sha512-bZovyhdOexB3Cv9ddUogWT+cd3KbnenMIZKhgrJ+R0J27rlOtzeUD9TeIjn4V8Of9mTxm3XDKUZGLgPiriN8Ww== +"@tiptap/extension-horizontal-rule@^2.27.2": + version "2.27.2" + resolved "https://registry.yarnpkg.com/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-2.27.2.tgz#7440adb913dfe270577d1853cfc2f725f36e0040" + integrity sha512-WGWUSgX+jCsbtf9Y9OCUUgRZYuwjVoieW5n6mAUohJ9/6gc6sGIOrUpBShf+HHo6WD+gtQjRd+PssmX3NPWMpg== -"@tiptap/extension-italic@^2.25.0": - version "2.25.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-italic/-/extension-italic-2.25.0.tgz#661f78fef61678e967161a5ce0804e099f7d0734" - integrity sha512-FZHmNqvWJ5SHYlUi+Qg3b2C0ZBt82DUDUqM+bqcQqSQu6B0c4IEc3+VHhjAJwEUIO9wX7xk/PsdM4Z5Ex4Lr3w== +"@tiptap/extension-italic@^2.27.2": + version "2.27.2" + resolved "https://registry.yarnpkg.com/@tiptap/extension-italic/-/extension-italic-2.27.2.tgz#91b6ded7b84ed218a8c07ed979332d0dbf923d2b" + integrity sha512-1OFsw2SZqfaqx5Fa5v90iNlPRcqyt+lVSjBwTDzuPxTPFY4Q0mL89mKgkq2gVHYNCiaRkXvFLDxaSvBWbmthgg== "@tiptap/extension-link@^2.9.1": - version "2.25.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-link/-/extension-link-2.25.0.tgz#afe13bc71184ae74344f2e4ef56bab9b40cbc02d" - integrity sha512-jNd+1Fd7wiIbxlS51weBzyDtBEBSVzW0cgzdwOzBYQtPJueRyXNNVERksyinDuVgcfvEWgmNZUylgzu7mehnEg== - dependencies: - linkifyjs "^4.2.0" - -"@tiptap/extension-list-item@^2.25.0": - version "2.25.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-list-item/-/extension-list-item-2.25.0.tgz#9cd552cf88c19b0a4eabcd7a818d9ce00c4107c6" - integrity sha512-HLstO/R+dNjIFMXN15bANc8i/+CDpEgtEQhZNHqvSUJH9xQ5op0S05m5VvFI10qnwXNjwwXdhxUYwwjIDCiAgg== - -"@tiptap/extension-ordered-list@^2.25.0": - version "2.25.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-ordered-list/-/extension-ordered-list-2.25.0.tgz#10954533c8ad30fdf857ccf720cb1f78c481e533" - integrity sha512-Hlid16nQdDFOGOx6mJT+zPEae2t1dGlJ18pqCqaVMuDnIpNIWmQutJk5QYxGVxr9awd2SpHTpQtdBTqcufbHtw== - -"@tiptap/extension-paragraph@^2.25.0": - version "2.25.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-paragraph/-/extension-paragraph-2.25.0.tgz#fdcf6376e099868d3cde3bf64aac67f5500e8102" - integrity sha512-53gpWMPedkWVDp3u/1sLt6vnr3BWz4vArGCmmabLucCI2Yl4R6S/AQ9yj/+jOHvWbXCroCbKtmmwxJl32uGN2w== - -"@tiptap/extension-strike@^2.25.0": - version "2.25.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-strike/-/extension-strike-2.25.0.tgz#d571cb1ef885305862fb37efacc7e4b01f85c3ac" - integrity sha512-Z5YBKnv4N6MMD1LEo9XbmWnmdXavZKOOJt/OkXYFZ3KgzB52Z3q3DDfH+NyeCtKKSWqWVxbBHKLnsojDerSf2g== - -"@tiptap/extension-text-style@^2.25.0": - version "2.25.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-text-style/-/extension-text-style-2.25.0.tgz#b002e76ebf45cdcc0196e9efdeacffe7fdea9c5b" - integrity sha512-MKAXqDATEbuFEB1SeeAFy2VbefUMJ9jxQyybpaHjDX+Ik0Ddu+aYuJP/njvLuejXCqhrkS/AorxzmHUC4HNPbQ== - -"@tiptap/extension-text@^2.25.0": - version "2.25.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-text/-/extension-text-2.25.0.tgz#cc116896c7f0319fb11fe52b07100fa9a2dc8064" - integrity sha512-HlZL86rihpP/R8+dqRrvzSRmiPpx6ctlAKM9PnWT/WRMeI4Y1AUq6PSHLz74wtYO1LH4PXys1ws3n+pLP4Mo6g== - -"@tiptap/pm@^2.25.0", "@tiptap/pm@^2.9.1": - version "2.25.0" - resolved "https://registry.yarnpkg.com/@tiptap/pm/-/pm-2.25.0.tgz#6485276749aa72a05013237a31932e0d77d2244c" - integrity sha512-vuzU0pLGQyHqtikAssHn9V61aXLSQERQtn3MUtaJ36fScQg7RClAK5gnIbBt3Ul3VFof8o4xYmcidARc0X/E5A== + version "2.27.2" + resolved "https://registry.yarnpkg.com/@tiptap/extension-link/-/extension-link-2.27.2.tgz#f250b6119b02f836e0746af4c28766b643b78f6c" + integrity sha512-bnP61qkr0Kj9Cgnop1hxn2zbOCBzNtmawxr92bVTOE31fJv6FhtCnQiD6tuPQVGMYhcmAj7eihtvuEMFfqEPcQ== + dependencies: + linkifyjs "^4.3.2" + +"@tiptap/extension-list-item@^2.27.2": + version "2.27.2" + resolved "https://registry.yarnpkg.com/@tiptap/extension-list-item/-/extension-list-item-2.27.2.tgz#562a8a5f56ed7ac70cd4fab37d7fbcd29e9dc078" + integrity sha512-eJNee7IEGXMnmygM5SdMGDC8m/lMWmwNGf9fPCK6xk0NxuQRgmZHL6uApKcdH6gyNcRPHCqvTTkhEP7pbny/fg== + +"@tiptap/extension-ordered-list@^2.27.2": + version "2.27.2" + resolved "https://registry.yarnpkg.com/@tiptap/extension-ordered-list/-/extension-ordered-list-2.27.2.tgz#12f2c4309512429a0c21863e741db00356573a4b" + integrity sha512-M7A4tLGJcLPYdLC4CI2Gwl8LOrENQW59u3cMVa+KkwG1hzSJyPsbDpa1DI6oXPC2WtYiTf22zrbq3gVvH+KA2w== + +"@tiptap/extension-paragraph@^2.27.2": + version "2.27.2" + resolved "https://registry.yarnpkg.com/@tiptap/extension-paragraph/-/extension-paragraph-2.27.2.tgz#e6873c16993bf21b831ecac41bbd137dc5945eb4" + integrity sha512-elYVn2wHJJ+zB9LESENWOAfI4TNT0jqEN34sMA/hCtA4im1ZG2DdLHwkHIshj/c4H0dzQhmsS/YmNC5Vbqab/A== + +"@tiptap/extension-strike@^2.27.2": + version "2.27.2" + resolved "https://registry.yarnpkg.com/@tiptap/extension-strike/-/extension-strike-2.27.2.tgz#9291f6dd9bcf00e1c2b7e043f9d9b18cf35f1db1" + integrity sha512-HHIjhafLhS2lHgfAsCwC1okqMsQzR4/mkGDm4M583Yftyjri1TNA7lzhzXWRFWiiMfJxKtdjHjUAQaHuteRTZw== + +"@tiptap/extension-text-style@^2.27.2": + version "2.27.2" + resolved "https://registry.yarnpkg.com/@tiptap/extension-text-style/-/extension-text-style-2.27.2.tgz#5f27d512e8421b5160be37aab17c47dde88a8bea" + integrity sha512-Omk+uxjJLyEY69KStpCw5fA9asvV+MGcAX2HOxyISDFoLaL49TMrNjhGAuz09P1L1b0KGXo4ml7Q3v/Lfy4WPA== + +"@tiptap/extension-text@^2.27.2": + version "2.27.2" + resolved "https://registry.yarnpkg.com/@tiptap/extension-text/-/extension-text-2.27.2.tgz#8b387a95cef4adb112bfb1ed00a8bc50d9204476" + integrity sha512-Xk7nYcigljAY0GO9hAQpZ65ZCxqOqaAlTPDFcKerXmlkQZP/8ndx95OgUb1Xf63kmPOh3xypurGS2is3v0MXSA== + +"@tiptap/pm@^2.27.2", "@tiptap/pm@^2.9.1": + version "2.27.2" + resolved "https://registry.yarnpkg.com/@tiptap/pm/-/pm-2.27.2.tgz#2e8b187df66eea54702cfba9820800c8d10c21ef" + integrity sha512-kaEg7BfiJPDQMKbjVIzEPO3wlcA+pZb2tlcK9gPrdDnEFaec2QTF1sXz2ak2IIb2curvnIrQ4yrfHgLlVA72wA== dependencies: prosemirror-changeset "^2.3.0" prosemirror-collab "^1.3.1" @@ -2218,57 +2220,57 @@ prosemirror-view "^1.37.0" "@tiptap/react@^2.9.1": - version "2.25.0" - resolved "https://registry.yarnpkg.com/@tiptap/react/-/react-2.25.0.tgz#9b28d575fec5c64ccd3c5b91b35153cf7f8e1a16" - integrity sha512-Fc7uj/+goEhvJkH2vYJxXLH1GsUkOcsIR3kUyL0vejNRvpzzd87CI/EiSD2ESJO43czQcsJkiYzY4EC+p8NF9w== + version "2.27.2" + resolved "https://registry.yarnpkg.com/@tiptap/react/-/react-2.27.2.tgz#cf390764f40ba2d077b50cf71728262527dfa1cc" + integrity sha512-0EAs8Cpkfbvben1PZ34JN2Nd79Dhioynm2jML27DBbf1VWPk+FFWFGTMLUT0bu+Np5iVxio8fqV9t0mc4D6thA== dependencies: - "@tiptap/extension-bubble-menu" "^2.25.0" - "@tiptap/extension-floating-menu" "^2.25.0" + "@tiptap/extension-bubble-menu" "^2.27.2" + "@tiptap/extension-floating-menu" "^2.27.2" "@types/use-sync-external-store" "^0.0.6" fast-deep-equal "^3" use-sync-external-store "^1" "@tiptap/starter-kit@^2.9.1": - version "2.25.0" - resolved "https://registry.yarnpkg.com/@tiptap/starter-kit/-/starter-kit-2.25.0.tgz#603db819f8c364edf7d8d528948f9a2c14ef3804" - integrity sha512-MWt6gEdQ2LPuCqbvNGmS0uA+6rtMGRh3vC0WBNp6rJPAvwS8OPcpraLz61cWjgzeKZBUKODpNA5IZ6gDRyH9LQ== - dependencies: - "@tiptap/core" "^2.25.0" - "@tiptap/extension-blockquote" "^2.25.0" - "@tiptap/extension-bold" "^2.25.0" - "@tiptap/extension-bullet-list" "^2.25.0" - "@tiptap/extension-code" "^2.25.0" - "@tiptap/extension-code-block" "^2.25.0" - "@tiptap/extension-document" "^2.25.0" - "@tiptap/extension-dropcursor" "^2.25.0" - "@tiptap/extension-gapcursor" "^2.25.0" - "@tiptap/extension-hard-break" "^2.25.0" - "@tiptap/extension-heading" "^2.25.0" - "@tiptap/extension-history" "^2.25.0" - "@tiptap/extension-horizontal-rule" "^2.25.0" - "@tiptap/extension-italic" "^2.25.0" - "@tiptap/extension-list-item" "^2.25.0" - "@tiptap/extension-ordered-list" "^2.25.0" - "@tiptap/extension-paragraph" "^2.25.0" - "@tiptap/extension-strike" "^2.25.0" - "@tiptap/extension-text" "^2.25.0" - "@tiptap/extension-text-style" "^2.25.0" - "@tiptap/pm" "^2.25.0" - -"@tldraw/editor@3.14.0": - version "3.14.0" - resolved "https://registry.yarnpkg.com/@tldraw/editor/-/editor-3.14.0.tgz#80843101d325c8da1153f96a5fbb8ac4b84e3ba6" - integrity sha512-HKUWmtEu/rBHOnPQoV9i+1VqSTWS7JXJBC1QipjaEap4Gw01C3z/mDolxc5N8Auy9R9kZ6fL+VyFiO/dbtzkxw== + version "2.27.2" + resolved "https://registry.yarnpkg.com/@tiptap/starter-kit/-/starter-kit-2.27.2.tgz#8cad96757376109ce9028c0dc2e941778e5051e9" + integrity sha512-bb0gJvPoDuyRUQ/iuN52j1//EtWWttw+RXAv1uJxfR0uKf8X7uAqzaOOgwjknoCIDC97+1YHwpGdnRjpDkOBxw== + dependencies: + "@tiptap/core" "^2.27.2" + "@tiptap/extension-blockquote" "^2.27.2" + "@tiptap/extension-bold" "^2.27.2" + "@tiptap/extension-bullet-list" "^2.27.2" + "@tiptap/extension-code" "^2.27.2" + "@tiptap/extension-code-block" "^2.27.2" + "@tiptap/extension-document" "^2.27.2" + "@tiptap/extension-dropcursor" "^2.27.2" + "@tiptap/extension-gapcursor" "^2.27.2" + "@tiptap/extension-hard-break" "^2.27.2" + "@tiptap/extension-heading" "^2.27.2" + "@tiptap/extension-history" "^2.27.2" + "@tiptap/extension-horizontal-rule" "^2.27.2" + "@tiptap/extension-italic" "^2.27.2" + "@tiptap/extension-list-item" "^2.27.2" + "@tiptap/extension-ordered-list" "^2.27.2" + "@tiptap/extension-paragraph" "^2.27.2" + "@tiptap/extension-strike" "^2.27.2" + "@tiptap/extension-text" "^2.27.2" + "@tiptap/extension-text-style" "^2.27.2" + "@tiptap/pm" "^2.27.2" + +"@tldraw/editor@3.15.6": + version "3.15.6" + resolved "https://registry.yarnpkg.com/@tldraw/editor/-/editor-3.15.6.tgz#e10a050d58a2a8043d5413773a9949c0698a25a3" + integrity sha512-avchuWHkX4zR/tkuzwgefr3bkATf/BZYyAJtp3OA+tqWkmqd5+AQkJi9jfEbauvy3ZIjljuejHGUUPWt3kS7yA== dependencies: "@tiptap/core" "^2.9.1" "@tiptap/pm" "^2.9.1" "@tiptap/react" "^2.9.1" - "@tldraw/state" "3.14.0" - "@tldraw/state-react" "3.14.0" - "@tldraw/store" "3.14.0" - "@tldraw/tlschema" "3.14.0" - "@tldraw/utils" "3.14.0" - "@tldraw/validate" "3.14.0" + "@tldraw/state" "3.15.6" + "@tldraw/state-react" "3.15.6" + "@tldraw/store" "3.15.6" + "@tldraw/tlschema" "3.15.6" + "@tldraw/utils" "3.15.6" + "@tldraw/validate" "3.15.6" "@types/core-js" "^2.5.8" "@use-gesture/react" "^10.3.1" classnames "^2.5.1" @@ -2277,61 +2279,61 @@ idb "^7.1.1" is-plain-object "^5.0.0" -"@tldraw/state-react@3.14.0": - version "3.14.0" - resolved "https://registry.yarnpkg.com/@tldraw/state-react/-/state-react-3.14.0.tgz#21af65b8d33e5811dc4f0f21befc76687ab8bcc5" - integrity sha512-CC38vGUTyElXak38OtViAhEVv9HMKsPUfNJdcGupsKt/l1k0MGf/ubmmf8bWctLEkOT5p+TUb8jed99dA2n5JA== +"@tldraw/state-react@3.15.6": + version "3.15.6" + resolved "https://registry.yarnpkg.com/@tldraw/state-react/-/state-react-3.15.6.tgz#4ba8f38956a292e98d10e40dc234f8b761ff3891" + integrity sha512-LWQGBv0Qtss8/grtPzHlfXYq2ge+TbZDM4hiV2oconp3x8LjiCzmVQDq9mIvcFSpZaD6ECj86k/lYX6GMsXBOQ== dependencies: - "@tldraw/state" "3.14.0" - "@tldraw/utils" "3.14.0" + "@tldraw/state" "3.15.6" + "@tldraw/utils" "3.15.6" -"@tldraw/state@3.14.0": - version "3.14.0" - resolved "https://registry.yarnpkg.com/@tldraw/state/-/state-3.14.0.tgz#a350bb981f8e50b21f96d875e0099fc7ebb7b004" - integrity sha512-lNSKXjeykb5BBJ85A4/R7LLCWEEAbdAAhhHwk42me9oBdonce5sVwRT7sjUkYCLU5MUejRXf1botyGTyWumkhg== +"@tldraw/state@3.15.6": + version "3.15.6" + resolved "https://registry.yarnpkg.com/@tldraw/state/-/state-3.15.6.tgz#406319f109128c319111bffe7a3da486fd94afb2" + integrity sha512-ZBwlNMUPwtxd++x8kzPEfXna/C7o+7qiiIejpI34hkC927trlr22MNajtYSEAmyO8Q8cP3nC0Q68Eh4Mx2NTHA== dependencies: - "@tldraw/utils" "3.14.0" + "@tldraw/utils" "3.15.6" -"@tldraw/store@3.14.0": - version "3.14.0" - resolved "https://registry.yarnpkg.com/@tldraw/store/-/store-3.14.0.tgz#ee368e6b0a790c21ac8e74ab2e420086a5efba49" - integrity sha512-nnKuQuI8uMieLTecHQ5g7ki4LS2UmmGdfVXB/K6EyBUCt5L4xjkXb13I4WOJslFVyDqoH9XxXJmwS2BOusr+rg== +"@tldraw/store@3.15.6": + version "3.15.6" + resolved "https://registry.yarnpkg.com/@tldraw/store/-/store-3.15.6.tgz#19e06ee04c7b5a4f2241f0725e8af8035f0efe0b" + integrity sha512-v2VGW+kvcwccehSjQKriDGhG2C7703wxGzW/dZqNtxpuoqm51qZL2fStUsrSRQb1Mh1zbHu+IJDhTQqmnZzEiQ== dependencies: - "@tldraw/state" "3.14.0" - "@tldraw/utils" "3.14.0" + "@tldraw/state" "3.15.6" + "@tldraw/utils" "3.15.6" -"@tldraw/tlschema@3.14.0": - version "3.14.0" - resolved "https://registry.yarnpkg.com/@tldraw/tlschema/-/tlschema-3.14.0.tgz#082f966ad8ec76fe53168324994e3e87a4ed32ea" - integrity sha512-9jzTttWxCVXa1qnInJ8GLnr5unxJ1L6fQtgPIxj4sCDpWE0YnZn+N6CT71XBFtL7I6Jn0Ojze+aHjNuxbh6CAw== +"@tldraw/tlschema@3.15.6": + version "3.15.6" + resolved "https://registry.yarnpkg.com/@tldraw/tlschema/-/tlschema-3.15.6.tgz#8b6fa2a9f10c0b26525c509e310c04cb3a52e865" + integrity sha512-7rlgTADifvNd5hXZynuKV/I7SIHKszw1XOYKxx/BcVeLmy5SYFBjofFIYPfrDJo9luZPt23MfXvy2iW2p8kIvw== dependencies: - "@tldraw/state" "3.14.0" - "@tldraw/store" "3.14.0" - "@tldraw/utils" "3.14.0" - "@tldraw/validate" "3.14.0" + "@tldraw/state" "3.15.6" + "@tldraw/store" "3.15.6" + "@tldraw/utils" "3.15.6" + "@tldraw/validate" "3.15.6" -"@tldraw/utils@3.14.0": - version "3.14.0" - resolved "https://registry.yarnpkg.com/@tldraw/utils/-/utils-3.14.0.tgz#4ee3cb13df26c69466f178f1049317acb2a55493" - integrity sha512-NU1A2bjUtNi3wXpt9zAgMpuKmOvXWVEMLMDhoalbZs50jNuNSTd/grUSsiiOkNb+NArD+WQhxIsKBWc3SoBxUw== +"@tldraw/utils@3.15.6": + version "3.15.6" + resolved "https://registry.yarnpkg.com/@tldraw/utils/-/utils-3.15.6.tgz#dad58a83bc83cfbcdd0ddb30d6dfc782977a8e31" + integrity sha512-GDvMJvw7Y4UPR8IU7/thPDMtuBkXqoyzjSlOslKzHIZAdF3hfQDLqmC7ugJ/xc7KtfC8WKeuiYhBf2Oo0Bv5HQ== dependencies: - fractional-indexing-jittered "^0.9.1" + fractional-indexing-jittered "^1.0.0" lodash.isequal "^4.5.0" lodash.isequalwith "^4.4.0" lodash.throttle "^4.1.1" lodash.uniq "^4.5.0" -"@tldraw/validate@3.14.0": - version "3.14.0" - resolved "https://registry.yarnpkg.com/@tldraw/validate/-/validate-3.14.0.tgz#bfec039a2f3f0578ce3816e5b96a7d40c223d07c" - integrity sha512-84qSokaJyDRba6A8PyKEGWLHBwn64pVcHiTlPMb4k1/vqv/lxeW8/qwcQLjpGsNt2xZPUZ9CIyI/bM2CYHUszA== +"@tldraw/validate@3.15.6": + version "3.15.6" + resolved "https://registry.yarnpkg.com/@tldraw/validate/-/validate-3.15.6.tgz#6c721378c7219dd5e79b9beb77d7b708f1c6a0c8" + integrity sha512-lYUbe36HmmJWFlg2wM5SCEGPB0n4HHALcxvfibEu351uGSwlemznr/tN3WCvQLshTXe1vMDNtyO+jJwrPprp5w== dependencies: - "@tldraw/utils" "3.14.0" + "@tldraw/utils" "3.15.6" -"@tybys/wasm-util@^0.9.0": - version "0.9.0" - resolved "https://registry.yarnpkg.com/@tybys/wasm-util/-/wasm-util-0.9.0.tgz#3e75eb00604c8d6db470bf18c37b7d984a0e3355" - integrity sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw== +"@tybys/wasm-util@^0.10.0": + version "0.10.1" + resolved "https://registry.yarnpkg.com/@tybys/wasm-util/-/wasm-util-0.10.1.tgz#ecddd3205cf1e2d5274649ff0eedd2991ed7f414" + integrity sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg== dependencies: tslib "^2.4.0" @@ -2362,11 +2364,11 @@ "@babel/types" "^7.0.0" "@types/babel__traverse@*": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.20.7.tgz#968cdc2366ec3da159f61166428ee40f370e56c2" - integrity sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng== + version "7.28.0" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.28.0.tgz#07d713d6cce0d265c9849db0cbe62d3f61f36f74" + integrity sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q== dependencies: - "@babel/types" "^7.20.7" + "@babel/types" "^7.28.2" "@types/core-js@^2.5.8": version "2.5.8" @@ -2375,26 +2377,26 @@ "@types/d3-array@*": version "3.2.2" - resolved "https://registry.npmmirror.com/@types/d3-array/-/d3-array-3.2.2.tgz#e02151464d02d4a1b44646d0fcdb93faf88fde8c" + resolved "https://registry.yarnpkg.com/@types/d3-array/-/d3-array-3.2.2.tgz#e02151464d02d4a1b44646d0fcdb93faf88fde8c" integrity sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw== "@types/d3-axis@*": version "3.0.6" - resolved "https://registry.npmmirror.com/@types/d3-axis/-/d3-axis-3.0.6.tgz#e760e5765b8188b1defa32bc8bb6062f81e4c795" + resolved "https://registry.yarnpkg.com/@types/d3-axis/-/d3-axis-3.0.6.tgz#e760e5765b8188b1defa32bc8bb6062f81e4c795" integrity sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw== dependencies: "@types/d3-selection" "*" "@types/d3-brush@*": version "3.0.6" - resolved "https://registry.npmmirror.com/@types/d3-brush/-/d3-brush-3.0.6.tgz#c2f4362b045d472e1b186cdbec329ba52bdaee6c" + resolved "https://registry.yarnpkg.com/@types/d3-brush/-/d3-brush-3.0.6.tgz#c2f4362b045d472e1b186cdbec329ba52bdaee6c" integrity sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A== dependencies: "@types/d3-selection" "*" "@types/d3-chord@*": version "3.0.6" - resolved "https://registry.npmmirror.com/@types/d3-chord/-/d3-chord-3.0.6.tgz#1706ca40cf7ea59a0add8f4456efff8f8775793d" + resolved "https://registry.yarnpkg.com/@types/d3-chord/-/d3-chord-3.0.6.tgz#1706ca40cf7ea59a0add8f4456efff8f8775793d" integrity sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg== "@types/d3-color@*", "@types/d3-color@^3.0.0": @@ -2404,7 +2406,7 @@ "@types/d3-contour@*": version "3.0.6" - resolved "https://registry.npmmirror.com/@types/d3-contour/-/d3-contour-3.0.6.tgz#9ada3fa9c4d00e3a5093fed0356c7ab929604231" + resolved "https://registry.yarnpkg.com/@types/d3-contour/-/d3-contour-3.0.6.tgz#9ada3fa9c4d00e3a5093fed0356c7ab929604231" integrity sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg== dependencies: "@types/d3-array" "*" @@ -2417,41 +2419,41 @@ "@types/d3-dispatch@*": version "3.0.7" - resolved "https://registry.npmmirror.com/@types/d3-dispatch/-/d3-dispatch-3.0.7.tgz#ef004d8a128046cfce434d17182f834e44ef95b2" + resolved "https://registry.yarnpkg.com/@types/d3-dispatch/-/d3-dispatch-3.0.7.tgz#ef004d8a128046cfce434d17182f834e44ef95b2" integrity sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA== "@types/d3-drag@*", "@types/d3-drag@^3.0.1": version "3.0.7" - resolved "https://registry.npmmirror.com/@types/d3-drag/-/d3-drag-3.0.7.tgz#b13aba8b2442b4068c9a9e6d1d82f8bcea77fc02" + resolved "https://registry.yarnpkg.com/@types/d3-drag/-/d3-drag-3.0.7.tgz#b13aba8b2442b4068c9a9e6d1d82f8bcea77fc02" integrity sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ== dependencies: "@types/d3-selection" "*" "@types/d3-dsv@*": version "3.0.7" - resolved "https://registry.npmmirror.com/@types/d3-dsv/-/d3-dsv-3.0.7.tgz#0a351f996dc99b37f4fa58b492c2d1c04e3dac17" + resolved "https://registry.yarnpkg.com/@types/d3-dsv/-/d3-dsv-3.0.7.tgz#0a351f996dc99b37f4fa58b492c2d1c04e3dac17" integrity sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g== "@types/d3-ease@*": version "3.0.2" - resolved "https://registry.npmmirror.com/@types/d3-ease/-/d3-ease-3.0.2.tgz#e28db1bfbfa617076f7770dd1d9a48eaa3b6c51b" + resolved "https://registry.yarnpkg.com/@types/d3-ease/-/d3-ease-3.0.2.tgz#e28db1bfbfa617076f7770dd1d9a48eaa3b6c51b" integrity sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA== "@types/d3-fetch@*": version "3.0.7" - resolved "https://registry.npmmirror.com/@types/d3-fetch/-/d3-fetch-3.0.7.tgz#c04a2b4f23181aa376f30af0283dbc7b3b569980" + resolved "https://registry.yarnpkg.com/@types/d3-fetch/-/d3-fetch-3.0.7.tgz#c04a2b4f23181aa376f30af0283dbc7b3b569980" integrity sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA== dependencies: "@types/d3-dsv" "*" "@types/d3-force@*": version "3.0.10" - resolved "https://registry.npmmirror.com/@types/d3-force/-/d3-force-3.0.10.tgz#6dc8fc6e1f35704f3b057090beeeb7ac674bff1a" + resolved "https://registry.yarnpkg.com/@types/d3-force/-/d3-force-3.0.10.tgz#6dc8fc6e1f35704f3b057090beeeb7ac674bff1a" integrity sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw== "@types/d3-format@*": version "3.0.4" - resolved "https://registry.npmmirror.com/@types/d3-format/-/d3-format-3.0.4.tgz#b1e4465644ddb3fdf3a263febb240a6cd616de90" + resolved "https://registry.yarnpkg.com/@types/d3-format/-/d3-format-3.0.4.tgz#b1e4465644ddb3fdf3a263febb240a6cd616de90" integrity sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g== "@types/d3-format@^1.4.1": @@ -2461,19 +2463,19 @@ "@types/d3-geo@*": version "3.1.0" - resolved "https://registry.npmmirror.com/@types/d3-geo/-/d3-geo-3.1.0.tgz#b9e56a079449174f0a2c8684a9a4df3f60522440" + resolved "https://registry.yarnpkg.com/@types/d3-geo/-/d3-geo-3.1.0.tgz#b9e56a079449174f0a2c8684a9a4df3f60522440" integrity sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ== dependencies: "@types/geojson" "*" "@types/d3-hierarchy@*": version "3.1.7" - resolved "https://registry.npmmirror.com/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz#6023fb3b2d463229f2d680f9ac4b47466f71f17b" + resolved "https://registry.yarnpkg.com/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz#6023fb3b2d463229f2d680f9ac4b47466f71f17b" integrity sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg== "@types/d3-interpolate@*": version "3.0.4" - resolved "https://registry.npmmirror.com/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz#412b90e84870285f2ff8a846c6eb60344f12a41c" + resolved "https://registry.yarnpkg.com/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz#412b90e84870285f2ff8a846c6eb60344f12a41c" integrity sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA== dependencies: "@types/d3-color" "*" @@ -2485,17 +2487,17 @@ "@types/d3-polygon@*": version "3.0.2" - resolved "https://registry.npmmirror.com/@types/d3-polygon/-/d3-polygon-3.0.2.tgz#dfae54a6d35d19e76ac9565bcb32a8e54693189c" + resolved "https://registry.yarnpkg.com/@types/d3-polygon/-/d3-polygon-3.0.2.tgz#dfae54a6d35d19e76ac9565bcb32a8e54693189c" integrity sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA== "@types/d3-quadtree@*": version "3.0.6" - resolved "https://registry.npmmirror.com/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz#d4740b0fe35b1c58b66e1488f4e7ed02952f570f" + resolved "https://registry.yarnpkg.com/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz#d4740b0fe35b1c58b66e1488f4e7ed02952f570f" integrity sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg== "@types/d3-random@*": version "3.0.3" - resolved "https://registry.npmmirror.com/@types/d3-random/-/d3-random-3.0.3.tgz#ed995c71ecb15e0cd31e22d9d5d23942e3300cfb" + resolved "https://registry.yarnpkg.com/@types/d3-random/-/d3-random-3.0.3.tgz#ed995c71ecb15e0cd31e22d9d5d23942e3300cfb" integrity sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ== "@types/d3-scale-chromatic@*", "@types/d3-scale-chromatic@^3.0.0": @@ -2512,26 +2514,19 @@ "@types/d3-selection@*", "@types/d3-selection@^3.0.3": version "3.0.11" - resolved "https://registry.npmmirror.com/@types/d3-selection/-/d3-selection-3.0.11.tgz#bd7a45fc0a8c3167a631675e61bc2ca2b058d4a3" + resolved "https://registry.yarnpkg.com/@types/d3-selection/-/d3-selection-3.0.11.tgz#bd7a45fc0a8c3167a631675e61bc2ca2b058d4a3" integrity sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w== -"@types/d3-shape@*": +"@types/d3-shape@*", "@types/d3-shape@^3.1.6": version "3.1.8" - resolved "https://registry.npmmirror.com/@types/d3-shape/-/d3-shape-3.1.8.tgz#d1516cc508753be06852cd06758e3bb54a22b0e3" + resolved "https://registry.yarnpkg.com/@types/d3-shape/-/d3-shape-3.1.8.tgz#d1516cc508753be06852cd06758e3bb54a22b0e3" integrity sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w== dependencies: "@types/d3-path" "*" -"@types/d3-shape@^3.1.6": - version "3.1.7" - resolved "https://registry.yarnpkg.com/@types/d3-shape/-/d3-shape-3.1.7.tgz#2b7b423dc2dfe69c8c93596e673e37443348c555" - integrity sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg== - dependencies: - "@types/d3-path" "*" - "@types/d3-time-format@*": version "4.0.3" - resolved "https://registry.npmmirror.com/@types/d3-time-format/-/d3-time-format-4.0.3.tgz#d6bc1e6b6a7db69cccfbbdd4c34b70632d9e9db2" + resolved "https://registry.yarnpkg.com/@types/d3-time-format/-/d3-time-format-4.0.3.tgz#d6bc1e6b6a7db69cccfbbdd4c34b70632d9e9db2" integrity sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg== "@types/d3-time-format@^2.3.1": @@ -2556,19 +2551,19 @@ "@types/d3-timer@*": version "3.0.2" - resolved "https://registry.npmmirror.com/@types/d3-timer/-/d3-timer-3.0.2.tgz#70bbda77dc23aa727413e22e214afa3f0e852f70" + resolved "https://registry.yarnpkg.com/@types/d3-timer/-/d3-timer-3.0.2.tgz#70bbda77dc23aa727413e22e214afa3f0e852f70" integrity sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw== "@types/d3-transition@*": version "3.0.9" - resolved "https://registry.npmmirror.com/@types/d3-transition/-/d3-transition-3.0.9.tgz#1136bc57e9ddb3c390dccc9b5ff3b7d2b8d94706" + resolved "https://registry.yarnpkg.com/@types/d3-transition/-/d3-transition-3.0.9.tgz#1136bc57e9ddb3c390dccc9b5ff3b7d2b8d94706" integrity sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg== dependencies: "@types/d3-selection" "*" "@types/d3-zoom@*", "@types/d3-zoom@^3.0.1": version "3.0.8" - resolved "https://registry.npmmirror.com/@types/d3-zoom/-/d3-zoom-3.0.8.tgz#dccb32d1c56b1e1c6e0f1180d994896f038bc40b" + resolved "https://registry.yarnpkg.com/@types/d3-zoom/-/d3-zoom-3.0.8.tgz#dccb32d1c56b1e1c6e0f1180d994896f038bc40b" integrity sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw== dependencies: "@types/d3-interpolate" "*" @@ -2576,7 +2571,7 @@ "@types/d3@^7.4.0": version "7.4.3" - resolved "https://registry.npmmirror.com/@types/d3/-/d3-7.4.3.tgz#d4550a85d08f4978faf0a4c36b848c61eaac07e2" + resolved "https://registry.yarnpkg.com/@types/d3/-/d3-7.4.3.tgz#d4550a85d08f4978faf0a4c36b848c61eaac07e2" integrity sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww== dependencies: "@types/d3-array" "*" @@ -2637,7 +2632,7 @@ "@types/geojson@*": version "7946.0.16" - resolved "https://registry.npmmirror.com/@types/geojson/-/geojson-7946.0.16.tgz#8ebe53d69efada7044454e3305c19017d97ced2a" + resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.16.tgz#8ebe53d69efada7044454e3305c19017d97ced2a" integrity sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg== "@types/json-schema@*", "@types/json-schema@^7.0.15": @@ -2656,9 +2651,9 @@ integrity sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q== "@types/lodash@^4.14.178": - version "4.17.20" - resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.20.tgz#1ca77361d7363432d29f5e55950d9ec1e1c6ea93" - integrity sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA== + version "4.17.23" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.23.tgz#c1bb06db218acc8fc232da0447473fc2fb9d9841" + integrity sha512-RDvF6wTulMPjrNdCoYRC8gNR880JNGT8uB+REUpC2Ns4pRqQJhGz90wh7rgdXDPpCczF3VGktDuFGVnz8zP7HA== "@types/markdown-it@^14.0.0": version "14.1.2" @@ -2732,19 +2727,19 @@ integrity sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w== "@types/react@*": - version "19.1.8" - resolved "https://registry.yarnpkg.com/@types/react/-/react-19.1.8.tgz#ff8395f2afb764597265ced15f8dddb0720ae1c3" - integrity sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g== + version "19.2.14" + resolved "https://registry.yarnpkg.com/@types/react/-/react-19.2.14.tgz#39604929b5e3957e3a6fa0001dafb17c7af70bad" + integrity sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w== dependencies: - csstype "^3.0.2" + csstype "^3.2.2" "@types/react@^18.3.1": - version "18.3.23" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.3.23.tgz#86ae6f6b95a48c418fecdaccc8069e0fbb63696a" - integrity sha512-/LDXMQh55EzZQ0uVAZmKKhfENivEvWz6E+EYzh+/MCjMhNsotd+ZHhBGIjFDTi6+fz0OhQQQLbTgdQIxxCsC0w== + version "18.3.28" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.3.28.tgz#0a85b1a7243b4258d9f626f43797ba18eb5f8781" + integrity sha512-z9VXpC7MWrhfWipitjNdgCauoMLRdIILQsAEV+ZesIzBq/oUlxk0m3ApZuMFCXdnS4U7KrI+l3WRUEGQ8K1QKw== dependencies: "@types/prop-types" "*" - csstype "^3.0.2" + csstype "^3.2.2" "@types/shuffle-seed@^1.1.0": version "1.1.3" @@ -2837,102 +2832,102 @@ "@typescript-eslint/types" "7.18.0" eslint-visitor-keys "^3.4.3" -"@unrs/resolver-binding-android-arm-eabi@1.11.0": - version "1.11.0" - resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.0.tgz#6a64664fa35d2aaf3ed9bc82dff15ef9df23c066" - integrity sha512-LRw5BW29sYj9NsQC6QoqeLVQhEa+BwVINYyMlcve+6stwdBsSt5UB7zw4UZB4+4PNqIVilHoMaPWCb/KhABHQw== +"@unrs/resolver-binding-android-arm-eabi@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz#9f5b04503088e6a354295e8ea8fe3cb99e43af81" + integrity sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw== -"@unrs/resolver-binding-android-arm64@1.11.0": - version "1.11.0" - resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.0.tgz#1ab6be0d8a1340d629318d5985c5555c8d750558" - integrity sha512-zYX8D2zcWCAHqghA8tPjbp7LwjVXbIZP++mpU/Mrf5jUVlk3BWIxkeB8yYzZi5GpFSlqMcRZQxQqbMI0c2lASQ== +"@unrs/resolver-binding-android-arm64@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz#7414885431bd7178b989aedc4d25cccb3865bc9f" + integrity sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g== -"@unrs/resolver-binding-darwin-arm64@1.11.0": - version "1.11.0" - resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.0.tgz#45754acf53eda1d9ec2916da9411924bcadd8cf2" - integrity sha512-YsYOT049hevAY/lTYD77GhRs885EXPeAfExG5KenqMJ417nYLS2N/kpRpYbABhFZBVQn+2uRPasTe4ypmYoo3w== +"@unrs/resolver-binding-darwin-arm64@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz#b4a8556f42171fb9c9f7bac8235045e82aa0cbdf" + integrity sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g== -"@unrs/resolver-binding-darwin-x64@1.11.0": - version "1.11.0" - resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.0.tgz#a5b64d820a1ca7d0f9d5f3b2dd4bf133b63143de" - integrity sha512-PSjvk3OZf1aZImdGY5xj9ClFG3bC4gnSSYWrt+id0UAv+GwwVldhpMFjAga8SpMo2T1GjV9UKwM+QCsQCQmtdA== +"@unrs/resolver-binding-darwin-x64@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz#fd4d81257b13f4d1a083890a6a17c00de571f0dc" + integrity sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ== -"@unrs/resolver-binding-freebsd-x64@1.11.0": - version "1.11.0" - resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.0.tgz#17fc82321b7cdfdae871b63e3a54fe1978fde62e" - integrity sha512-KC/iFaEN/wsTVYnHClyHh5RSYA9PpuGfqkFua45r4sweXpC0KHZ+BYY7ikfcGPt5w1lMpR1gneFzuqWLQxsRKg== +"@unrs/resolver-binding-freebsd-x64@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz#d2513084d0f37c407757e22f32bd924a78cfd99b" + integrity sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw== -"@unrs/resolver-binding-linux-arm-gnueabihf@1.11.0": - version "1.11.0" - resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.0.tgz#acc6d616231bde02dab70a844903652441aa1cbb" - integrity sha512-CDh/0v8uot43cB4yKtDL9CVY8pbPnMV0dHyQCE4lFz6PW/+9tS0i9eqP5a91PAqEBVMqH1ycu+k8rP6wQU846w== +"@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz#844d2605d057488d77fab09705f2866b86164e0a" + integrity sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw== -"@unrs/resolver-binding-linux-arm-musleabihf@1.11.0": - version "1.11.0" - resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.0.tgz#8d200f0ceb193efe56972d7ca76a3cb147c992be" - integrity sha512-+TE7epATDSnvwr3L/hNHX3wQ8KQYB+jSDTdywycg3qDqvavRP8/HX9qdq/rMcnaRDn4EOtallb3vL/5wCWGCkw== +"@unrs/resolver-binding-linux-arm-musleabihf@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz#204892995cefb6bd1d017d52d097193bc61ddad3" + integrity sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw== -"@unrs/resolver-binding-linux-arm64-gnu@1.11.0": - version "1.11.0" - resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.0.tgz#2d976fdbb4d6b94bf77b51c668e893ffc2e95c45" - integrity sha512-VBAYGg3VahofpQ+L4k/ZO8TSICIbUKKTaMYOWHWfuYBFqPbSkArZZLezw3xd27fQkxX4BaLGb/RKnW0dH9Y/UA== +"@unrs/resolver-binding-linux-arm64-gnu@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz#023eb0c3aac46066a10be7a3f362e7b34f3bdf9d" + integrity sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ== -"@unrs/resolver-binding-linux-arm64-musl@1.11.0": - version "1.11.0" - resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.0.tgz#0bb3fcd3d490e171102a7e786557f3e30f375d74" - integrity sha512-9IgGFUUb02J1hqdRAHXpZHIeUHRrbnGo6vrRbz0fREH7g+rzQy53/IBSyadZ/LG5iqMxukriNPu4hEMUn+uWEg== +"@unrs/resolver-binding-linux-arm64-musl@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz#9e6f9abb06424e3140a60ac996139786f5d99be0" + integrity sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w== -"@unrs/resolver-binding-linux-ppc64-gnu@1.11.0": - version "1.11.0" - resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.0.tgz#436b591ccda94e191a2a6eff6cd021b1375a703b" - integrity sha512-LR4iQ/LPjMfivpL2bQ9kmm3UnTas3U+umcCnq/CV7HAkukVdHxrDD1wwx74MIWbbgzQTLPYY7Ur2MnnvkYJCBQ== +"@unrs/resolver-binding-linux-ppc64-gnu@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz#b111417f17c9d1b02efbec8e08398f0c5527bb44" + integrity sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA== -"@unrs/resolver-binding-linux-riscv64-gnu@1.11.0": - version "1.11.0" - resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.0.tgz#bcd73a92c8fb2897cdf345c21bc9e657fc04a120" - integrity sha512-HCupFQwMrRhrOg7YHrobbB5ADg0Q8RNiuefqMHVsdhEy9lLyXm/CxsCXeLJdrg27NAPsCaMDtdlm8Z2X8x91Tg== +"@unrs/resolver-binding-linux-riscv64-gnu@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz#92ffbf02748af3e99873945c9a8a5ead01d508a9" + integrity sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ== -"@unrs/resolver-binding-linux-riscv64-musl@1.11.0": - version "1.11.0" - resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.0.tgz#29d7e501830635559a3b6bd24a46895ad015a257" - integrity sha512-Ckxy76A5xgjWa4FNrzcKul5qFMWgP5JSQ5YKd0XakmWOddPLSkQT+uAvUpQNnFGNbgKzv90DyQlxPDYPQ4nd6A== +"@unrs/resolver-binding-linux-riscv64-musl@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz#0bec6f1258fc390e6b305e9ff44256cb207de165" + integrity sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew== -"@unrs/resolver-binding-linux-s390x-gnu@1.11.0": - version "1.11.0" - resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.0.tgz#0aff45e49256ed0340cac63562c0a424dfdbe45c" - integrity sha512-HfO0PUCCRte2pMJmVyxPI+eqT7KuV3Fnvn2RPvMe5mOzb2BJKf4/Vth8sSt9cerQboMaTVpbxyYjjLBWIuI5BQ== +"@unrs/resolver-binding-linux-s390x-gnu@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz#577843a084c5952f5906770633ccfb89dac9bc94" + integrity sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg== -"@unrs/resolver-binding-linux-x64-gnu@1.11.0": - version "1.11.0" - resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.0.tgz#7437389f2d89eee159cd0a458c5719959cf37c1a" - integrity sha512-9PZdjP7tLOEjpXHS6+B/RNqtfVUyDEmaViPOuSqcbomLdkJnalt5RKQ1tr2m16+qAufV0aDkfhXtoO7DQos/jg== +"@unrs/resolver-binding-linux-x64-gnu@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz#36fb318eebdd690f6da32ac5e0499a76fa881935" + integrity sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w== -"@unrs/resolver-binding-linux-x64-musl@1.11.0": - version "1.11.0" - resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.0.tgz#9fc10fa5fd0c37bc780296ef5aa15e32034c9701" - integrity sha512-qkE99ieiSKMnFJY/EfyGKVtNra52/k+lVF/PbO4EL5nU6AdvG4XhtJ+WHojAJP7ID9BNIra/yd75EHndewNRfA== +"@unrs/resolver-binding-linux-x64-musl@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz#bfb9af75f783f98f6a22c4244214efe4df1853d6" + integrity sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA== -"@unrs/resolver-binding-wasm32-wasi@1.11.0": - version "1.11.0" - resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.0.tgz#4be4f755b056207ed4f909798970d719d0a28a1b" - integrity sha512-MjXek8UL9tIX34gymvQLecz2hMaQzOlaqYJJBomwm1gsvK2F7hF+YqJJ2tRyBDTv9EZJGMt4KlKkSD/gZWCOiw== +"@unrs/resolver-binding-wasm32-wasi@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz#752c359dd875684b27429500d88226d7cc72f71d" + integrity sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ== dependencies: "@napi-rs/wasm-runtime" "^0.2.11" -"@unrs/resolver-binding-win32-arm64-msvc@1.11.0": - version "1.11.0" - resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.0.tgz#fdd98a7d3cf6864c33d6e730efe31c0cff9ab591" - integrity sha512-9LT6zIGO7CHybiQSh7DnQGwFMZvVr0kUjah6qQfkH2ghucxPV6e71sUXJdSM4Ba0MaGE6DC/NwWf7mJmc3DAng== +"@unrs/resolver-binding-win32-arm64-msvc@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz#ce5735e600e4c2fbb409cd051b3b7da4a399af35" + integrity sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw== -"@unrs/resolver-binding-win32-ia32-msvc@1.11.0": - version "1.11.0" - resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.0.tgz#b09f3dcf0f3b516d90dd1a3b00fa72d8536dadb6" - integrity sha512-HYchBYOZ7WN266VjoGm20xFv5EonG/ODURRgwl9EZT7Bq1nLEs6VKJddzfFdXEAho0wfFlt8L/xIiE29Pmy1RA== +"@unrs/resolver-binding-win32-ia32-msvc@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz#72fc57bc7c64ec5c3de0d64ee0d1810317bc60a6" + integrity sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ== -"@unrs/resolver-binding-win32-x64-msvc@1.11.0": - version "1.11.0" - resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.0.tgz#70c6b7d5807ece551c57dee4587dd4a40d197bd2" - integrity sha512-+oLKLHw3I1UQo4MeHfoLYF+e6YBa8p5vYUw3Rgt7IDzCs+57vIZqQlIo62NDpYM0VG6BjWOwnzBczMvbtH8hag== +"@unrs/resolver-binding-win32-x64-msvc@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz#538b1e103bf8d9864e7b85cc96fa8d6fb6c40777" + integrity sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g== "@use-gesture/core@10.3.1": version "10.3.1" @@ -2947,14 +2942,14 @@ "@use-gesture/core" "10.3.1" "@vitejs/plugin-react@^4.5.2": - version "4.6.0" - resolved "https://registry.yarnpkg.com/@vitejs/plugin-react/-/plugin-react-4.6.0.tgz#2707b485f44806d42d41c63921883cff9c54dfaa" - integrity sha512-5Kgff+m8e2PB+9j51eGHEpn5kUzRKH2Ry0qGoe8ItJg7pqnkPrYPkDQZGgGmTa0EGarHrkjLvOdU3b1fzI8otQ== + version "4.7.0" + resolved "https://registry.yarnpkg.com/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz#647af4e7bb75ad3add578e762ad984b90f4a24b9" + integrity sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA== dependencies: - "@babel/core" "^7.27.4" + "@babel/core" "^7.28.0" "@babel/plugin-transform-react-jsx-self" "^7.27.1" "@babel/plugin-transform-react-jsx-source" "^7.27.1" - "@rolldown/pluginutils" "1.0.0-beta.19" + "@rolldown/pluginutils" "1.0.0-beta.27" "@types/babel__core" "^7.20.5" react-refresh "^0.17.0" @@ -2984,9 +2979,9 @@ ansi-regex@^5.0.1: integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== ansi-regex@^6.0.1: - version "6.1.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.1.0.tgz#95ec409c69619d6cb1b8b34f14b660ef28ebd654" - integrity sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA== + version "6.2.2" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.2.2.tgz#60216eea464d864597ce2832000738a0589650c1" + integrity sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg== ansi-styles@^3.2.1: version "3.2.1" @@ -3003,9 +2998,9 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0: color-convert "^2.0.1" ansi-styles@^6.1.0: - version "6.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" - integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== + version "6.2.3" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.3.tgz#c044d5dcc521a076413472597a1acb1f103c4041" + integrity sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg== archiver-utils@^2.1.0: version "2.1.0" @@ -3196,6 +3191,11 @@ base64-js@^1.3.1: resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== +baseline-browser-mapping@^2.9.0: + version "2.9.19" + resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz#3e508c43c46d961eb4d7d2e5b8d1dd0f9ee4f488" + integrity sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg== + big-integer@^1.6.16, big-integer@^1.6.17: version "1.6.52" resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.52.tgz#60a887f3047614a8e1bffe5d7173490a97dc8c85" @@ -3260,14 +3260,15 @@ broadcast-channel@^3.4.1: unload "2.2.0" browserslist@^4.24.0: - version "4.25.1" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.25.1.tgz#ba9e8e6f298a1d86f829c9b975e07948967bb111" - integrity sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw== + version "4.28.1" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.28.1.tgz#7f534594628c53c63101079e27e40de490456a95" + integrity sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA== dependencies: - caniuse-lite "^1.0.30001726" - electron-to-chromium "^1.5.173" - node-releases "^2.0.19" - update-browserslist-db "^1.1.3" + baseline-browser-mapping "^2.9.0" + caniuse-lite "^1.0.30001759" + electron-to-chromium "^1.5.263" + node-releases "^2.0.27" + update-browserslist-db "^1.2.0" buffer-crc32@^0.2.1, buffer-crc32@^0.2.13: version "0.2.13" @@ -3335,10 +3336,10 @@ callsites@^3.0.0: resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== -caniuse-lite@^1.0.30001579, caniuse-lite@^1.0.30001726: - version "1.0.30001727" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001727.tgz#22e9706422ad37aa50556af8c10e40e2d93a8b85" - integrity sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q== +caniuse-lite@^1.0.30001579, caniuse-lite@^1.0.30001759: + version "1.0.30001770" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001770.tgz#4dc47d3b263a50fbb243448034921e0a88591a84" + integrity sha512-x/2CLQ1jHENRbHg5PSId2sXq1CIO1CISvwWAj027ltMVG2UNgW+w9oH2+HzgEIRFembL8bUlXtfbBHR1fCg2xw== chainsaw@~0.1.0: version "0.1.0" @@ -3371,7 +3372,7 @@ chance@^1.1.12: classcat@^5.0.3, classcat@^5.0.4: version "5.0.5" - resolved "https://registry.npmmirror.com/classcat/-/classcat-5.0.5.tgz#8c209f359a93ac302404a10161b501eba9c09c77" + resolved "https://registry.yarnpkg.com/classcat/-/classcat-5.0.5.tgz#8c209f359a93ac302404a10161b501eba9c09c77" integrity sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w== classnames@^2.5.1: @@ -3444,9 +3445,9 @@ convert-source-map@^2.0.0: integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== core-js@^3.40.0: - version "3.44.0" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.44.0.tgz#db4fd4fa07933c1d6898c8b112a1119a9336e959" - integrity sha512-aFCtd4l6GvAXwVEh3XbbVqJGHDJt0OZRa+5ePGx3LLwi12WfexqQxcsohb2wgsa/92xtl19Hd66G/L+TaAxDMw== + version "3.48.0" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.48.0.tgz#1f813220a47bbf0e667e3885c36cd6f0593bf14d" + integrity sha512-zpEHTy1fjTMZCKLHUZoVeylt9XrzaIN2rbPXEt0k+q7JE5CkCZdo6bNq55bn24a69CH7ErAVLKijxJja4fw+UQ== core-util-is@~1.0.0: version "1.0.3" @@ -3502,14 +3503,14 @@ cross-spawn@^7.0.6: shebang-command "^2.0.0" which "^2.0.1" -csstype@^3.0.2, csstype@^3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" - integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== +csstype@^3.0.2, csstype@^3.1.3, csstype@^3.2.2: + version "3.2.3" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.2.3.tgz#ec48c0f3e993e50648c86da559e2610995cf989a" + integrity sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ== cytoscape@^3.33.1: version "3.33.1" - resolved "https://registry.npmmirror.com/cytoscape/-/cytoscape-3.33.1.tgz#449e05d104b760af2912ab76482d24c01cdd4c97" + resolved "https://registry.yarnpkg.com/cytoscape/-/cytoscape-3.33.1.tgz#449e05d104b760af2912ab76482d24c01cdd4c97" integrity sha512-iJc4TwyANnOGR1OmWhsS9ayRS3s+XQ185FmuHObThD+5AeJCakAAbWv8KimMTt08xCCLNgneQwFp+JRJOr9qGQ== d3-array@2: @@ -3540,12 +3541,12 @@ d3-delaunay@^6.0.4: "d3-dispatch@1 - 3": version "3.0.1" - resolved "https://registry.npmmirror.com/d3-dispatch/-/d3-dispatch-3.0.1.tgz#5fc75284e9c2375c36c839411a0cf550cbfc4d5e" + resolved "https://registry.yarnpkg.com/d3-dispatch/-/d3-dispatch-3.0.1.tgz#5fc75284e9c2375c36c839411a0cf550cbfc4d5e" integrity sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg== "d3-drag@2 - 3", d3-drag@^3.0.0: version "3.0.0" - resolved "https://registry.npmmirror.com/d3-drag/-/d3-drag-3.0.0.tgz#994aae9cd23c719f53b5e10e3a0a6108c69607ba" + resolved "https://registry.yarnpkg.com/d3-drag/-/d3-drag-3.0.0.tgz#994aae9cd23c719f53b5e10e3a0a6108c69607ba" integrity sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg== dependencies: d3-dispatch "1 - 3" @@ -3553,13 +3554,13 @@ d3-delaunay@^6.0.4: "d3-ease@1 - 3": version "3.0.1" - resolved "https://registry.npmmirror.com/d3-ease/-/d3-ease-3.0.1.tgz#9658ac38a2140d59d346160f1f6c30fda0bd12f4" + resolved "https://registry.yarnpkg.com/d3-ease/-/d3-ease-3.0.1.tgz#9658ac38a2140d59d346160f1f6c30fda0bd12f4" integrity sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w== "d3-format@1 - 3": - version "3.1.0" - resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-3.1.0.tgz#9260e23a28ea5cb109e93b21a06e24e2ebd55641" - integrity sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA== + version "3.1.2" + resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-3.1.2.tgz#01fdb46b58beb1f55b10b42ad70b6e344d5eb2ae" + integrity sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg== d3-format@^1.4.4: version "1.4.5" @@ -3599,7 +3600,7 @@ d3-scale@^4.0.2: "d3-selection@2 - 3", d3-selection@3, d3-selection@^3.0.0: version "3.0.0" - resolved "https://registry.npmmirror.com/d3-selection/-/d3-selection-3.0.0.tgz#c25338207efa72cc5b9bd1458a1a41901f1e1b31" + resolved "https://registry.yarnpkg.com/d3-selection/-/d3-selection-3.0.0.tgz#c25338207efa72cc5b9bd1458a1a41901f1e1b31" integrity sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ== d3-shape@^3.2.0: @@ -3644,12 +3645,12 @@ d3-time@^1.0.11: "d3-timer@1 - 3": version "3.0.1" - resolved "https://registry.npmmirror.com/d3-timer/-/d3-timer-3.0.1.tgz#6284d2a2708285b1abb7e201eda4380af35e63b0" + resolved "https://registry.yarnpkg.com/d3-timer/-/d3-timer-3.0.1.tgz#6284d2a2708285b1abb7e201eda4380af35e63b0" integrity sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA== "d3-transition@2 - 3": version "3.0.1" - resolved "https://registry.npmmirror.com/d3-transition/-/d3-transition-3.0.1.tgz#6869fdde1448868077fdd5989200cb61b2a1645f" + resolved "https://registry.yarnpkg.com/d3-transition/-/d3-transition-3.0.1.tgz#6869fdde1448868077fdd5989200cb61b2a1645f" integrity sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w== dependencies: d3-color "1 - 3" @@ -3660,7 +3661,7 @@ d3-time@^1.0.11: d3-zoom@^3.0.0: version "3.0.0" - resolved "https://registry.npmmirror.com/d3-zoom/-/d3-zoom-3.0.0.tgz#d13f4165c73217ffeaa54295cd6969b3e7aee8f3" + resolved "https://registry.yarnpkg.com/d3-zoom/-/d3-zoom-3.0.0.tgz#d13f4165c73217ffeaa54295cd6969b3e7aee8f3" integrity sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw== dependencies: d3-dispatch "1 - 3" @@ -3704,9 +3705,9 @@ date-fns@^2.28.0: "@babel/runtime" "^7.21.0" dayjs@^1.8.34: - version "1.11.13" - resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.13.tgz#92430b0139055c3ebb60150aa13e860a4b5a366c" - integrity sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg== + version "1.11.19" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.19.tgz#15dc98e854bb43917f12021806af897c58ae2938" + integrity sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw== debug@^3.2.7: version "3.2.7" @@ -3716,9 +3717,9 @@ debug@^3.2.7: ms "^2.1.1" debug@^4.1.0, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.4.0: - version "4.4.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.1.tgz#e5a8bc6cbc4c6cd3e64308b0693a3d4fa550189b" - integrity sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ== + version "4.4.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" + integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== dependencies: ms "^2.1.3" @@ -3805,10 +3806,10 @@ eastasianwidth@^0.2.0: resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== -electron-to-chromium@^1.5.173: - version "1.5.180" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.180.tgz#3e4f6e7494d6371e014af176dfdfd43c8a4b56df" - integrity sha512-ED+GEyEh3kYMwt2faNmgMB0b8O5qtATGgR4RmRsIp4T6p7B8vdMbIedYndnvZfsaXvSzegtpfqRMDNCjjiSduA== +electron-to-chromium@^1.5.263: + version "1.5.286" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.286.tgz#142be1ab5e1cd5044954db0e5898f60a4960384e" + integrity sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A== emoji-regex@^8.0.0: version "8.0.0" @@ -3833,16 +3834,16 @@ entities@^4.4.0: integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== error-ex@^1.3.1: - version "1.3.2" - resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" - integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + version "1.3.4" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.4.tgz#b3a8d8bb6f92eecc1629e3e27d3c8607a8a32414" + integrity sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ== dependencies: is-arrayish "^0.2.1" -es-abstract@^1.17.5, es-abstract@^1.23.2, es-abstract@^1.23.3, es-abstract@^1.23.5, es-abstract@^1.23.6, es-abstract@^1.23.9, es-abstract@^1.24.0: - version "1.24.0" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.24.0.tgz#c44732d2beb0acc1ed60df840869e3106e7af328" - integrity sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg== +es-abstract@^1.17.5, es-abstract@^1.23.2, es-abstract@^1.23.3, es-abstract@^1.23.5, es-abstract@^1.23.6, es-abstract@^1.23.9, es-abstract@^1.24.0, es-abstract@^1.24.1: + version "1.24.1" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.24.1.tgz#f0c131ed5ea1bb2411134a8dd94def09c46c7899" + integrity sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw== dependencies: array-buffer-byte-length "^1.0.2" arraybuffer.prototype.slice "^1.0.4" @@ -3910,25 +3911,25 @@ es-errors@^1.3.0: integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== es-iterator-helpers@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/es-iterator-helpers/-/es-iterator-helpers-1.2.1.tgz#d1dd0f58129054c0ad922e6a9a1e65eef435fe75" - integrity sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w== + version "1.2.2" + resolved "https://registry.yarnpkg.com/es-iterator-helpers/-/es-iterator-helpers-1.2.2.tgz#d979a9f686e2b0b72f88dbead7229924544720bc" + integrity sha512-BrUQ0cPTB/IwXj23HtwHjS9n7O4h9FX94b4xc5zlTHxeLgTAdzYUDyy6KdExAl9lbN5rtfe44xpjpmj9grxs5w== dependencies: call-bind "^1.0.8" - call-bound "^1.0.3" + call-bound "^1.0.4" define-properties "^1.2.1" - es-abstract "^1.23.6" + es-abstract "^1.24.1" es-errors "^1.3.0" - es-set-tostringtag "^2.0.3" + es-set-tostringtag "^2.1.0" function-bind "^1.1.2" - get-intrinsic "^1.2.6" + get-intrinsic "^1.3.0" globalthis "^1.0.4" gopd "^1.2.0" has-property-descriptors "^1.0.2" has-proto "^1.2.0" has-symbols "^1.1.0" internal-slot "^1.1.0" - iterator.prototype "^1.1.4" + iterator.prototype "^1.1.5" safe-array-concat "^1.1.3" es-object-atoms@^1.0.0, es-object-atoms@^1.1.1: @@ -3938,7 +3939,7 @@ es-object-atoms@^1.0.0, es-object-atoms@^1.1.1: dependencies: es-errors "^1.3.0" -es-set-tostringtag@^2.0.3, es-set-tostringtag@^2.1.0: +es-set-tostringtag@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz#f31dbbe0c183b00a6d26eb6325c810c0fd18bd4d" integrity sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA== @@ -3964,37 +3965,34 @@ es-to-primitive@^1.3.0: is-date-object "^1.0.5" is-symbol "^1.0.4" -esbuild@^0.25.0: - version "0.25.6" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.25.6.tgz#9b82a3db2fa131aec069ab040fd57ed0a880cdcd" - integrity sha512-GVuzuUwtdsghE3ocJ9Bs8PNoF13HNQ5TXbEi2AhvVb8xU1Iwt9Fos9FEamfoee+u/TOsn7GUWc04lz46n2bbTg== +esbuild@^0.21.3: + version "0.21.5" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.21.5.tgz#9ca301b120922959b766360d8ac830da0d02997d" + integrity sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw== optionalDependencies: - "@esbuild/aix-ppc64" "0.25.6" - "@esbuild/android-arm" "0.25.6" - "@esbuild/android-arm64" "0.25.6" - "@esbuild/android-x64" "0.25.6" - "@esbuild/darwin-arm64" "0.25.6" - "@esbuild/darwin-x64" "0.25.6" - "@esbuild/freebsd-arm64" "0.25.6" - "@esbuild/freebsd-x64" "0.25.6" - "@esbuild/linux-arm" "0.25.6" - "@esbuild/linux-arm64" "0.25.6" - "@esbuild/linux-ia32" "0.25.6" - "@esbuild/linux-loong64" "0.25.6" - "@esbuild/linux-mips64el" "0.25.6" - "@esbuild/linux-ppc64" "0.25.6" - "@esbuild/linux-riscv64" "0.25.6" - "@esbuild/linux-s390x" "0.25.6" - "@esbuild/linux-x64" "0.25.6" - "@esbuild/netbsd-arm64" "0.25.6" - "@esbuild/netbsd-x64" "0.25.6" - "@esbuild/openbsd-arm64" "0.25.6" - "@esbuild/openbsd-x64" "0.25.6" - "@esbuild/openharmony-arm64" "0.25.6" - "@esbuild/sunos-x64" "0.25.6" - "@esbuild/win32-arm64" "0.25.6" - "@esbuild/win32-ia32" "0.25.6" - "@esbuild/win32-x64" "0.25.6" + "@esbuild/aix-ppc64" "0.21.5" + "@esbuild/android-arm" "0.21.5" + "@esbuild/android-arm64" "0.21.5" + "@esbuild/android-x64" "0.21.5" + "@esbuild/darwin-arm64" "0.21.5" + "@esbuild/darwin-x64" "0.21.5" + "@esbuild/freebsd-arm64" "0.21.5" + "@esbuild/freebsd-x64" "0.21.5" + "@esbuild/linux-arm" "0.21.5" + "@esbuild/linux-arm64" "0.21.5" + "@esbuild/linux-ia32" "0.21.5" + "@esbuild/linux-loong64" "0.21.5" + "@esbuild/linux-mips64el" "0.21.5" + "@esbuild/linux-ppc64" "0.21.5" + "@esbuild/linux-riscv64" "0.21.5" + "@esbuild/linux-s390x" "0.21.5" + "@esbuild/linux-x64" "0.21.5" + "@esbuild/netbsd-x64" "0.21.5" + "@esbuild/openbsd-x64" "0.21.5" + "@esbuild/sunos-x64" "0.21.5" + "@esbuild/win32-arm64" "0.21.5" + "@esbuild/win32-ia32" "0.21.5" + "@esbuild/win32-x64" "0.21.5" escalade@^3.2.0: version "3.2.0" @@ -4012,9 +4010,9 @@ escape-string-regexp@^4.0.0: integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== eslint-config-prettier@^9.1.0: - version "9.1.0" - resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz#31af3d94578645966c082fcb71a5846d3c94867f" - integrity sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw== + version "9.1.2" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-9.1.2.tgz#90deb4fa0259592df774b600dbd1d2249a78ce91" + integrity sha512-iI1f+D2ViGn+uvv5HuHVUamg8ll4tN+JRHGc6IJi4TP9Kl976C57fzPXgseXNs8v0iA8aSJpHsTWjDb9QJamGQ== eslint-import-resolver-node@^0.3.9: version "0.3.9" @@ -4118,23 +4116,22 @@ eslint-visitor-keys@^4.2.1: integrity sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ== eslint@^9.3.0: - version "9.30.1" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.30.1.tgz#d4107b39964412acd9b5c0744f1c6df514fa1211" - integrity sha512-zmxXPNMOXmwm9E0yQLi5uqXHs7uq2UIiqEKo3Gq+3fwo1XrJ+hijAZImyF7hclW3E6oHz43Yk3RP8at6OTKflQ== + version "9.39.2" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.39.2.tgz#cb60e6d16ab234c0f8369a3fe7cc87967faf4b6c" + integrity sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw== dependencies: - "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/eslint-utils" "^4.8.0" "@eslint-community/regexpp" "^4.12.1" - "@eslint/config-array" "^0.21.0" - "@eslint/config-helpers" "^0.3.0" - "@eslint/core" "^0.14.0" + "@eslint/config-array" "^0.21.1" + "@eslint/config-helpers" "^0.4.2" + "@eslint/core" "^0.17.0" "@eslint/eslintrc" "^3.3.1" - "@eslint/js" "9.30.1" - "@eslint/plugin-kit" "^0.3.1" + "@eslint/js" "9.39.2" + "@eslint/plugin-kit" "^0.4.1" "@humanfs/node" "^0.16.6" "@humanwhocodes/module-importer" "^1.0.1" "@humanwhocodes/retry" "^0.4.2" "@types/estree" "^1.0.6" - "@types/json-schema" "^7.0.15" ajv "^6.12.4" chalk "^4.0.0" cross-spawn "^7.0.6" @@ -4168,9 +4165,9 @@ espree@^10.0.1, espree@^10.4.0: eslint-visitor-keys "^4.2.1" esquery@^1.5.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7" - integrity sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg== + version "1.7.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.7.0.tgz#08d048f261f0ddedb5bae95f46809463d9c9496d" + integrity sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g== dependencies: estraverse "^5.1.0" @@ -4246,16 +4243,16 @@ fast-levenshtein@^2.0.6: integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== fastq@^1.6.0: - version "1.19.1" - resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.19.1.tgz#d50eaba803c8846a883c16492821ebcd2cda55f5" - integrity sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ== + version "1.20.1" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.20.1.tgz#ca750a10dc925bc8b18839fd203e3ef4b3ced675" + integrity sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw== dependencies: reusify "^1.0.4" -fdir@^6.4.4, fdir@^6.4.6: - version "6.4.6" - resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.4.6.tgz#2b268c0232697063111bbf3f64810a2a741ba281" - integrity sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w== +fdir@^6.5.0: + version "6.5.0" + resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.5.0.tgz#ed2ab967a331ade62f18d077dae192684d50d350" + integrity sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg== file-entry-cache@^8.0.0: version "8.0.0" @@ -4312,10 +4309,10 @@ foreground-child@^3.1.0: cross-spawn "^7.0.6" signal-exit "^4.0.1" -fractional-indexing-jittered@^0.9.1: - version "0.9.1" - resolved "https://registry.yarnpkg.com/fractional-indexing-jittered/-/fractional-indexing-jittered-0.9.1.tgz#d1bf552cb0ab460ba992000c108b19c894900ba0" - integrity sha512-qyzDZ7JXWf/yZT2rQDpQwFBbIaZS2o+zb0s740vqreXQ6bFQPd8tAy4D1gGN0CUeIcnNHjuvb0EaLnqHhGV/PA== +fractional-indexing-jittered@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fractional-indexing-jittered/-/fractional-indexing-jittered-1.0.0.tgz#d79f81186a371e6f0df498a0f4d4ebc3b2cc6bee" + integrity sha512-0tLU0FOedVY7lrvN4LK0DVj6FTuYM0pWDpN97/8UTZE2lx1+OwX8+2uL7IOWc2PmktYTHQjMT6FvZZ3SGCdZdg== framer-motion@^11.2.10: version "11.18.2" @@ -4373,6 +4370,11 @@ functions-have-names@^1.2.3: resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== +generator-function@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/generator-function/-/generator-function-2.0.1.tgz#0e75dd410d1243687a0ba2e951b94eedb8f737a2" + integrity sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g== + gensync@^1.0.0-beta.2: version "1.0.0-beta.2" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" @@ -4417,9 +4419,9 @@ get-symbol-description@^1.1.0: get-intrinsic "^1.2.6" get-tsconfig@^4.10.0: - version "4.10.1" - resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.10.1.tgz#d34c1c01f47d65a606c37aa7a177bc3e56ab4b2e" - integrity sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ== + version "4.13.6" + resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.13.6.tgz#2fbfda558a98a691a798f123afd95915badce876" + integrity sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw== dependencies: resolve-pkg-maps "^1.0.0" @@ -4466,9 +4468,9 @@ globals@^14.0.0: integrity sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ== globals@^16.2.0: - version "16.3.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-16.3.0.tgz#66118e765ddaf9e2d880f7e17658543f93f1f667" - integrity sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ== + version "16.5.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-16.5.0.tgz#ccf1594a437b97653b2be13ed4d8f5c9f850cac1" + integrity sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ== globalthis@^1.0.4: version "1.0.4" @@ -4698,7 +4700,7 @@ is-callable@^1.2.7: resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== -is-core-module@^2.13.0, is-core-module@^2.16.0, is-core-module@^2.16.1: +is-core-module@^2.13.0, is-core-module@^2.16.1: version "2.16.1" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.16.1.tgz#2a98801a849f43e2add644fbb6bc6229b19a4ef4" integrity sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w== @@ -4740,12 +4742,13 @@ is-fullwidth-code-point@^3.0.0: integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== is-generator-function@^1.0.10: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.1.0.tgz#bf3eeda931201394f57b5dba2800f91a238309ca" - integrity sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ== + version "1.1.2" + resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.1.2.tgz#ae3b61e3d5ea4e4839b90bad22b02335051a17d5" + integrity sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA== dependencies: - call-bound "^1.0.3" - get-proto "^1.0.0" + call-bound "^1.0.4" + generator-function "^2.0.0" + get-proto "^1.0.1" has-tostringtag "^1.0.2" safe-regex-test "^1.1.0" @@ -4870,7 +4873,7 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== -iterator.prototype@^1.1.4: +iterator.prototype@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/iterator.prototype/-/iterator.prototype-1.1.5.tgz#12c959a29de32de0aa3bbbb801f4d777066dae39" integrity sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g== @@ -4901,10 +4904,10 @@ js-sha3@0.8.0: resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== -js-yaml@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" - integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== +js-yaml@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.1.tgz#854c292467705b699476e1a2decc0c8a3458806b" + integrity sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA== dependencies: argparse "^2.0.1" @@ -4971,9 +4974,9 @@ jszip@^3.10.1: setimmediate "^1.0.5" katex@^0.16.0, katex@^0.16.2: - version "0.16.22" - resolved "https://registry.yarnpkg.com/katex/-/katex-0.16.22.tgz#d2b3d66464b1e6d69e6463b28a86ced5a02c5ccd" - integrity sha512-XCHRdUw4lf3SKBaJe4EvgqIuWwkPSo9XoeO8GjQW94Bp7TWv9hNhzZjZ+OH9yf1UmLygb7DIT5GSFQiyt16zYg== + version "0.16.28" + resolved "https://registry.yarnpkg.com/katex/-/katex-0.16.28.tgz#64068425b5a29b41b136aae0d51cbb2c71d64c39" + integrity sha512-YHzO7721WbmAL6Ov1uzN/l5mY5WWWhJBSW+jq4tkfZfsxmo1hu6frS0EOswvjBUnWE6NtjEs48SFn5CQESRLZg== dependencies: commander "^8.3.0" @@ -5018,10 +5021,10 @@ linkify-it@^5.0.0: dependencies: uc.micro "^2.0.0" -linkifyjs@^4.2.0: - version "4.3.1" - resolved "https://registry.yarnpkg.com/linkifyjs/-/linkifyjs-4.3.1.tgz#1f246ebf4be040002accd1f4535b6af7c7e37898" - integrity sha512-DRSlB9DKVW04c4SUdGvKK5FR6be45lTU9M76JnngqPeeGDqPwYc0zdUErtsNVMtxPXgUWV4HbXbnC4sNyBxkYg== +linkifyjs@^4.3.2: + version "4.3.2" + resolved "https://registry.yarnpkg.com/linkifyjs/-/linkifyjs-4.3.2.tgz#d97eb45419aabf97ceb4b05a7adeb7b8c8ade2b1" + integrity sha512-NT1CJtq3hHIreOianA8aSXn6Cw0JzYOuDQbOrSPe7gqFnCpKP++MQe3ODgO3oh2GJFORkAAdqredOa60z63GbA== listenercount@~1.0.1: version "1.0.1" @@ -5126,9 +5129,9 @@ lodash.uniq@^4.5.0: integrity sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ== lodash@^4.17.21: - version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" - integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + version "4.17.23" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.23.tgz#f113b0378386103be4f6893388c73d0bde7f2c5a" + integrity sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w== loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" @@ -5143,9 +5146,9 @@ lru-cache@^10.2.0: integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== lru-cache@^11.0.1: - version "11.1.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-11.1.0.tgz#afafb060607108132dbc1cf8ae661afb69486117" - integrity sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A== + version "11.2.6" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-11.2.6.tgz#356bf8a29e88a7a2945507b31f6429a65a192c58" + integrity sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ== lru-cache@^5.1.1: version "5.1.1" @@ -5160,9 +5163,9 @@ lz-string@^1.5.0: integrity sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ== markdown-it@^14.0.0: - version "14.1.0" - resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-14.1.0.tgz#3c3c5992883c633db4714ccb4d7b5935d98b7d45" - integrity sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg== + version "14.1.1" + resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-14.1.1.tgz#856f90b66fc39ae70affd25c1b18b581d7deee1f" + integrity sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA== dependencies: argparse "^2.0.1" entities "^4.4.0" @@ -5172,9 +5175,9 @@ markdown-it@^14.0.0: uc.micro "^2.1.0" markdown-to-jsx@^7.1.7: - version "7.7.10" - resolved "https://registry.yarnpkg.com/markdown-to-jsx/-/markdown-to-jsx-7.7.10.tgz#74cbc4689f7e635c3195148b60f47dca47284943" - integrity sha512-au62yyLyJukhC2P1TYi3uBi/RScGYai69uT72D8a048QH8rRj+yhND3C21GdZHE+6emtsf6Yqemcf//K+EIWDg== + version "7.7.17" + resolved "https://registry.yarnpkg.com/markdown-to-jsx/-/markdown-to-jsx-7.7.17.tgz#6e997d6aa4dbe2e69c423c65745541846777483c" + integrity sha512-7mG/1feQ0TX5I7YyMZVDgCC/y2I3CiEhIRQIhyov9nGBP5eoVrOXXHuL5ZP8GRfxVZKRiXWJgwXkb9It+nQZfQ== match-sorter@^6.0.2: version "6.4.0" @@ -5298,9 +5301,9 @@ nanoid@^3.3.11, nanoid@^3.3.6: integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w== napi-postinstall@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/napi-postinstall/-/napi-postinstall-0.3.0.tgz#888e51d1fb500e86dcf6ace1baccdbb377e654ce" - integrity sha512-M7NqKyhODKV1gRLdkwE7pDsZP2/SC2a2vHkOYh9MCpKMbWVfyVfUw5MaH83Fv6XMjxr5jryUp3IDDL9rlxsTeA== + version "0.3.4" + resolved "https://registry.yarnpkg.com/napi-postinstall/-/napi-postinstall-0.3.4.tgz#7af256d6588b5f8e952b9190965d6b019653bbb9" + integrity sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ== natural-compare@^1.4.0: version "1.4.0" @@ -5308,11 +5311,11 @@ natural-compare@^1.4.0: integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== next@^14.2.4: - version "14.2.30" - resolved "https://registry.yarnpkg.com/next/-/next-14.2.30.tgz#7b7288859794574067f65d6e2ea98822f2173006" - integrity sha512-+COdu6HQrHHFQ1S/8BBsCag61jZacmvbuL2avHvQFbWa2Ox7bE+d8FyNgxRLjXQ5wtPyQwEmk85js/AuaG2Sbg== + version "14.2.35" + resolved "https://registry.yarnpkg.com/next/-/next-14.2.35.tgz#7c68873a15fe5a19401f2f993fea535be3366ee9" + integrity sha512-KhYd2Hjt/O1/1aZVX3dCwGXM1QmOV4eNM2UTacK5gipDdPN/oHHK/4oVGy7X8GMfPMsUTUEmGlsy0EY1YGAkig== dependencies: - "@next/env" "14.2.30" + "@next/env" "14.2.35" "@swc/helpers" "0.5.5" busboy "1.6.0" caniuse-lite "^1.0.30001579" @@ -5320,25 +5323,35 @@ next@^14.2.4: postcss "8.4.31" styled-jsx "5.1.1" optionalDependencies: - "@next/swc-darwin-arm64" "14.2.30" - "@next/swc-darwin-x64" "14.2.30" - "@next/swc-linux-arm64-gnu" "14.2.30" - "@next/swc-linux-arm64-musl" "14.2.30" - "@next/swc-linux-x64-gnu" "14.2.30" - "@next/swc-linux-x64-musl" "14.2.30" - "@next/swc-win32-arm64-msvc" "14.2.30" - "@next/swc-win32-ia32-msvc" "14.2.30" - "@next/swc-win32-x64-msvc" "14.2.30" + "@next/swc-darwin-arm64" "14.2.33" + "@next/swc-darwin-x64" "14.2.33" + "@next/swc-linux-arm64-gnu" "14.2.33" + "@next/swc-linux-arm64-musl" "14.2.33" + "@next/swc-linux-x64-gnu" "14.2.33" + "@next/swc-linux-x64-musl" "14.2.33" + "@next/swc-win32-arm64-msvc" "14.2.33" + "@next/swc-win32-ia32-msvc" "14.2.33" + "@next/swc-win32-x64-msvc" "14.2.33" nice-try@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== -node-releases@^2.0.19: - version "2.0.19" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.19.tgz#9e445a52950951ec4d177d843af370b411caf314" - integrity sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw== +node-exports-info@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/node-exports-info/-/node-exports-info-1.6.0.tgz#1aedafb01a966059c9a5e791a94a94d93f5c2a13" + integrity sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw== + dependencies: + array.prototype.flatmap "^1.3.3" + es-errors "^1.3.0" + object.entries "^1.1.9" + semver "^6.3.1" + +node-releases@^2.0.27: + version "2.0.27" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.27.tgz#eedca519205cf20f650f61d56b070db111231e4e" + integrity sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA== normalize-package-data@^2.3.2: version "2.5.0" @@ -5500,7 +5513,7 @@ pako@~1.0.2: paper@^0.12.18: version "0.12.18" - resolved "https://registry.npmmirror.com/paper/-/paper-0.12.18.tgz#e024056217a35c36e2b5fda4629310fdc7025c91" + resolved "https://registry.yarnpkg.com/paper/-/paper-0.12.18.tgz#e024056217a35c36e2b5fda4629310fdc7025c91" integrity sha512-ZSLIEejQTJZuYHhSSqAf4jXOnii0kPhCJGAnYAANtdS72aNwXJ9cP95tZHgq1tnNpvEwgQhggy+4OarviqTCGw== parent-module@^1.0.0: @@ -5583,10 +5596,10 @@ picomatch@^2.3.1: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== -picomatch@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.2.tgz#77c742931e8f3b8820946c76cd0c1f13730d1dab" - integrity sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg== +picomatch@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.3.tgz#796c76136d1eead715db1e7bad785dedd695a042" + integrity sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q== pidtree@^0.3.0: version "0.3.1" @@ -5612,7 +5625,7 @@ postcss@8.4.31: picocolors "^1.0.0" source-map-js "^1.0.2" -postcss@^8.5.6: +postcss@^8.4.43: version "8.5.6" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.6.tgz#2825006615a619b4f62a9e7426cc120b349a8f3c" integrity sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg== @@ -5627,9 +5640,9 @@ prelude-ls@^1.2.1: integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== prettier@^3.3.3: - version "3.6.2" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.6.2.tgz#ccda02a1003ebbb2bfda6f83a074978f608b9393" - integrity sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ== + version "3.8.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.8.1.tgz#edf48977cf991558f4fcbd8a3ba6015ba2a3a173" + integrity sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg== process-nextick-args@~2.0.0: version "2.0.1" @@ -5646,9 +5659,9 @@ prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1: react-is "^16.13.1" prosemirror-changeset@^2.3.0: - version "2.3.1" - resolved "https://registry.yarnpkg.com/prosemirror-changeset/-/prosemirror-changeset-2.3.1.tgz#eee3299cfabc7a027694e9abdc4e85505e9dd5e7" - integrity sha512-j0kORIBm8ayJNl3zQvD1TTPHJX3g042et6y/KQhZhnPrruO8exkTgG8X+NRpj7kIyMMEx74Xb3DyMIBtO0IKkQ== + version "2.4.0" + resolved "https://registry.yarnpkg.com/prosemirror-changeset/-/prosemirror-changeset-2.4.0.tgz#8d8ea0290cb9545c298ec427ac3a8f298c39170f" + integrity sha512-LvqH2v7Q2SF6yxatuPP2e8vSUKS/L+xAU7dPDC4RMyHMhZoGDfBC74mYuyYF4gLqOEG758wajtyhNnsTkuhvng== dependencies: prosemirror-transform "^1.0.0" @@ -5678,9 +5691,9 @@ prosemirror-dropcursor@^1.8.1: prosemirror-view "^1.1.0" prosemirror-gapcursor@^1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/prosemirror-gapcursor/-/prosemirror-gapcursor-1.3.2.tgz#5fa336b83789c6199a7341c9493587e249215cb4" - integrity sha512-wtjswVBd2vaQRrnYZaBCbyDqr232Ed4p2QPtRIUK5FuqHYKGWkEwl08oQM4Tw7DOR0FsasARV5uJFvMZWxdNxQ== + version "1.4.0" + resolved "https://registry.yarnpkg.com/prosemirror-gapcursor/-/prosemirror-gapcursor-1.4.0.tgz#e1144a83b79db7ed0ec32cd0e915a0364220af43" + integrity sha512-z00qvurSdCEWUIulij/isHaqu4uLS8r/Fi61IbjdIPJEonQgggbJsLnstW7Lgdk4zQ68/yr6B6bf7sJXowIgdQ== dependencies: prosemirror-keymap "^1.0.0" prosemirror-model "^1.0.0" @@ -5688,9 +5701,9 @@ prosemirror-gapcursor@^1.3.2: prosemirror-view "^1.0.0" prosemirror-history@^1.0.0, prosemirror-history@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/prosemirror-history/-/prosemirror-history-1.4.1.tgz#cc370a46fb629e83a33946a0e12612e934ab8b98" - integrity sha512-2JZD8z2JviJrboD9cPuX/Sv/1ChFng+xh2tChQ2X4bB2HeK+rra/bmJ3xGntCcjhOqIzSDG6Id7e8RJ9QPXLEQ== + version "1.5.0" + resolved "https://registry.yarnpkg.com/prosemirror-history/-/prosemirror-history-1.5.0.tgz#ee21fc5de85a1473e3e3752015ffd6d649a06859" + integrity sha512-zlzTiH01eKA55UAf1MEjtssJeHnGxO0j4K4Dpx+gnmX9n+SHNlDqI2oO1Kv1iPN5B1dm5fsljCfqKF9nFL6HRg== dependencies: prosemirror-state "^1.2.2" prosemirror-transform "^1.0.0" @@ -5698,14 +5711,14 @@ prosemirror-history@^1.0.0, prosemirror-history@^1.4.1: rope-sequence "^1.3.0" prosemirror-inputrules@^1.4.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/prosemirror-inputrules/-/prosemirror-inputrules-1.5.0.tgz#e22bfaf1d6ea4fe240ad447c184af3d520d43c37" - integrity sha512-K0xJRCmt+uSw7xesnHmcn72yBGTbY45vm8gXI4LZXbx2Z0jwh5aF9xrGQgrVPu0WbyFVFF3E/o9VhJYz6SQWnA== + version "1.5.1" + resolved "https://registry.yarnpkg.com/prosemirror-inputrules/-/prosemirror-inputrules-1.5.1.tgz#d2e935f6086e3801486b09222638f61dae89a570" + integrity sha512-7wj4uMjKaXWAQ1CDgxNzNtR9AlsuwzHfdFH1ygEHA2KHF2DOEaXl1CJfNPAKCg9qNEh4rum975QLaCiQPyY6Fw== dependencies: prosemirror-state "^1.0.0" prosemirror-transform "^1.0.0" -prosemirror-keymap@^1.0.0, prosemirror-keymap@^1.2.2: +prosemirror-keymap@^1.0.0, prosemirror-keymap@^1.2.2, prosemirror-keymap@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/prosemirror-keymap/-/prosemirror-keymap-1.2.3.tgz#c0f6ab95f75c0b82c97e44eb6aaf29cbfc150472" integrity sha512-4HucRlpiLd1IPQQXNqeo81BGtkY8Ai5smHhKW9jjPKRc2wQIxksg7Hl1tTI2IfT2B/LgX6bfYvXxEpJl7aKYKw== @@ -5714,28 +5727,28 @@ prosemirror-keymap@^1.0.0, prosemirror-keymap@^1.2.2: w3c-keyname "^2.2.0" prosemirror-markdown@^1.13.1: - version "1.13.2" - resolved "https://registry.yarnpkg.com/prosemirror-markdown/-/prosemirror-markdown-1.13.2.tgz#863eb3fd5f57a444e4378174622b562735b1c503" - integrity sha512-FPD9rHPdA9fqzNmIIDhhnYQ6WgNoSWX9StUZ8LEKapaXU9i6XgykaHKhp6XMyXlOWetmaFgGDS/nu/w9/vUc5g== + version "1.13.4" + resolved "https://registry.yarnpkg.com/prosemirror-markdown/-/prosemirror-markdown-1.13.4.tgz#4620e6a0580cd52b5fc8e352c7e04830cd4b3048" + integrity sha512-D98dm4cQ3Hs6EmjK500TdAOew4Z03EV71ajEFiWra3Upr7diytJsjF4mPV2dW+eK5uNectiRj0xFxYI9NLXDbw== dependencies: "@types/markdown-it" "^14.0.0" markdown-it "^14.0.0" prosemirror-model "^1.25.0" prosemirror-menu@^1.2.4: - version "1.2.5" - resolved "https://registry.yarnpkg.com/prosemirror-menu/-/prosemirror-menu-1.2.5.tgz#dea00e7b623cea89f4d76963bee22d2ac2343250" - integrity sha512-qwXzynnpBIeg1D7BAtjOusR+81xCp53j7iWu/IargiRZqRjGIlQuu1f3jFi+ehrHhWMLoyOQTSRx/IWZJqOYtQ== + version "1.3.0" + resolved "https://registry.yarnpkg.com/prosemirror-menu/-/prosemirror-menu-1.3.0.tgz#f51e25259b91d7c35ad7b65fc0c92d838404e177" + integrity sha512-TImyPXCHPcDsSka2/lwJ6WjTASr4re/qWq1yoTTuLOqfXucwF6VcRa2LWCkM/EyTD1UO3CUwiH8qURJoWJRxwg== dependencies: crelt "^1.0.0" prosemirror-commands "^1.0.0" prosemirror-history "^1.0.0" prosemirror-state "^1.0.0" -prosemirror-model@^1.0.0, prosemirror-model@^1.20.0, prosemirror-model@^1.21.0, prosemirror-model@^1.23.0, prosemirror-model@^1.25.0: - version "1.25.1" - resolved "https://registry.yarnpkg.com/prosemirror-model/-/prosemirror-model-1.25.1.tgz#aeae9f1ec79fcaa76f6fc619800d91fbcf726870" - integrity sha512-AUvbm7qqmpZa5d9fPKMvH1Q5bqYQvAZWOGRvxsB6iFLyycvC9MwNemNVjHVrWgjaoxAfY8XVg7DbvQ/qxvI9Eg== +prosemirror-model@^1.0.0, prosemirror-model@^1.20.0, prosemirror-model@^1.21.0, prosemirror-model@^1.23.0, prosemirror-model@^1.25.0, prosemirror-model@^1.25.4: + version "1.25.4" + resolved "https://registry.yarnpkg.com/prosemirror-model/-/prosemirror-model-1.25.4.tgz#8ebfbe29ecbee9e5e2e4048c4fe8e363fcd56e7c" + integrity sha512-PIM7E43PBxKce8OQeezAs9j4TP+5yDpZVbuurd1h5phUxEKIu+G2a+EUZzIC5nS1mJktDJWzbqS23n1tsAf5QA== dependencies: orderedmap "^2.0.0" @@ -5755,25 +5768,25 @@ prosemirror-schema-list@^1.4.1: prosemirror-state "^1.0.0" prosemirror-transform "^1.7.3" -prosemirror-state@^1.0.0, prosemirror-state@^1.2.2, prosemirror-state@^1.4.3: - version "1.4.3" - resolved "https://registry.yarnpkg.com/prosemirror-state/-/prosemirror-state-1.4.3.tgz#94aecf3ffd54ec37e87aa7179d13508da181a080" - integrity sha512-goFKORVbvPuAQaXhpbemJFRKJ2aixr+AZMGiquiqKxaucC6hlpHNZHWgz5R7dS4roHiwq9vDctE//CZ++o0W1Q== +prosemirror-state@^1.0.0, prosemirror-state@^1.2.2, prosemirror-state@^1.4.3, prosemirror-state@^1.4.4: + version "1.4.4" + resolved "https://registry.yarnpkg.com/prosemirror-state/-/prosemirror-state-1.4.4.tgz#72b5e926f9e92dcee12b62a05fcc8a2de3bf5b39" + integrity sha512-6jiYHH2CIGbCfnxdHbXZ12gySFY/fz/ulZE333G6bPqIZ4F+TXo9ifiR86nAHpWnfoNjOb3o5ESi7J8Uz1jXHw== dependencies: prosemirror-model "^1.0.0" prosemirror-transform "^1.0.0" prosemirror-view "^1.27.0" prosemirror-tables@^1.6.4: - version "1.7.1" - resolved "https://registry.yarnpkg.com/prosemirror-tables/-/prosemirror-tables-1.7.1.tgz#df2507f285c6c7563097b4904cb7c4b9e0cd724b" - integrity sha512-eRQ97Bf+i9Eby99QbyAiyov43iOKgWa7QCGly+lrDt7efZ1v8NWolhXiB43hSDGIXT1UXgbs4KJN3a06FGpr1Q== + version "1.8.5" + resolved "https://registry.yarnpkg.com/prosemirror-tables/-/prosemirror-tables-1.8.5.tgz#104427012e5a5da1d2a38c122efee8d66bdd5104" + integrity sha512-V/0cDCsHKHe/tfWkeCmthNUcEp1IVO3p6vwN8XtwE9PZQLAZJigbw3QoraAdfJPir4NKJtNvOB8oYGKRl+t0Dw== dependencies: - prosemirror-keymap "^1.2.2" - prosemirror-model "^1.25.0" - prosemirror-state "^1.4.3" - prosemirror-transform "^1.10.3" - prosemirror-view "^1.39.1" + prosemirror-keymap "^1.2.3" + prosemirror-model "^1.25.4" + prosemirror-state "^1.4.4" + prosemirror-transform "^1.10.5" + prosemirror-view "^1.41.4" prosemirror-trailing-node@^3.0.0: version "3.0.0" @@ -5783,17 +5796,17 @@ prosemirror-trailing-node@^3.0.0: "@remirror/core-constants" "3.0.0" escape-string-regexp "^4.0.0" -prosemirror-transform@^1.0.0, prosemirror-transform@^1.1.0, prosemirror-transform@^1.10.2, prosemirror-transform@^1.10.3, prosemirror-transform@^1.7.3: - version "1.10.4" - resolved "https://registry.yarnpkg.com/prosemirror-transform/-/prosemirror-transform-1.10.4.tgz#56419eac14f9f56612c806ae46f9238648f3f02e" - integrity sha512-pwDy22nAnGqNR1feOQKHxoFkkUtepoFAd3r2hbEDsnf4wp57kKA36hXsB3njA9FtONBEwSDnDeCiJe+ItD+ykw== +prosemirror-transform@^1.0.0, prosemirror-transform@^1.1.0, prosemirror-transform@^1.10.2, prosemirror-transform@^1.10.5, prosemirror-transform@^1.7.3: + version "1.11.0" + resolved "https://registry.yarnpkg.com/prosemirror-transform/-/prosemirror-transform-1.11.0.tgz#f5c5050354423dc83c6b083f6f1959ec86a3f9ba" + integrity sha512-4I7Ce4KpygXb9bkiPS3hTEk4dSHorfRw8uI0pE8IhxlK2GXsqv5tIA7JUSxtSu7u8APVOTtbUBxTmnHIxVkIJw== dependencies: prosemirror-model "^1.21.0" -prosemirror-view@^1.0.0, prosemirror-view@^1.1.0, prosemirror-view@^1.27.0, prosemirror-view@^1.31.0, prosemirror-view@^1.37.0, prosemirror-view@^1.39.1: - version "1.40.0" - resolved "https://registry.yarnpkg.com/prosemirror-view/-/prosemirror-view-1.40.0.tgz#212e627a0c4f0198ac9823a1232e0099c9a92865" - integrity sha512-2G3svX0Cr1sJjkD/DYWSe3cfV5VPVTBOxI9XQEGWJDFEpsZb/gh4MV29ctv+OJx2RFX4BLt09i+6zaGM/ldkCw== +prosemirror-view@^1.0.0, prosemirror-view@^1.1.0, prosemirror-view@^1.27.0, prosemirror-view@^1.31.0, prosemirror-view@^1.37.0, prosemirror-view@^1.41.4: + version "1.41.6" + resolved "https://registry.yarnpkg.com/prosemirror-view/-/prosemirror-view-1.41.6.tgz#949d0407a91e36f6024db2191b8d3058dfd18838" + integrity sha512-mxpcDG4hNQa/CPtzxjdlir5bJFDlm0/x5nGBbStB2BWX+XOQ9M8ekEG+ojqB5BcVu2Rc80/jssCMZzSstJuSYg== dependencies: prosemirror-model "^1.20.0" prosemirror-state "^1.0.0" @@ -5815,57 +5828,57 @@ queue-microtask@^1.2.2: integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== radix-ui@^1.3.4: - version "1.4.2" - resolved "https://registry.yarnpkg.com/radix-ui/-/radix-ui-1.4.2.tgz#b74634f38313b0f292dda47ed8985471b3d66b44" - integrity sha512-fT/3YFPJzf2WUpqDoQi005GS8EpCi+53VhcLaHUj5fwkPYiZAjk1mSxFvbMA8Uq71L03n+WysuYC+mlKkXxt/Q== + version "1.4.3" + resolved "https://registry.yarnpkg.com/radix-ui/-/radix-ui-1.4.3.tgz#17712d9e26ee61fdf4cd3969f4e16a794419508b" + integrity sha512-aWizCQiyeAenIdUbqEpXgRA1ya65P13NKn/W8rWkcN0OPkRDxdBVLWnIEDsS2RpwCK2nobI7oMUSmexzTDyAmA== dependencies: - "@radix-ui/primitive" "1.1.2" + "@radix-ui/primitive" "1.1.3" "@radix-ui/react-accessible-icon" "1.1.7" - "@radix-ui/react-accordion" "1.2.11" - "@radix-ui/react-alert-dialog" "1.1.14" + "@radix-ui/react-accordion" "1.2.12" + "@radix-ui/react-alert-dialog" "1.1.15" "@radix-ui/react-arrow" "1.1.7" "@radix-ui/react-aspect-ratio" "1.1.7" "@radix-ui/react-avatar" "1.1.10" - "@radix-ui/react-checkbox" "1.3.2" - "@radix-ui/react-collapsible" "1.1.11" + "@radix-ui/react-checkbox" "1.3.3" + "@radix-ui/react-collapsible" "1.1.12" "@radix-ui/react-collection" "1.1.7" "@radix-ui/react-compose-refs" "1.1.2" "@radix-ui/react-context" "1.1.2" - "@radix-ui/react-context-menu" "2.2.15" - "@radix-ui/react-dialog" "1.1.14" + "@radix-ui/react-context-menu" "2.2.16" + "@radix-ui/react-dialog" "1.1.15" "@radix-ui/react-direction" "1.1.1" - "@radix-ui/react-dismissable-layer" "1.1.10" - "@radix-ui/react-dropdown-menu" "2.1.15" - "@radix-ui/react-focus-guards" "1.1.2" + "@radix-ui/react-dismissable-layer" "1.1.11" + "@radix-ui/react-dropdown-menu" "2.1.16" + "@radix-ui/react-focus-guards" "1.1.3" "@radix-ui/react-focus-scope" "1.1.7" - "@radix-ui/react-form" "0.1.7" - "@radix-ui/react-hover-card" "1.1.14" + "@radix-ui/react-form" "0.1.8" + "@radix-ui/react-hover-card" "1.1.15" "@radix-ui/react-label" "2.1.7" - "@radix-ui/react-menu" "2.1.15" - "@radix-ui/react-menubar" "1.1.15" - "@radix-ui/react-navigation-menu" "1.2.13" - "@radix-ui/react-one-time-password-field" "0.1.7" - "@radix-ui/react-password-toggle-field" "0.1.2" - "@radix-ui/react-popover" "1.1.14" - "@radix-ui/react-popper" "1.2.7" + "@radix-ui/react-menu" "2.1.16" + "@radix-ui/react-menubar" "1.1.16" + "@radix-ui/react-navigation-menu" "1.2.14" + "@radix-ui/react-one-time-password-field" "0.1.8" + "@radix-ui/react-password-toggle-field" "0.1.3" + "@radix-ui/react-popover" "1.1.15" + "@radix-ui/react-popper" "1.2.8" "@radix-ui/react-portal" "1.1.9" - "@radix-ui/react-presence" "1.1.4" + "@radix-ui/react-presence" "1.1.5" "@radix-ui/react-primitive" "2.1.3" "@radix-ui/react-progress" "1.1.7" - "@radix-ui/react-radio-group" "1.3.7" - "@radix-ui/react-roving-focus" "1.1.10" - "@radix-ui/react-scroll-area" "1.2.9" - "@radix-ui/react-select" "2.2.5" + "@radix-ui/react-radio-group" "1.3.8" + "@radix-ui/react-roving-focus" "1.1.11" + "@radix-ui/react-scroll-area" "1.2.10" + "@radix-ui/react-select" "2.2.6" "@radix-ui/react-separator" "1.1.7" - "@radix-ui/react-slider" "1.3.5" + "@radix-ui/react-slider" "1.3.6" "@radix-ui/react-slot" "1.2.3" - "@radix-ui/react-switch" "1.2.5" - "@radix-ui/react-tabs" "1.1.12" - "@radix-ui/react-toast" "1.2.14" - "@radix-ui/react-toggle" "1.1.9" - "@radix-ui/react-toggle-group" "1.1.10" - "@radix-ui/react-toolbar" "1.1.10" - "@radix-ui/react-tooltip" "1.2.7" + "@radix-ui/react-switch" "1.2.6" + "@radix-ui/react-tabs" "1.1.13" + "@radix-ui/react-toast" "1.2.15" + "@radix-ui/react-toggle" "1.1.10" + "@radix-ui/react-toggle-group" "1.1.11" + "@radix-ui/react-toolbar" "1.1.11" + "@radix-ui/react-tooltip" "1.2.8" "@radix-ui/react-use-callback-ref" "1.1.1" "@radix-ui/react-use-controllable-state" "1.2.2" "@radix-ui/react-use-effect-event" "0.0.2" @@ -5884,19 +5897,19 @@ react-dom@^18.3.1: scheduler "^0.23.2" react-hook-form@^7.31.2: - version "7.60.0" - resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.60.0.tgz#cc4bfa2952d602fc8f1dcf2d4929fcb6d9cdc782" - integrity sha512-SBrYOvMbDB7cV8ZfNpaiLcgjH/a1c7aK0lK+aNigpf4xWLO8q+o4tcvVurv3c4EOyzn/3dCsYt4GKD42VvJ/+A== + version "7.71.1" + resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.71.1.tgz#6a758958861682cf0eb22131eead684ba3618f66" + integrity sha512-9SUJKCGKo8HUSsCO+y0CtqkqI5nNuaDqTxyqPsZPqIwudpj4rCrAz/jZV+jn57bx5gtZKOh3neQu94DXMc+w5w== react-is@^16.13.1, react-is@^16.7.0: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== -react-is@^19.0.0, react-is@^19.1.0: - version "19.1.0" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-19.1.0.tgz#805bce321546b7e14c084989c77022351bbdd11b" - integrity sha512-Oe56aUPnkHyyDxxkvqtd7KkdQP5uIUfHxd5XTb3wE9d/kRnZLmKbDB0GWk919tdQ+mxxPtG6EAs6RMT6i1qtHg== +react-is@^19.0.0, react-is@^19.2.3: + version "19.2.4" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-19.2.4.tgz#a080758243c572ccd4a63386537654298c99d135" + integrity sha512-W+EWGn2v0ApPKgKKCy/7s7WHXkboGcsrXE+2joLyVxkbyVQfO3MUEaUQDHoSmb8TFFrSKYa9mw64WZHNHSDzYA== react-katex@^3.0.1: version "3.1.0" @@ -5928,9 +5941,9 @@ react-remove-scroll-bar@^2.3.7: tslib "^2.0.0" react-remove-scroll@^2.6.3: - version "2.7.1" - resolved "https://registry.yarnpkg.com/react-remove-scroll/-/react-remove-scroll-2.7.1.tgz#d2101d414f6d81d7d3bf033f3c1cb4785789f753" - integrity sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA== + version "2.7.2" + resolved "https://registry.yarnpkg.com/react-remove-scroll/-/react-remove-scroll-2.7.2.tgz#6442da56791117661978ae99cd29be9026fecca0" + integrity sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q== dependencies: react-remove-scroll-bar "^2.3.7" react-style-singleton "^2.2.3" @@ -5965,7 +5978,7 @@ react@^18.3.1: reactflow@^11.11.4: version "11.11.4" - resolved "https://registry.npmmirror.com/reactflow/-/reactflow-11.11.4.tgz#e3593e313420542caed81aecbd73fb9bc6576653" + resolved "https://registry.yarnpkg.com/reactflow/-/reactflow-11.11.4.tgz#e3593e313420542caed81aecbd73fb9bc6576653" integrity sha512-70FOtJkUWH3BAOsN+LU9lCrKoKbtOPnz2uq0CV2PLdNSwxTXOhCbsZr50GmZ+Rtw3jx8Uv7/vBFtCGixLfd4Og== dependencies: "@reactflow/background" "11.3.14" @@ -6070,20 +6083,23 @@ resolve-pkg-maps@^1.0.0: integrity sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw== resolve@^1.10.0, resolve@^1.19.0, resolve@^1.22.4: - version "1.22.10" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.10.tgz#b663e83ffb09bbf2386944736baae803029b8b39" - integrity sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w== + version "1.22.11" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.11.tgz#aad857ce1ffb8bfa9b0b1ac29f1156383f68c262" + integrity sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ== dependencies: - is-core-module "^2.16.0" + is-core-module "^2.16.1" path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" resolve@^2.0.0-next.5: - version "2.0.0-next.5" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.5.tgz#6b0ec3107e671e52b68cd068ef327173b90dc03c" - integrity sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA== + version "2.0.0-next.6" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.6.tgz#b3961812be69ace7b3bc35d5bf259434681294af" + integrity sha512-3JmVl5hMGtJ3kMmB3zi3DL25KfkCEyy3Tw7Gmw7z5w8M9WlwoPFnIvwChzu1+cF3iaK3sp18hhPz8ANeimdJfA== dependencies: - is-core-module "^2.13.0" + es-errors "^1.3.0" + is-core-module "^2.16.1" + node-exports-info "^1.6.0" + object-keys "^1.1.1" path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" @@ -6111,33 +6127,38 @@ robust-predicates@^3.0.2: resolved "https://registry.yarnpkg.com/robust-predicates/-/robust-predicates-3.0.2.tgz#d5b28528c4824d20fc48df1928d41d9efa1ad771" integrity sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg== -rollup@^4.40.0: - version "4.44.2" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.44.2.tgz#faedb27cb2aa6742530c39668092eecbaf78c488" - integrity sha512-PVoapzTwSEcelaWGth3uR66u7ZRo6qhPHc0f2uRO9fX6XDVNrIiGYS0Pj9+R8yIIYSD/mCx2b16Ws9itljKSPg== +rollup@^4.20.0: + version "4.57.1" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.57.1.tgz#947f70baca32db2b9c594267fe9150aa316e5a88" + integrity sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A== dependencies: "@types/estree" "1.0.8" optionalDependencies: - "@rollup/rollup-android-arm-eabi" "4.44.2" - "@rollup/rollup-android-arm64" "4.44.2" - "@rollup/rollup-darwin-arm64" "4.44.2" - "@rollup/rollup-darwin-x64" "4.44.2" - "@rollup/rollup-freebsd-arm64" "4.44.2" - "@rollup/rollup-freebsd-x64" "4.44.2" - "@rollup/rollup-linux-arm-gnueabihf" "4.44.2" - "@rollup/rollup-linux-arm-musleabihf" "4.44.2" - "@rollup/rollup-linux-arm64-gnu" "4.44.2" - "@rollup/rollup-linux-arm64-musl" "4.44.2" - "@rollup/rollup-linux-loongarch64-gnu" "4.44.2" - "@rollup/rollup-linux-powerpc64le-gnu" "4.44.2" - "@rollup/rollup-linux-riscv64-gnu" "4.44.2" - "@rollup/rollup-linux-riscv64-musl" "4.44.2" - "@rollup/rollup-linux-s390x-gnu" "4.44.2" - "@rollup/rollup-linux-x64-gnu" "4.44.2" - "@rollup/rollup-linux-x64-musl" "4.44.2" - "@rollup/rollup-win32-arm64-msvc" "4.44.2" - "@rollup/rollup-win32-ia32-msvc" "4.44.2" - "@rollup/rollup-win32-x64-msvc" "4.44.2" + "@rollup/rollup-android-arm-eabi" "4.57.1" + "@rollup/rollup-android-arm64" "4.57.1" + "@rollup/rollup-darwin-arm64" "4.57.1" + "@rollup/rollup-darwin-x64" "4.57.1" + "@rollup/rollup-freebsd-arm64" "4.57.1" + "@rollup/rollup-freebsd-x64" "4.57.1" + "@rollup/rollup-linux-arm-gnueabihf" "4.57.1" + "@rollup/rollup-linux-arm-musleabihf" "4.57.1" + "@rollup/rollup-linux-arm64-gnu" "4.57.1" + "@rollup/rollup-linux-arm64-musl" "4.57.1" + "@rollup/rollup-linux-loong64-gnu" "4.57.1" + "@rollup/rollup-linux-loong64-musl" "4.57.1" + "@rollup/rollup-linux-ppc64-gnu" "4.57.1" + "@rollup/rollup-linux-ppc64-musl" "4.57.1" + "@rollup/rollup-linux-riscv64-gnu" "4.57.1" + "@rollup/rollup-linux-riscv64-musl" "4.57.1" + "@rollup/rollup-linux-s390x-gnu" "4.57.1" + "@rollup/rollup-linux-x64-gnu" "4.57.1" + "@rollup/rollup-linux-x64-musl" "4.57.1" + "@rollup/rollup-openbsd-x64" "4.57.1" + "@rollup/rollup-openharmony-arm64" "4.57.1" + "@rollup/rollup-win32-arm64-msvc" "4.57.1" + "@rollup/rollup-win32-ia32-msvc" "4.57.1" + "@rollup/rollup-win32-x64-gnu" "4.57.1" + "@rollup/rollup-win32-x64-msvc" "4.57.1" fsevents "~2.3.2" rope-sequence@^1.3.0: @@ -6215,9 +6236,9 @@ semver@^6.3.1: integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== semver@^7.6.0, semver@^7.7.1: - version "7.7.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.2.tgz#67d99fdcd35cec21e6f8b87a7fd515a33f982b58" - integrity sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA== + version "7.7.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.4.tgz#28464e36060e991fa7a11d0279d2d3f3b57a7e8a" + integrity sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA== set-function-length@^1.2.2: version "1.2.2" @@ -6366,9 +6387,9 @@ spdx-expression-parse@^3.0.0: spdx-license-ids "^3.0.0" spdx-license-ids@^3.0.0: - version "3.0.21" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz#6d6e980c9df2b6fc905343a3b2d702a6239536c3" - integrity sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg== + version "3.0.22" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.22.tgz#abf5a08a6f5d7279559b669f47f0a43e8f3464ef" + integrity sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ== stable-hash@^0.0.5: version "0.0.5" @@ -6523,9 +6544,9 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1: ansi-regex "^5.0.1" strip-ansi@^7.0.1: - version "7.1.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" - integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== + version "7.1.2" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.2.tgz#132875abde678c7ea8d691533f2e7e22bb744dba" + integrity sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA== dependencies: ansi-regex "^6.0.1" @@ -6594,13 +6615,13 @@ through@^2.3.8: resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== -tinyglobby@^0.2.13, tinyglobby@^0.2.14: - version "0.2.14" - resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.14.tgz#5280b0cf3f972b050e74ae88406c0a6a58f4079d" - integrity sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ== +tinyglobby@^0.2.13: + version "0.2.15" + resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.15.tgz#e228dd1e638cea993d2fdb4fcd2d4602a79951c2" + integrity sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ== dependencies: - fdir "^6.4.4" - picomatch "^4.0.2" + fdir "^6.5.0" + picomatch "^4.0.3" tippy.js@^6.3.7: version "6.3.7" @@ -6610,9 +6631,9 @@ tippy.js@^6.3.7: "@popperjs/core" "^2.9.0" tldraw@^3.13.1: - version "3.14.0" - resolved "https://registry.yarnpkg.com/tldraw/-/tldraw-3.14.0.tgz#8a485716dc59498b86e5a299e31497b70e8a4f2f" - integrity sha512-NbMipLx7S8YdBaHTqox0DYWcQVZ0u9Wir5vETcHBuUShEaa/1yjBH/vps5RYgylCY6pTwDVAEQX8CNwcxDS2YQ== + version "3.15.6" + resolved "https://registry.yarnpkg.com/tldraw/-/tldraw-3.15.6.tgz#55d003f61d680cf9fb2134b4a51a99bd98a06a3f" + integrity sha512-DGFYaKexMrk7POFLyIUU5mzlOqBw3cR3xk2S25zauvhSPlQ64EAsR1fIMwXX4rJVmuV/4zg0jh6mqK0E0KfGHg== dependencies: "@tiptap/core" "^2.9.1" "@tiptap/extension-code" "^2.9.1" @@ -6621,8 +6642,8 @@ tldraw@^3.13.1: "@tiptap/pm" "^2.9.1" "@tiptap/react" "^2.9.1" "@tiptap/starter-kit" "^2.9.1" - "@tldraw/editor" "3.14.0" - "@tldraw/store" "3.14.0" + "@tldraw/editor" "3.15.6" + "@tldraw/store" "3.15.6" classnames "^2.5.1" hotkeys-js "^3.13.9" idb "^7.1.1" @@ -6630,9 +6651,9 @@ tldraw@^3.13.1: radix-ui "^1.3.4" tmp@^0.2.0: - version "0.2.3" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.3.tgz#eb783cc22bc1e8bebd0671476d46ea4eb32a79ae" - integrity sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w== + version "0.2.5" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.5.tgz#b06bcd23f0f3c8357b426891726d16015abfd8f8" + integrity sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow== to-regex-range@^5.0.1: version "5.0.1" @@ -6737,9 +6758,9 @@ typescript-eslint@^7.11.0: "@typescript-eslint/utils" "7.18.0" typescript@^5.4.5: - version "5.8.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.8.3.tgz#92f8a3e5e3cf497356f4178c34cd65a7f5e8440e" - integrity sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ== + version "5.9.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.9.3.tgz#5b4f59e15310ab17a216f5d6cf53ee476ede670f" + integrity sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw== uc.micro@^2.0.0, uc.micro@^2.1.0: version "2.1.0" @@ -6765,31 +6786,31 @@ unload@2.2.0: detect-node "^2.0.4" unrs-resolver@^1.6.2: - version "1.11.0" - resolved "https://registry.yarnpkg.com/unrs-resolver/-/unrs-resolver-1.11.0.tgz#6c14cf04f44fc7117c84f9341115e180b99a44bb" - integrity sha512-uw3hCGO/RdAEAb4zgJ3C/v6KIAFFOtBoxR86b2Ejc5TnH7HrhTWJR2o0A9ullC3eWMegKQCw/arQ/JivywQzkg== + version "1.11.1" + resolved "https://registry.yarnpkg.com/unrs-resolver/-/unrs-resolver-1.11.1.tgz#be9cd8686c99ef53ecb96df2a473c64d304048a9" + integrity sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg== dependencies: napi-postinstall "^0.3.0" optionalDependencies: - "@unrs/resolver-binding-android-arm-eabi" "1.11.0" - "@unrs/resolver-binding-android-arm64" "1.11.0" - "@unrs/resolver-binding-darwin-arm64" "1.11.0" - "@unrs/resolver-binding-darwin-x64" "1.11.0" - "@unrs/resolver-binding-freebsd-x64" "1.11.0" - "@unrs/resolver-binding-linux-arm-gnueabihf" "1.11.0" - "@unrs/resolver-binding-linux-arm-musleabihf" "1.11.0" - "@unrs/resolver-binding-linux-arm64-gnu" "1.11.0" - "@unrs/resolver-binding-linux-arm64-musl" "1.11.0" - "@unrs/resolver-binding-linux-ppc64-gnu" "1.11.0" - "@unrs/resolver-binding-linux-riscv64-gnu" "1.11.0" - "@unrs/resolver-binding-linux-riscv64-musl" "1.11.0" - "@unrs/resolver-binding-linux-s390x-gnu" "1.11.0" - "@unrs/resolver-binding-linux-x64-gnu" "1.11.0" - "@unrs/resolver-binding-linux-x64-musl" "1.11.0" - "@unrs/resolver-binding-wasm32-wasi" "1.11.0" - "@unrs/resolver-binding-win32-arm64-msvc" "1.11.0" - "@unrs/resolver-binding-win32-ia32-msvc" "1.11.0" - "@unrs/resolver-binding-win32-x64-msvc" "1.11.0" + "@unrs/resolver-binding-android-arm-eabi" "1.11.1" + "@unrs/resolver-binding-android-arm64" "1.11.1" + "@unrs/resolver-binding-darwin-arm64" "1.11.1" + "@unrs/resolver-binding-darwin-x64" "1.11.1" + "@unrs/resolver-binding-freebsd-x64" "1.11.1" + "@unrs/resolver-binding-linux-arm-gnueabihf" "1.11.1" + "@unrs/resolver-binding-linux-arm-musleabihf" "1.11.1" + "@unrs/resolver-binding-linux-arm64-gnu" "1.11.1" + "@unrs/resolver-binding-linux-arm64-musl" "1.11.1" + "@unrs/resolver-binding-linux-ppc64-gnu" "1.11.1" + "@unrs/resolver-binding-linux-riscv64-gnu" "1.11.1" + "@unrs/resolver-binding-linux-riscv64-musl" "1.11.1" + "@unrs/resolver-binding-linux-s390x-gnu" "1.11.1" + "@unrs/resolver-binding-linux-x64-gnu" "1.11.1" + "@unrs/resolver-binding-linux-x64-musl" "1.11.1" + "@unrs/resolver-binding-wasm32-wasi" "1.11.1" + "@unrs/resolver-binding-win32-arm64-msvc" "1.11.1" + "@unrs/resolver-binding-win32-ia32-msvc" "1.11.1" + "@unrs/resolver-binding-win32-x64-msvc" "1.11.1" unzipper@^0.10.11: version "0.10.14" @@ -6807,10 +6828,10 @@ unzipper@^0.10.11: readable-stream "~2.3.6" setimmediate "~1.0.4" -update-browserslist-db@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz#348377dd245216f9e7060ff50b15a1b740b75420" - integrity sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw== +update-browserslist-db@^1.2.0: + version "1.2.3" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz#64d76db58713136acbeb4c49114366cc6cc2e80d" + integrity sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w== dependencies: escalade "^3.2.0" picocolors "^1.1.1" @@ -6837,14 +6858,9 @@ use-sidecar@^1.1.3: detect-node-es "^1.1.0" tslib "^2.0.0" -use-sync-external-store@^1, use-sync-external-store@^1.0.0, use-sync-external-store@^1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz#55122e2a3edd2a6c106174c27485e0fd59bcfca0" - integrity sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A== - -use-sync-external-store@^1.2.2: +use-sync-external-store@^1, use-sync-external-store@^1.0.0, use-sync-external-store@^1.2.2, use-sync-external-store@^1.5.0: version "1.6.0" - resolved "https://registry.npmmirror.com/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz#b174bfa65cb2b526732d9f2ac0a408027876f32d" + resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz#b174bfa65cb2b526732d9f2ac0a408027876f32d" integrity sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w== util-deprecate@^1.0.1, util-deprecate@~1.0.1: @@ -6865,17 +6881,14 @@ validate-npm-package-license@^3.0.1: spdx-correct "^3.0.0" spdx-expression-parse "^3.0.0" -vite@^7.0.0: - version "7.0.3" - resolved "https://registry.yarnpkg.com/vite/-/vite-7.0.3.tgz#3bf15e1a6d054960406a70fa1052043938b39c5a" - integrity sha512-y2L5oJZF7bj4c0jgGYgBNSdIu+5HF+m68rn2cQXFbGoShdhV1phX9rbnxy9YXj82aS8MMsCLAAFkRxZeWdldrQ== - dependencies: - esbuild "^0.25.0" - fdir "^6.4.6" - picomatch "^4.0.2" - postcss "^8.5.6" - rollup "^4.40.0" - tinyglobby "^0.2.14" +vite@^5.4.11: + version "5.4.21" + resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.21.tgz#84a4f7c5d860b071676d39ba513c0d598fdc7027" + integrity sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw== + dependencies: + esbuild "^0.21.3" + postcss "^8.4.43" + rollup "^4.20.0" optionalDependencies: fsevents "~2.3.3" @@ -6925,9 +6938,9 @@ which-collection@^1.0.2: is-weakset "^2.0.3" which-typed-array@^1.1.16, which-typed-array@^1.1.19: - version "1.1.19" - resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.19.tgz#df03842e870b6b88e117524a4b364b6fc689f956" - integrity sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw== + version "1.1.20" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.20.tgz#3fdb7adfafe0ea69157b1509f3a1cd892bd1d122" + integrity sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg== dependencies: available-typed-arrays "^1.0.7" call-bind "^1.0.8" @@ -7022,7 +7035,7 @@ zod@^3.14.4: zustand@^4.4.1: version "4.5.7" - resolved "https://registry.npmmirror.com/zustand/-/zustand-4.5.7.tgz#7d6bb2026a142415dd8be8891d7870e6dbe65f55" + resolved "https://registry.yarnpkg.com/zustand/-/zustand-4.5.7.tgz#7d6bb2026a142415dd8be8891d7870e6dbe65f55" integrity sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw== dependencies: use-sync-external-store "^1.2.2" From 06f1588bb9ad9ecb3ba8621f1f46d833b4a66ceb Mon Sep 17 00:00:00 2001 From: Harry Date: Wed, 18 Feb 2026 03:00:23 +0000 Subject: [PATCH 16/36] functioning front end --- src/types/Graph/Graph.component.tsx | 42 +++++------- src/types/Graph/index.tsx | 101 +++++++++++++++++++++++++--- 2 files changed, 109 insertions(+), 34 deletions(-) diff --git a/src/types/Graph/Graph.component.tsx b/src/types/Graph/Graph.component.tsx index ea49978..e47f49e 100644 --- a/src/types/Graph/Graph.component.tsx +++ b/src/types/Graph/Graph.component.tsx @@ -165,7 +165,7 @@ export const GraphEditor: React.FC = ({ }) cyRef.current = cy - console.log('Cytoscape initialized') + return () => { cy.destroy() @@ -173,13 +173,25 @@ export const GraphEditor: React.FC = ({ } }, []) + /* -------------------- Update Node Counter -------------------- */ + useEffect(() => { + // Find the highest node number to avoid duplicate IDs + let maxNum = 0 + graph.nodes.forEach(node => { + const match = node.id.match(/^n(\d+)$/) + if (match && match[1]) { + const num = parseInt(match[1], 10) + if (num > maxNum) maxNum = num + } + }) + setNodeCounter(maxNum + 1) + }, [graph.nodes]) + /* -------------------- Update Cytoscape from Graph -------------------- */ useEffect(() => { const cy = cyRef.current if (!cy) return - console.log('Updating cytoscape from graph', graph) - // Get existing elements const existingNodeIds = new Set(cy.nodes().map(n => n.id())) const existingEdgeIds = new Set(cy.edges().map(e => e.id())) @@ -304,7 +316,7 @@ export const GraphEditor: React.FC = ({ // Create a new Paper.js project for this instance const paperProject = new paper.Project(canvas) paperProjectRef.current = paperProject - console.log('Paper.js project created') + // Setup canvas size const updateCanvasSize = () => { @@ -545,15 +557,12 @@ export const GraphEditor: React.FC = ({ return } - console.log('Node clicked:', node.id()) - console.log('Setting selectedNodeId to:', node.id()) setSelectedNodeId(node.id()) setSelectedEdgeId(null) } const tapEdge = (e: cytoscape.EventObject) => { const edge = e.target as EdgeSingular - console.log('Edge clicked:', edge.id()) setSelectedEdgeId(edge.id()) setSelectedNodeId(null) } @@ -561,7 +570,6 @@ export const GraphEditor: React.FC = ({ const tapBlank = (e: cytoscape.EventObject) => { // Only clear selection if we clicked the background (not a node or edge) if (e.target === cy) { - console.log('Blank clicked - clearing selection') setSelectedNodeId(null) setSelectedEdgeId(null) } @@ -583,11 +591,6 @@ export const GraphEditor: React.FC = ({ const selectedNode = selectedNodeId && cyRef.current ? cyRef.current.$id(selectedNodeId) : null const selectedEdge = selectedEdgeId && cyRef.current ? cyRef.current.$id(selectedEdgeId) : null - console.log('GraphEditor render - selectedNodeId:', selectedNodeId, 'selectedEdgeId:', selectedEdgeId) - console.log('selectedNode:', selectedNode?.id(), 'length:', selectedNode?.length, 'selectedEdge:', selectedEdge?.id(), 'length:', selectedEdge?.length) - console.log('Should show node panel:', !!(selectedNode && selectedNode.length > 0)) - console.log('Should show edge panel:', !!(selectedEdge && selectedEdge.length > 0)) - return (
{/* -------------------- Item Properties Panel -------------------- */} @@ -646,19 +649,6 @@ export const GraphEditor: React.FC = ({
)} - {/* Debug info */} -
- Debug: nodeId={selectedNodeId || 'none'}, edgeId={selectedEdgeId || 'none'}
- Node exists: {selectedNode ? 'yes' : 'no'}, Length: {selectedNode?.length || 0} -
- {selectedNode && selectedNode.length > 0 ? ( <>
{ this.config = { nodes: [], @@ -65,6 +73,11 @@ export class GraphResponseAreaTub extends ResponseAreaTub { } } + initWithConfig = () => { + // Called by the parent app when initialising with config only (student view) + // config is already extracted via extractConfig — nothing extra needed + } + // Override extractConfig to handle missing/invalid config gracefully protected extractConfig = (provided: any): void => { if (!provided || typeof provided !== 'object') { @@ -99,19 +112,69 @@ export class GraphResponseAreaTub extends ResponseAreaTub { this.config = parsedConfig.data } + // Override extractAnswer to properly store student's answer + protected extractAnswer = (provided: any): void => { + console.log('[Graph extractAnswer] Called with:', provided) + + if (!provided || typeof provided !== 'object') { + console.log('[Graph extractAnswer] No valid answer provided') + return + } + + const parsedAnswer = this.answerSchema.safeParse(provided) + if (!parsedAnswer.success) { + console.log('[Graph extractAnswer] Failed to parse answer:', parsedAnswer.error) + return + } + + console.log('[Graph extractAnswer] Successfully extracted answer:', parsedAnswer.data) + this.answer = parsedAnswer.data + } + /* -------------------- Custom Check -------------------- */ - customCheck(): void { - // Block submission if preview validation fails - if (this.previewFeedback) { - throw new Error('preview failed') + customCheck = (): boolean => { + console.log('[Graph customCheck] Called') + console.log('[Graph customCheck] this.answer:', this.answer) + + // Validate the student's answer before submission + if (!this.answer || this.answer.nodes.length === 0) { + console.log('[Graph customCheck] No answer or empty graph - returning false') + return false // No answer to submit } - // Preview passed — ensure it's cleared - this.previewFeedback = null + // Decompress the graph (convert stringified nodes/edges back to objects) + const decompressedGraph: Graph = { + nodes: this.answer.nodes.map((e) => JSON.parse(e)), + edges: this.answer.edges.map((e) => JSON.parse(e)), + directed: this.answer.directed, + weighted: this.answer.weighted, + multigraph: this.answer.multigraph, + name: this.answer.name || '', + metadata: this.answer.metadata || {}, + } + + console.log('[Graph customCheck] Decompressed graph:', decompressedGraph) + + // Validate the decompressed graph + const feedback = validateGraph(decompressedGraph) + + console.log('[Graph customCheck] Validation feedback:', feedback) + + // Check if there are any errors + const hasErrors = feedback.errors.filter(e => e.type === 'error').length > 0 + + console.log('[Graph customCheck] Has errors:', hasErrors) + console.log('[Graph customCheck] Returning:', !hasErrors) + + // Return true if valid, false if there are errors + return !hasErrors } /* -------------------- Input -------------------- */ InputComponent = (props: BaseResponseAreaProps) => { + console.log('[Graph InputComponent] Rendering with props.answer:', props.answer) + console.log('[Graph InputComponent] Current this.answer:', this.answer) + // In teacher preview mode, edit the initial config // In student mode, start with config and save to props.answer const isTeacherPreview = props.isTeacherMode && props.hasPreview @@ -122,12 +185,21 @@ export class GraphResponseAreaTub extends ResponseAreaTub { // If props.answer exists, use it (parent component's state) const parsed = this.answerSchema.safeParse(props.answer) if (parsed.success) { + // Update this.answer so customCheck can access it + console.log('[Graph InputComponent] Updating this.answer with parsed data:', parsed.data) + this.answer = parsed.data return parsed.data + } else { + console.log('[Graph InputComponent] Failed to parse props.answer:', parsed.error) } + } else { + console.log('[Graph InputComponent] No props.answer provided') } // Fallback to config (initial state) or answer (for teacher answer panel) - return isTeacherPreview ? this.config : (this.config ?? this.answer) + const fallback = isTeacherPreview ? this.config : (this.config ?? this.answer) + console.log('[Graph InputComponent] Using fallback:', fallback) + return fallback })() /* ---------- Extract submitted feedback ---------- */ @@ -165,6 +237,8 @@ export class GraphResponseAreaTub extends ResponseAreaTub { feedback={effectiveFeedback} phase={this.phase} onChange={(val: Graph) => { + console.log('[Graph onChange] Called with graph:', val) + const compressed: CompressedGraph = { nodes: val.nodes.map((n) => JSON.stringify(n)), edges: val.edges.map((e) => JSON.stringify(e)), @@ -175,11 +249,20 @@ export class GraphResponseAreaTub extends ResponseAreaTub { metadata: val.metadata, } + console.log('[Graph onChange] Compressed:', compressed) + if (isTeacherPreview) { // Teacher is editing the initial config in preview section + console.log('[Graph onChange] Updating this.config') this.config = compressed + } else { + // Student is working - save their current answer for validation + console.log('[Graph onChange] Updating this.answer') + this.answer = compressed } + console.log('[Graph onChange] After update, this.answer:', this.answer) + // Validate the graph const preview = validateGraph(val) @@ -191,7 +274,9 @@ export class GraphResponseAreaTub extends ResponseAreaTub { this.phase = CheckPhase.Idle } + console.log('[Graph onChange] Calling props.handleChange with compressed data') props.handleChange(compressed) + console.log('[Graph onChange] props.handleChange called') }} /> ) From 1c4f98551c63097270575c5a445e774cde75a447 Mon Sep 17 00:00:00 2001 From: Harry Date: Thu, 26 Feb 2026 18:34:16 +0000 Subject: [PATCH 17/36] Refactor code structure for improved readability and maintainability, and corrected data logic for sending response and answer to backend --- .gitignore | 1 + .../Graph/components/GraphFeedbackPanel.tsx | 14 +- src/types/Graph/index.tsx | 130 ++---- src/types/Graph/type.ts | 61 ++- src/types/Graph/utils.ts | 108 ++++- src/types/Graph/validateGraph.ts | 2 +- yarn.lock | 406 +++++++++--------- 7 files changed, 397 insertions(+), 325 deletions(-) diff --git a/.gitignore b/.gitignore index 1b68e4d..ec94b55 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ tsconfig.tsbuildinfo # env files (copy .env.example) .env +FSA \ No newline at end of file diff --git a/src/types/Graph/components/GraphFeedbackPanel.tsx b/src/types/Graph/components/GraphFeedbackPanel.tsx index 8d89634..4225728 100644 --- a/src/types/Graph/components/GraphFeedbackPanel.tsx +++ b/src/types/Graph/components/GraphFeedbackPanel.tsx @@ -25,9 +25,7 @@ export function GraphFeedbackPanel({ if (!feedback || !parsed.success) { return (
- {phase === CheckPhase.PreviewError - ? 'Preview errors found' - : 'No feedback yet'} + No feedback yet
) } @@ -48,19 +46,13 @@ export function GraphFeedbackPanel({ fontWeight: 600, }} > - { - phase === CheckPhase.PreviewError ? "Errors in Preview" : "Errors in Evaluation" - } + Validation Results
{/* ================= Errors ================= */} {errors.length > 0 && ( diff --git a/src/types/Graph/index.tsx b/src/types/Graph/index.tsx index 72116c3..51b3ef0 100644 --- a/src/types/Graph/index.tsx +++ b/src/types/Graph/index.tsx @@ -5,45 +5,36 @@ import { import { ResponseAreaTub } from '../response-area-tub' import { GraphEditor } from './Graph.component' -import { Graph, CompressedGraph, CompressedGraphSchema, GraphFeedback, CheckPhase } from './type' -import { Edge, Node } from './type' +import { Graph, SimpleGraph, SimpleGraphSchema, GraphFeedback, CheckPhase, toSimpleGraph, fromSimpleGraph } from './type' import { validateGraph } from './validateGraph' export class GraphResponseAreaTub extends ResponseAreaTub { public readonly responseType = 'HANDDRAWNGRAPH' public readonly displayWideInput = true - protected answerSchema = CompressedGraphSchema - protected configSchema = CompressedGraphSchema + protected answerSchema = SimpleGraphSchema + protected configSchema = SimpleGraphSchema // Correct answer for grading (teacher sets in "answer" panel) - protected answer: CompressedGraph = { + protected answer: SimpleGraph = { nodes: [], edges: [], directed: false, weighted: false, multigraph: false, - name:'', - metadata: {}, } // Initial state shown to students (teacher sets in "preview" panel) - protected config: CompressedGraph = { + protected config: SimpleGraph = { nodes: [], edges: [], directed: false, weighted: false, multigraph: false, - name:'', - metadata: {}, } - private previewFeedback: GraphFeedback | null = null - private phase: CheckPhase = CheckPhase.Idle - // Temporarily test with delegateFeedback = true public readonly delegateFeedback = true - public readonly delegateLivePreview = true constructor() { super() @@ -59,8 +50,6 @@ export class GraphResponseAreaTub extends ResponseAreaTub { directed: false, weighted: false, multigraph: false, - name:'', - metadata: {}, } this.answer = { nodes: [], @@ -68,8 +57,6 @@ export class GraphResponseAreaTub extends ResponseAreaTub { directed: false, weighted: false, multigraph: false, - name:'', - metadata: {}, } } @@ -88,8 +75,6 @@ export class GraphResponseAreaTub extends ResponseAreaTub { directed: false, weighted: false, multigraph: false, - name:'', - metadata: {}, } return } @@ -103,8 +88,6 @@ export class GraphResponseAreaTub extends ResponseAreaTub { directed: false, weighted: false, multigraph: false, - name:'', - metadata: {}, } return } @@ -142,21 +125,12 @@ export class GraphResponseAreaTub extends ResponseAreaTub { return false // No answer to submit } - // Decompress the graph (convert stringified nodes/edges back to objects) - const decompressedGraph: Graph = { - nodes: this.answer.nodes.map((e) => JSON.parse(e)), - edges: this.answer.edges.map((e) => JSON.parse(e)), - directed: this.answer.directed, - weighted: this.answer.weighted, - multigraph: this.answer.multigraph, - name: this.answer.name || '', - metadata: this.answer.metadata || {}, - } - - console.log('[Graph customCheck] Decompressed graph:', decompressedGraph) + // Convert from SimpleGraph to Graph for validation + const graph = fromSimpleGraph(this.answer) + console.log('[Graph customCheck] Converted graph:', graph) - // Validate the decompressed graph - const feedback = validateGraph(decompressedGraph) + // Validate the graph + const feedback = validateGraph(graph) console.log('[Graph customCheck] Validation feedback:', feedback) @@ -180,14 +154,12 @@ export class GraphResponseAreaTub extends ResponseAreaTub { const isTeacherPreview = props.isTeacherMode && props.hasPreview // Determine the source of truth for the graph data - const initialGraph: CompressedGraph = (() => { + const initialGraph: SimpleGraph = (() => { if (props.answer) { // If props.answer exists, use it (parent component's state) const parsed = this.answerSchema.safeParse(props.answer) if (parsed.success) { - // Update this.answer so customCheck can access it - console.log('[Graph InputComponent] Updating this.answer with parsed data:', parsed.data) - this.answer = parsed.data + console.log('[Graph InputComponent] Using parsed props.answer:', parsed.data) return parsed.data } else { console.log('[Graph InputComponent] Failed to parse props.answer:', parsed.error) @@ -208,74 +180,47 @@ export class GraphResponseAreaTub extends ResponseAreaTub { if (!raw) return null try { - const jsonPart = raw.split('
')[1]?.trim() + const jsonPart = raw.split('
')[1]?.trim() if (!jsonPart) return null return JSON.parse(jsonPart) - } catch { + } catch (e) { + console.error('Failed to parse feedback JSON:', e) return null } })() /* ---------- Effective feedback ---------- */ - const effectiveFeedback = this.previewFeedback ?? submittedFeedback + const effectiveFeedback = submittedFeedback - // Decompress to Graph format for editor - const graph: Graph = { - nodes: initialGraph.nodes.map((e) => JSON.parse(e)), - edges: initialGraph.edges.map((e) => JSON.parse(e)), - directed: initialGraph.directed, - weighted: initialGraph.weighted, - multigraph: initialGraph.multigraph, - name: initialGraph.name, - metadata: initialGraph.metadata, - } + // Convert from SimpleGraph to Graph for editor + const graph: Graph = fromSimpleGraph(initialGraph) return ( { console.log('[Graph onChange] Called with graph:', val) - - const compressed: CompressedGraph = { - nodes: val.nodes.map((n) => JSON.stringify(n)), - edges: val.edges.map((e) => JSON.stringify(e)), - directed: val.directed, - weighted: val.weighted, - multigraph: val.multigraph, - name: val.name, - metadata: val.metadata, - } - console.log('[Graph onChange] Compressed:', compressed) + // Convert to SimpleGraph for backend + const simple = toSimpleGraph(val) if (isTeacherPreview) { // Teacher is editing the initial config in preview section console.log('[Graph onChange] Updating this.config') - this.config = compressed + this.config = simple } else { // Student is working - save their current answer for validation console.log('[Graph onChange] Updating this.answer') - this.answer = compressed + this.answer = simple } console.log('[Graph onChange] After update, this.answer:', this.answer) - // Validate the graph - const preview = validateGraph(val) - - if (preview.errors.filter((e) => e.type === 'error').length > 0) { - this.previewFeedback = preview - this.phase = CheckPhase.PreviewError - } else { - this.previewFeedback = null - this.phase = CheckPhase.Idle - } - - console.log('[Graph onChange] Calling props.handleChange with compressed data') - props.handleChange(compressed) + console.log('[Graph onChange] Calling props.handleChange with simple graph data') + props.handleChange(simple) console.log('[Graph onChange] props.handleChange called') }} /> @@ -286,15 +231,7 @@ export class GraphResponseAreaTub extends ResponseAreaTub { WizardComponent = (props: BaseResponseAreaWizardProps) => { // Wizard shows correct answer for grading // The separate "Response Area Preview" section handles the initial state (config) - const answerGraph: Graph = { - nodes: this.answer.nodes.map((e) => JSON.parse(e)), - edges: this.answer.edges.map((e) => JSON.parse(e)), - directed: this.answer.directed, - weighted: this.answer.weighted, - multigraph: this.answer.multigraph, - name: this.answer.name, - metadata: this.answer.metadata, - } + const answerGraph: Graph = fromSimpleGraph(this.answer) return ( { - const compressed: CompressedGraph = { - nodes: val.nodes.map((n: Node) => JSON.stringify(n)), - edges: val.edges.map((e: Edge) => JSON.stringify(e)), - directed: val.directed, - weighted: val.weighted, - multigraph: val.multigraph, - name: val.name, - metadata: val.metadata, - } - - this.answer = compressed + const simple = toSimpleGraph(val) + this.answer = simple props.handleChange({ responseType: this.responseType, config: this.config, - answer: compressed, + answer: simple, }) }} /> diff --git a/src/types/Graph/type.ts b/src/types/Graph/type.ts index da9579a..ed2af8f 100644 --- a/src/types/Graph/type.ts +++ b/src/types/Graph/type.ts @@ -42,12 +42,70 @@ export const GraphSchema = z.object({ directed: z.boolean().default(false), weighted: z.boolean().default(false), multigraph: z.boolean().default(false), - name: z.string().optional(), metadata: z.record(z.any()).optional().default({}), }); export type Graph = z.infer +// ----------------------------- +// Simplified Graph: for backend communication (like FSA) +// ----------------------------- +export const SimpleGraphSchema = z.object({ + // Nodes as pipe-delimited strings: "id|label|x|y" + nodes: z.array(z.string()), + // Edges as pipe-delimited strings: "source|target|weight|label" + edges: z.array(z.string()), + directed: z.boolean().default(false), + weighted: z.boolean().default(false), + multigraph: z.boolean().default(false), +}); + +export type SimpleGraph = z.infer + +// Helper functions to convert between Graph and SimpleGraph +export function toSimpleGraph(graph: Graph): SimpleGraph { + return { + nodes: graph.nodes.map(n => + `${n.id}|${n.label || ''}|${n.x || 0}|${n.y || 0}` + ), + edges: graph.edges.map(e => + `${e.source}|${e.target}|${e.weight || 1}|${e.label || ''}` + ), + directed: graph.directed, + weighted: graph.weighted, + multigraph: graph.multigraph, + }; +} + +export function fromSimpleGraph(simple: SimpleGraph): Graph { + return { + nodes: simple.nodes.map(str => { + const [id = '', label = '', xStr = '0', yStr = '0'] = str.split('|'); + return { + id, + label: label || undefined, + x: parseFloat(xStr) || 0, + y: parseFloat(yStr) || 0, + metadata: {}, + }; + }), + edges: simple.edges.map(str => { + const [source = '', target = '', weightStr = '1', label = ''] = str.split('|'); + return { + source, + target, + weight: parseFloat(weightStr) || 1, + label: label || undefined, + metadata: {}, + }; + }), + directed: simple.directed, + weighted: simple.weighted, + multigraph: simple.multigraph, + metadata: {}, + }; +} + // ----------------------------- // Compressed Graph: JSON-stringified nodes/edges // ----------------------------- @@ -79,7 +137,6 @@ export function compressGraph(graph: z.infer) { // ----------------------------- export enum CheckPhase { Idle = 'idle', - PreviewError = 'preview_error', Evaluated = 'evaluated', } diff --git a/src/types/Graph/utils.ts b/src/types/Graph/utils.ts index ac41a20..3b18715 100644 --- a/src/types/Graph/utils.ts +++ b/src/types/Graph/utils.ts @@ -1,13 +1,95 @@ -// import { CheckPhase, FSAFeedback } from "./type" - -// export function mergeFeedback( -// feedback: FSAFeedback | null, -// previewFeedback: FSAFeedback | null, -// phase: CheckPhase, -// ): FSAFeedback | null { -// if (phase === CheckPhase.PreviewError) { -// return previewFeedback -// } - -// return feedback -// } \ No newline at end of file +import { Graph, SimpleGraph } from './type' + +// BackendGraph type: what backend expects +export interface BackendGraph { + nodes: Array<{ + id: string; + label?: string; + position: { x: number; y: number }; + }>; + edges: Array<{ + source: string; + target: string; + weight?: number; + label?: string; + }>; + directed: boolean; + weighted: boolean; + multigraph: boolean; +} + +export class GraphConverter { + /** + * Convert from frontend Graph to flattened SimpleGraph + */ + static toSimple(graph: Graph): SimpleGraph { + return { + nodes: graph.nodes.map(n => `${n.id}|${n.label || ''}|${n.x || 0}|${n.y || 0}`), + edges: graph.edges.map(e => `${e.source}|${e.target}|${e.weight || 1}|${e.label || ''}`), + directed: graph.directed, + weighted: graph.weighted, + multigraph: graph.multigraph + } + } + + /** + * Convert from flattened SimpleGraph to backend format + */ + static toBackend(simple: SimpleGraph): BackendGraph { + return { + nodes: simple.nodes.map(nodeStr => { + const [id = '', label = '', x = '0', y = '0'] = nodeStr.split('|'); + return { + id, + label: label || undefined, + position: { x: parseFloat(x), y: parseFloat(y) } + } + }), + edges: simple.edges.map(edgeStr => { + const [source = '', target = '', weight = '1', label = ''] = edgeStr.split('|'); + return { + source, + target, + weight: parseFloat(weight), + label: label || undefined + } + }), + directed: simple.directed, + weighted: simple.weighted, + multigraph: simple.multigraph + } + } + + /** + * Convert from SimpleGraph back to frontend Graph + */ + static fromSimple(simple: SimpleGraph): Graph { + return { + nodes: simple.nodes.map(nodeStr => { + const [id = '', label = '', x = '0', y = '0'] = nodeStr.split('|'); + return { + id, + label: label || undefined, + x: parseFloat(x), + y: parseFloat(y), + metadata: {} + } + }), + edges: simple.edges.map((edgeStr, idx) => { + const [source = '', target = '', weight = '1', label = ''] = edgeStr.split('|'); + return { + id: `e-${source}-${target}-${idx}`, + source, + target, + weight: parseFloat(weight), + label: label || undefined, + metadata: {} + } + }), + directed: simple.directed, + weighted: simple.weighted, + multigraph: simple.multigraph, + metadata: {} + } + } +} diff --git a/src/types/Graph/validateGraph.ts b/src/types/Graph/validateGraph.ts index ee765f1..72395d4 100644 --- a/src/types/Graph/validateGraph.ts +++ b/src/types/Graph/validateGraph.ts @@ -99,6 +99,6 @@ export function validateGraph(graph: Graph): GraphFeedback { return { valid: errors.filter((e) => e.type === 'error').length === 0, errors, - phase: errors.length > 0 ? CheckPhase.PreviewError : CheckPhase.Idle, + phase: CheckPhase.Idle, } } diff --git a/yarn.lock b/yarn.lock index 1e001b9..a6ee217 100644 --- a/yarn.lock +++ b/yarn.lock @@ -488,10 +488,10 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@eslint/js@9.39.2", "@eslint/js@^9.3.0": - version "9.39.2" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.39.2.tgz#2d4b8ec4c3ea13c1b3748e0c97ecd766bdd80599" - integrity sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA== +"@eslint/js@9.39.3", "@eslint/js@^9.3.0": + version "9.39.3" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.39.3.tgz#c6168736c7e0c43ead49654ed06a4bcb3833363d" + integrity sha512-1B1VkCq6FuUNlQvlBYb+1jDu/gV297TIs/OeiaSR9l1H27SVW55ONE1e1Vp16NqP683+xEGzxYtv4XCiDPaQiw== "@eslint/object-schema@^2.1.7": version "2.1.7" @@ -1926,130 +1926,130 @@ resolved "https://registry.yarnpkg.com/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz#47d2bf4cef6d470b22f5831b420f8964e0bf755f" integrity sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA== -"@rollup/rollup-android-arm-eabi@4.57.1": - version "4.57.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.57.1.tgz#add5e608d4e7be55bc3ca3d962490b8b1890e088" - integrity sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg== - -"@rollup/rollup-android-arm64@4.57.1": - version "4.57.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.57.1.tgz#10bd0382b73592beee6e9800a69401a29da625c4" - integrity sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w== - -"@rollup/rollup-darwin-arm64@4.57.1": - version "4.57.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.57.1.tgz#1e99ab04c0b8c619dd7bbde725ba2b87b55bfd81" - integrity sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg== - -"@rollup/rollup-darwin-x64@4.57.1": - version "4.57.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.57.1.tgz#69e741aeb2839d2e8f0da2ce7a33d8bd23632423" - integrity sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w== - -"@rollup/rollup-freebsd-arm64@4.57.1": - version "4.57.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.57.1.tgz#3736c232a999c7bef7131355d83ebdf9651a0839" - integrity sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug== - -"@rollup/rollup-freebsd-x64@4.57.1": - version "4.57.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.57.1.tgz#227dcb8f466684070169942bd3998901c9bfc065" - integrity sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q== - -"@rollup/rollup-linux-arm-gnueabihf@4.57.1": - version "4.57.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.57.1.tgz#ba004b30df31b724f99ce66e7128248bea17cb0c" - integrity sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw== - -"@rollup/rollup-linux-arm-musleabihf@4.57.1": - version "4.57.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.57.1.tgz#6929f3e07be6b6da5991f63c6b68b3e473d0a65a" - integrity sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw== - -"@rollup/rollup-linux-arm64-gnu@4.57.1": - version "4.57.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.57.1.tgz#06e89fd4a25d21fe5575d60b6f913c0e65297bfa" - integrity sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g== - -"@rollup/rollup-linux-arm64-musl@4.57.1": - version "4.57.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.57.1.tgz#fddabf395b90990d5194038e6cd8c00156ed8ac0" - integrity sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q== - -"@rollup/rollup-linux-loong64-gnu@4.57.1": - version "4.57.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.57.1.tgz#04c10bb764bbf09a3c1bd90432e92f58d6603c36" - integrity sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA== - -"@rollup/rollup-linux-loong64-musl@4.57.1": - version "4.57.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.57.1.tgz#f2450361790de80581d8687ea19142d8a4de5c0f" - integrity sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw== - -"@rollup/rollup-linux-ppc64-gnu@4.57.1": - version "4.57.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.57.1.tgz#0474f4667259e407eee1a6d38e29041b708f6a30" - integrity sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w== - -"@rollup/rollup-linux-ppc64-musl@4.57.1": - version "4.57.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.57.1.tgz#9f32074819eeb1ddbe51f50ea9dcd61a6745ec33" - integrity sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw== - -"@rollup/rollup-linux-riscv64-gnu@4.57.1": - version "4.57.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.57.1.tgz#3fdb9d4b1e29fb6b6a6da9f15654d42eb77b99b2" - integrity sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A== - -"@rollup/rollup-linux-riscv64-musl@4.57.1": - version "4.57.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.57.1.tgz#1de780d64e6be0e3e8762035c22e0d8ea68df8ed" - integrity sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw== - -"@rollup/rollup-linux-s390x-gnu@4.57.1": - version "4.57.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.57.1.tgz#1da022ffd2d9e9f0fd8344ea49e113001fbcac64" - integrity sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg== - -"@rollup/rollup-linux-x64-gnu@4.57.1": - version "4.57.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.57.1.tgz#78c16eef9520bd10e1ea7a112593bb58e2842622" - integrity sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg== - -"@rollup/rollup-linux-x64-musl@4.57.1": - version "4.57.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.57.1.tgz#a7598591b4d9af96cb3167b50a5bf1e02dfea06c" - integrity sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw== - -"@rollup/rollup-openbsd-x64@4.57.1": - version "4.57.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.57.1.tgz#c51d48c07cd6c466560e5bed934aec688ce02614" - integrity sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw== - -"@rollup/rollup-openharmony-arm64@4.57.1": - version "4.57.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.57.1.tgz#f09921d0b2a0b60afbf3586d2a7a7f208ba6df17" - integrity sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ== - -"@rollup/rollup-win32-arm64-msvc@4.57.1": - version "4.57.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.57.1.tgz#08d491717135376e4a99529821c94ecd433d5b36" - integrity sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ== - -"@rollup/rollup-win32-ia32-msvc@4.57.1": - version "4.57.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.57.1.tgz#b0c12aac1104a8b8f26a5e0098e5facbb3e3964a" - integrity sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew== - -"@rollup/rollup-win32-x64-gnu@4.57.1": - version "4.57.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.57.1.tgz#b9cccef26f5e6fdc013bf3c0911a3c77428509d0" - integrity sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ== - -"@rollup/rollup-win32-x64-msvc@4.57.1": - version "4.57.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.57.1.tgz#a03348e7b559c792b6277cc58874b89ef46e1e72" - integrity sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA== +"@rollup/rollup-android-arm-eabi@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz#a6742c74c7d9d6d604ef8a48f99326b4ecda3d82" + integrity sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg== + +"@rollup/rollup-android-arm64@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz#97247be098de4df0c11971089fd2edf80a5da8cf" + integrity sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q== + +"@rollup/rollup-darwin-arm64@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz#674852cf14cf11b8056e0b1a2f4e872b523576cf" + integrity sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg== + +"@rollup/rollup-darwin-x64@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz#36dfd7ed0aaf4d9d89d9ef983af72632455b0246" + integrity sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w== + +"@rollup/rollup-freebsd-arm64@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz#2f87c2074b4220260fdb52a9996246edfc633c22" + integrity sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA== + +"@rollup/rollup-freebsd-x64@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz#9b5a26522a38a95dc06616d1939d4d9a76937803" + integrity sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg== + +"@rollup/rollup-linux-arm-gnueabihf@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz#86aa4859385a8734235b5e40a48e52d770758c3a" + integrity sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw== + +"@rollup/rollup-linux-arm-musleabihf@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz#cbe70e56e6ece8dac83eb773b624fc9e5a460976" + integrity sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA== + +"@rollup/rollup-linux-arm64-gnu@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz#d14992a2e653bc3263d284bc6579b7a2890e1c45" + integrity sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA== + +"@rollup/rollup-linux-arm64-musl@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz#2fdd1ddc434ea90aeaa0851d2044789b4d07f6da" + integrity sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA== + +"@rollup/rollup-linux-loong64-gnu@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz#8a181e6f89f969f21666a743cd411416c80099e7" + integrity sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg== + +"@rollup/rollup-linux-loong64-musl@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz#904125af2babc395f8061daa27b5af1f4e3f2f78" + integrity sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q== + +"@rollup/rollup-linux-ppc64-gnu@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz#a57970ac6864c9a3447411a658224bdcf948be22" + integrity sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA== + +"@rollup/rollup-linux-ppc64-musl@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz#bb84de5b26870567a4267666e08891e80bb56a63" + integrity sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA== + +"@rollup/rollup-linux-riscv64-gnu@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz#72d00d2c7fb375ce3564e759db33f17a35bffab9" + integrity sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg== + +"@rollup/rollup-linux-riscv64-musl@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz#4c166ef58e718f9245bd31873384ba15a5c1a883" + integrity sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg== + +"@rollup/rollup-linux-s390x-gnu@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz#bb5025cde9a61db478c2ca7215808ad3bce73a09" + integrity sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w== + +"@rollup/rollup-linux-x64-gnu@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz#9b66b1f9cd95c6624c788f021c756269ffed1552" + integrity sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg== + +"@rollup/rollup-linux-x64-musl@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz#b007ca255dc7166017d57d7d2451963f0bd23fd9" + integrity sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg== + +"@rollup/rollup-openbsd-x64@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz#e8b357b2d1aa2c8d76a98f5f0d889eabe93f4ef9" + integrity sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ== + +"@rollup/rollup-openharmony-arm64@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz#96c2e3f4aacd3d921981329831ff8dde492204dc" + integrity sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA== + +"@rollup/rollup-win32-arm64-msvc@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz#2d865149d706d938df8b4b8f117e69a77646d581" + integrity sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A== + +"@rollup/rollup-win32-ia32-msvc@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz#abe1593be0fa92325e9971c8da429c5e05b92c36" + integrity sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA== + +"@rollup/rollup-win32-x64-gnu@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz#c4af3e9518c9a5cd4b1c163dc81d0ad4d82e7eab" + integrity sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA== + +"@rollup/rollup-win32-x64-msvc@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz#4584a8a87b29188a4c1fe987a9fcf701e256d86c" + integrity sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA== "@rtsao/scc@^1.1.0": version "1.1.0" @@ -2959,14 +2959,14 @@ acorn-jsx@^5.3.2: integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== acorn@^8.15.0: - version "8.15.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.15.0.tgz#a360898bc415edaac46c8241f6383975b930b816" - integrity sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg== + version "8.16.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.16.0.tgz#4ce79c89be40afe7afe8f3adb902a1f1ce9ac08a" + integrity sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw== ajv@^6.12.4: - version "6.12.6" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" - integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + version "6.14.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.14.0.tgz#fd067713e228210636ebb08c60bd3765d6dbe73a" + integrity sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw== dependencies: fast-deep-equal "^3.1.1" fast-json-stable-stringify "^2.0.0" @@ -3186,15 +3186,20 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +balanced-match@^4.0.2: + version "4.0.4" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-4.0.4.tgz#bfb10662feed8196a2c62e7c68e17720c274179a" + integrity sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA== + base64-js@^1.3.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== baseline-browser-mapping@^2.9.0: - version "2.9.19" - resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz#3e508c43c46d961eb4d7d2e5b8d1dd0f9ee4f488" - integrity sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg== + version "2.10.0" + resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.10.0.tgz#5b09935025bf8a80e29130251e337c6a7fc8cbb9" + integrity sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA== big-integer@^1.6.16, big-integer@^1.6.17: version "1.6.52" @@ -3238,6 +3243,13 @@ brace-expansion@^2.0.1: dependencies: balanced-match "^1.0.0" +brace-expansion@^5.0.2: + version "5.0.3" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-5.0.3.tgz#6a9c6c268f85b53959ec527aeafe0f7300258eef" + integrity sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA== + dependencies: + balanced-match "^4.0.2" + braces@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" @@ -3337,9 +3349,9 @@ callsites@^3.0.0: integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== caniuse-lite@^1.0.30001579, caniuse-lite@^1.0.30001759: - version "1.0.30001770" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001770.tgz#4dc47d3b263a50fbb243448034921e0a88591a84" - integrity sha512-x/2CLQ1jHENRbHg5PSId2sXq1CIO1CISvwWAj027ltMVG2UNgW+w9oH2+HzgEIRFembL8bUlXtfbBHR1fCg2xw== + version "1.0.30001772" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001772.tgz#aa8a176eba0006e78c965a8215c7a1ceb030122d" + integrity sha512-mIwLZICj+ntVTw4BT2zfp+yu/AqV6GMKfJVJMx3MwPxs+uk/uj2GLl2dH8LQbjiLDX66amCga5nKFyDgRR43kg== chainsaw@~0.1.0: version "0.1.0" @@ -3807,9 +3819,9 @@ eastasianwidth@^0.2.0: integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== electron-to-chromium@^1.5.263: - version "1.5.286" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.286.tgz#142be1ab5e1cd5044954db0e5898f60a4960384e" - integrity sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A== + version "1.5.302" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.302.tgz#032a5802b31f7119269959c69fe2015d8dad5edb" + integrity sha512-sM6HAN2LyK82IyPBpznDRqlTQAtuSaO+ShzFiWTvoMJLHyZ+Y39r8VMfHzwbU8MVBzQ4Wdn85+wlZl2TLGIlwg== emoji-regex@^8.0.0: version "8.0.0" @@ -4116,9 +4128,9 @@ eslint-visitor-keys@^4.2.1: integrity sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ== eslint@^9.3.0: - version "9.39.2" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.39.2.tgz#cb60e6d16ab234c0f8369a3fe7cc87967faf4b6c" - integrity sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw== + version "9.39.3" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.39.3.tgz#08d63df1533d7743c0907b32a79a7e134e63ee2f" + integrity sha512-VmQ+sifHUbI/IcSopBCF/HO3YiHQx/AVd3UVyYL6weuwW+HvON9VYn5l6Zl1WZzPWXPNZrSQpxwkkZ/VuvJZzg== dependencies: "@eslint-community/eslint-utils" "^4.8.0" "@eslint-community/regexpp" "^4.12.1" @@ -4126,7 +4138,7 @@ eslint@^9.3.0: "@eslint/config-helpers" "^0.4.2" "@eslint/core" "^0.17.0" "@eslint/eslintrc" "^3.3.1" - "@eslint/js" "9.39.2" + "@eslint/js" "9.39.3" "@eslint/plugin-kit" "^0.4.1" "@humanfs/node" "^0.16.6" "@humanwhocodes/module-importer" "^1.0.1" @@ -4974,9 +4986,9 @@ jszip@^3.10.1: setimmediate "^1.0.5" katex@^0.16.0, katex@^0.16.2: - version "0.16.28" - resolved "https://registry.yarnpkg.com/katex/-/katex-0.16.28.tgz#64068425b5a29b41b136aae0d51cbb2c71d64c39" - integrity sha512-YHzO7721WbmAL6Ov1uzN/l5mY5WWWhJBSW+jq4tkfZfsxmo1hu6frS0EOswvjBUnWE6NtjEs48SFn5CQESRLZg== + version "0.16.32" + resolved "https://registry.yarnpkg.com/katex/-/katex-0.16.32.tgz#4f0cbfb68db20f2ea333173c35e8e45d729a65fa" + integrity sha512-ac0FzkRJlpw4WyH3Zu/OgU9LmPKqjHr6O2BxfSrBt8uJ1BhvH2YK3oJ4ut/K+O+6qQt2MGpdbn0MrffVEnnUDQ== dependencies: commander "^8.3.0" @@ -5221,25 +5233,25 @@ microseconds@0.2.0: integrity sha512-n7DHHMjR1avBbSpsTBj6fmMGh2AGrifVV4e+WYc3Q9lO+xnSZ3NyhcBND3vzzatt05LFhoKFRxrIyklmLlUtyA== minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" - integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + version "3.1.3" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.3.tgz#6a5cba9b31f503887018f579c89f81f61162e624" + integrity sha512-M2GCs7Vk83NxkUyQV1bkABc4yxgz9kILhHImZiBPAZ9ybuvCb0/H7lEl5XvIg3g+9d4eNotkZA5IWwYl0tibaA== dependencies: brace-expansion "^1.1.7" minimatch@^5.1.0: - version "5.1.6" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" - integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== + version "5.1.7" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.7.tgz#9bce540b26998f278d34784a3dd25d96f5054d6d" + integrity sha512-FjiwU9HaHW6YB3H4a1sFudnv93lvydNjz2lmyUXR6IwKhGI+bgL3SOZrBGn6kvvX2pJvhEkGSGjyTHN47O4rqA== dependencies: brace-expansion "^2.0.1" minimatch@^9.0.1, minimatch@^9.0.4: - version "9.0.5" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" - integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== + version "9.0.6" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.6.tgz#a7e3bccfcb3d78ec1bf8d51c9ba749080237a5c8" + integrity sha512-kQAVowdR33euIqeA0+VZTDqU+qo1IeVY+hrKYtZMio3Pg0P0vuh/kwRylLUddJhB6pf3q/botcOvRtx4IN1wqQ== dependencies: - brace-expansion "^2.0.1" + brace-expansion "^5.0.2" minimist@^1.2.0, minimist@^1.2.6, minimist@~1.2.5: version "1.2.8" @@ -5247,9 +5259,9 @@ minimist@^1.2.0, minimist@^1.2.6, minimist@~1.2.5: integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== "minipass@^5.0.0 || ^6.0.2 || ^7.0.0": - version "7.1.2" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" - integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== + version "7.1.3" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.3.tgz#79389b4eb1bb2d003a9bba87d492f2bd37bdc65b" + integrity sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A== "mkdirp@>=0.5 0": version "0.5.6" @@ -5897,9 +5909,9 @@ react-dom@^18.3.1: scheduler "^0.23.2" react-hook-form@^7.31.2: - version "7.71.1" - resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.71.1.tgz#6a758958861682cf0eb22131eead684ba3618f66" - integrity sha512-9SUJKCGKo8HUSsCO+y0CtqkqI5nNuaDqTxyqPsZPqIwudpj4rCrAz/jZV+jn57bx5gtZKOh3neQu94DXMc+w5w== + version "7.71.2" + resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.71.2.tgz#a5f1d2b855be9ecf1af6e74df9b80f54beae7e35" + integrity sha512-1CHvcDYzuRUNOflt4MOq3ZM46AronNJtQ1S7tnX6YN4y72qhgiUItpacZUAQ0TyWYci3yz1X+rXaSxiuEm86PA== react-is@^16.13.1, react-is@^16.7.0: version "16.13.1" @@ -6128,37 +6140,37 @@ robust-predicates@^3.0.2: integrity sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg== rollup@^4.20.0: - version "4.57.1" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.57.1.tgz#947f70baca32db2b9c594267fe9150aa316e5a88" - integrity sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A== + version "4.59.0" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.59.0.tgz#cf74edac17c1486f562d728a4d923a694abdf06f" + integrity sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg== dependencies: "@types/estree" "1.0.8" optionalDependencies: - "@rollup/rollup-android-arm-eabi" "4.57.1" - "@rollup/rollup-android-arm64" "4.57.1" - "@rollup/rollup-darwin-arm64" "4.57.1" - "@rollup/rollup-darwin-x64" "4.57.1" - "@rollup/rollup-freebsd-arm64" "4.57.1" - "@rollup/rollup-freebsd-x64" "4.57.1" - "@rollup/rollup-linux-arm-gnueabihf" "4.57.1" - "@rollup/rollup-linux-arm-musleabihf" "4.57.1" - "@rollup/rollup-linux-arm64-gnu" "4.57.1" - "@rollup/rollup-linux-arm64-musl" "4.57.1" - "@rollup/rollup-linux-loong64-gnu" "4.57.1" - "@rollup/rollup-linux-loong64-musl" "4.57.1" - "@rollup/rollup-linux-ppc64-gnu" "4.57.1" - "@rollup/rollup-linux-ppc64-musl" "4.57.1" - "@rollup/rollup-linux-riscv64-gnu" "4.57.1" - "@rollup/rollup-linux-riscv64-musl" "4.57.1" - "@rollup/rollup-linux-s390x-gnu" "4.57.1" - "@rollup/rollup-linux-x64-gnu" "4.57.1" - "@rollup/rollup-linux-x64-musl" "4.57.1" - "@rollup/rollup-openbsd-x64" "4.57.1" - "@rollup/rollup-openharmony-arm64" "4.57.1" - "@rollup/rollup-win32-arm64-msvc" "4.57.1" - "@rollup/rollup-win32-ia32-msvc" "4.57.1" - "@rollup/rollup-win32-x64-gnu" "4.57.1" - "@rollup/rollup-win32-x64-msvc" "4.57.1" + "@rollup/rollup-android-arm-eabi" "4.59.0" + "@rollup/rollup-android-arm64" "4.59.0" + "@rollup/rollup-darwin-arm64" "4.59.0" + "@rollup/rollup-darwin-x64" "4.59.0" + "@rollup/rollup-freebsd-arm64" "4.59.0" + "@rollup/rollup-freebsd-x64" "4.59.0" + "@rollup/rollup-linux-arm-gnueabihf" "4.59.0" + "@rollup/rollup-linux-arm-musleabihf" "4.59.0" + "@rollup/rollup-linux-arm64-gnu" "4.59.0" + "@rollup/rollup-linux-arm64-musl" "4.59.0" + "@rollup/rollup-linux-loong64-gnu" "4.59.0" + "@rollup/rollup-linux-loong64-musl" "4.59.0" + "@rollup/rollup-linux-ppc64-gnu" "4.59.0" + "@rollup/rollup-linux-ppc64-musl" "4.59.0" + "@rollup/rollup-linux-riscv64-gnu" "4.59.0" + "@rollup/rollup-linux-riscv64-musl" "4.59.0" + "@rollup/rollup-linux-s390x-gnu" "4.59.0" + "@rollup/rollup-linux-x64-gnu" "4.59.0" + "@rollup/rollup-linux-x64-musl" "4.59.0" + "@rollup/rollup-openbsd-x64" "4.59.0" + "@rollup/rollup-openharmony-arm64" "4.59.0" + "@rollup/rollup-win32-arm64-msvc" "4.59.0" + "@rollup/rollup-win32-ia32-msvc" "4.59.0" + "@rollup/rollup-win32-x64-gnu" "4.59.0" + "@rollup/rollup-win32-x64-msvc" "4.59.0" fsevents "~2.3.2" rope-sequence@^1.3.0: @@ -6387,9 +6399,9 @@ spdx-expression-parse@^3.0.0: spdx-license-ids "^3.0.0" spdx-license-ids@^3.0.0: - version "3.0.22" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.22.tgz#abf5a08a6f5d7279559b669f47f0a43e8f3464ef" - integrity sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ== + version "3.0.23" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.23.tgz#b069e687b1291a32f126893ed76a27a745ee2133" + integrity sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw== stable-hash@^0.0.5: version "0.0.5" From c60111a2c9d5f1c9583e48a614e0e7a79e7adf31 Mon Sep 17 00:00:00 2001 From: Harry Date: Sat, 28 Feb 2026 03:39:16 +0000 Subject: [PATCH 18/36] feat: added configuration panel to control evlauation type --- .gitignore | 3 +- package.json | 2 +- src/types/Graph/Graph.component.tsx | 12 +- src/types/Graph/components/ConfigPanel.tsx | 182 ++++++----- src/types/Graph/index.tsx | 357 ++++++++++++--------- src/types/Graph/type.ts | 53 ++- src/types/Graph/utils.ts | 3 +- 7 files changed, 374 insertions(+), 238 deletions(-) diff --git a/.gitignore b/.gitignore index ec94b55..8605a82 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,5 @@ tsconfig.tsbuildinfo # env files (copy .env.example) .env -FSA \ No newline at end of file +FSA +PropositionalLogic \ No newline at end of file diff --git a/package.json b/package.json index 1a326b3..15c07f4 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "type": "module", "scripts": { "build": "tsc -b && vite build", - "build:watch": "vite build --watch", + "build:watch": "node --max-old-space-size=4096 --no-opt ./node_modules/.bin/vite build --watch", "format": "prettier --check \"**/*.{js,ts,tsx}\"", "format:fix": "prettier --write \"**/*.{js,ts,tsx}\"", "lint": "eslint .", diff --git a/src/types/Graph/Graph.component.tsx b/src/types/Graph/Graph.component.tsx index e47f49e..e923f97 100644 --- a/src/types/Graph/Graph.component.tsx +++ b/src/types/Graph/Graph.component.tsx @@ -140,7 +140,7 @@ export const GraphEditor: React.FC = ({ style: { label: 'data(label)', 'curve-style': 'bezier', - 'target-arrow-shape': 'triangle', + 'target-arrow-shape': graph.directed ? 'triangle' : 'none', 'line-color': '#555', 'target-arrow-color': '#555', 'font-size': '12px' @@ -173,6 +173,16 @@ export const GraphEditor: React.FC = ({ } }, []) + /* -------------------- Update arrow style when directed flag changes -------------------- */ + useEffect(() => { + const cy = cyRef.current + if (!cy) return + cy.style() + .selector('edge') + .style({ 'target-arrow-shape': graph.directed ? 'triangle' : 'none' }) + .update() + }, [graph.directed]) + /* -------------------- Update Node Counter -------------------- */ useEffect(() => { // Find the highest node number to avoid duplicate IDs diff --git a/src/types/Graph/components/ConfigPanel.tsx b/src/types/Graph/components/ConfigPanel.tsx index 893d927..a717866 100644 --- a/src/types/Graph/components/ConfigPanel.tsx +++ b/src/types/Graph/components/ConfigPanel.tsx @@ -1,77 +1,115 @@ -// import React from 'react' +import React from 'react'; +import { GraphConfig } from '../type'; -// import { FSAConfig } from '../type' +const evaluationOptions = [ + 'isomorphism', + 'connectivity', + 'bipartite', + 'cycle_detection', + 'graph_coloring', + 'planarity', + 'tree', + 'forest', + 'dag', + 'eulerian', + 'semi_eulerian', + 'regular', + 'complete', + 'degree_sequence', + 'subgraph', + 'hamiltonian_path', + 'hamiltonian_cycle', + 'clique_number' +]; -// interface EvaluationConfigPanelProps { -// config: FSAConfig -// setConfig: (config: FSAConfig) => void -// configOpen: boolean -// setConfigOpen: React.Dispatch> -// classes: Record -// } +interface ConfigPanelProps { + config: GraphConfig; + onChange: (config: GraphConfig) => void; + AnswerPanel?: React.ReactNode; +} -// export default function EvaluationConfigPanel({ -// config, -// setConfig, -// configOpen, -// setConfigOpen, -// classes -// }: EvaluationConfigPanelProps) { -// return ( -//
-//
setConfigOpen((o) => !o)} -// > -// Evaluation Config -// {configOpen ? '▾' : '▸'} -//
+export const ConfigPanel: React.FC = ({ config, onChange, AnswerPanel }) => { + const [selectedType, setSelectedType] = React.useState(config.evaluation_type ?? '') + const [directed, setDirected] = React.useState(config.directed ?? false) -// {configOpen && ( -//
-// {Object.entries(config).map(([key, value]) => ( -//
-// + const updateConfig = (updates: Partial) => { + onChange({ ...config, ...updates }); + }; -// {typeof value === 'boolean' ? ( -// -// setConfig({ -// ...config, -// [key]: e.target.checked, -// }) -// } -// /> -// ) : typeof value === 'number' ? ( -// -// setConfig({ -// ...config, -// [key]: Number(e.target.value), -// }) -// } -// /> -// ) : ( -// -// setConfig({ -// ...config, -// [key]: e.target.value, -// }) -// } -// /> -// )} -//
-// ))} -//
-// )} -//
-// ) -// } + const handleTypeChange = (type: string) => { + setSelectedType(type); + updateConfig({ evaluation_type: type }); + }; + + const handleDirectedToggle = (val: boolean) => { + setDirected(val); + updateConfig({ directed: val }); + }; + + const radioStyle = (active: boolean): React.CSSProperties => ({ + display: 'flex', + alignItems: 'center', + padding: '10px 20px', + cursor: 'pointer', + border: active ? '2px solid #0057b8' : '1px solid #d9d9d9', + background: active ? '#cce6ff' : '#fff', + borderRadius: '8px', + fontWeight: active ? 700 : 400, + color: active ? '#0057b8' : '#333', + transition: 'all 0.2s', + }); + + return ( +
+ + {/* ---- Directed / Undirected toggle ---- */} +
+

Graph Type

+
+ {([false, true] as const).map(val => ( + + ))} +
+
+ + {/* ---- Evaluation type selector ---- */} +
+

Evaluation Type

+
+ {evaluationOptions.map(type => ( + + ))} +
+
+ + {selectedType === 'isomorphism' && AnswerPanel} + +
+ ); +}; diff --git a/src/types/Graph/index.tsx b/src/types/Graph/index.tsx index 51b3ef0..930c4a5 100644 --- a/src/types/Graph/index.tsx +++ b/src/types/Graph/index.tsx @@ -1,63 +1,58 @@ +import React, { useState, useEffect } from 'react' import { BaseResponseAreaProps, BaseResponseAreaWizardProps, } from '../base-props.type' import { ResponseAreaTub } from '../response-area-tub' +import { ConfigPanel } from './components/ConfigPanel' import { GraphEditor } from './Graph.component' -import { Graph, SimpleGraph, SimpleGraphSchema, GraphFeedback, CheckPhase, toSimpleGraph, fromSimpleGraph } from './type' +import { + Graph, + GraphConfig, + GraphConfigSchema, + GraphAnswer, + GraphAnswerSchema, + GraphFeedback, + CheckPhase, + toSimpleGraph, + fromSimpleGraph, + graphAnswerToSimple, + simpleToAnswer, +} from './type' import { validateGraph } from './validateGraph' +const DEFAULT_CONFIG: GraphConfig = { + directed: false, + weighted: false, + multigraph: false, + evaluation_type: '', +} + +const DEFAULT_ANSWER: GraphAnswer = { + nodes: [], + edges: [], +} + export class GraphResponseAreaTub extends ResponseAreaTub { public readonly responseType = 'HANDDRAWNGRAPH' public readonly displayWideInput = true - protected answerSchema = SimpleGraphSchema - protected configSchema = SimpleGraphSchema + protected answerSchema = GraphAnswerSchema + protected configSchema = GraphConfigSchema - // Correct answer for grading (teacher sets in "answer" panel) - protected answer: SimpleGraph = { - nodes: [], - edges: [], - directed: false, - weighted: false, - multigraph: false, - } + protected answer: GraphAnswer = { ...DEFAULT_ANSWER } + protected config: GraphConfig = { ...DEFAULT_CONFIG } - // Initial state shown to students (teacher sets in "preview" panel) - protected config: SimpleGraph = { - nodes: [], - edges: [], - directed: false, - weighted: false, - multigraph: false, - } - - // Temporarily test with delegateFeedback = true public readonly delegateFeedback = true constructor() { super() - console.log('[Graph Constructor] GraphResponseAreaTub instantiated') - console.log('[Graph Constructor] customCheck method:', this.customCheck) - console.log('[Graph Constructor] typeof customCheck:', typeof this.customCheck) } initWithDefault = () => { - this.config = { - nodes: [], - edges: [], - directed: false, - weighted: false, - multigraph: false, - } - this.answer = { - nodes: [], - edges: [], - directed: false, - weighted: false, - multigraph: false, - } + this.config = { ...DEFAULT_CONFIG } + this.answer = { ...DEFAULT_ANSWER } } initWithConfig = () => { @@ -68,26 +63,22 @@ export class GraphResponseAreaTub extends ResponseAreaTub { // Override extractConfig to handle missing/invalid config gracefully protected extractConfig = (provided: any): void => { if (!provided || typeof provided !== 'object') { - // No config provided - use empty graph as default - this.config = { - nodes: [], - edges: [], - directed: false, - weighted: false, - multigraph: false, - } + this.config = { directed: false, weighted: false, multigraph: false, evaluation_type: '' } return } const parsedConfig = this.configSchema?.safeParse(provided) if (!parsedConfig || !parsedConfig.success) { - // Invalid config - use empty graph as default + // Legacy migration: config was a SimpleGraph — extract just the flags + // evaluation_type may be a legacy string[] — take first element + const legacyEval = provided.evaluation_type this.config = { - nodes: [], - edges: [], - directed: false, - weighted: false, - multigraph: false, + directed: provided.directed ?? false, + weighted: provided.weighted ?? false, + multigraph: provided.multigraph ?? false, + evaluation_type: Array.isArray(legacyEval) + ? (legacyEval[0] ?? '') + : (legacyEval ?? ''), } return } @@ -95,133 +86,103 @@ export class GraphResponseAreaTub extends ResponseAreaTub { this.config = parsedConfig.data } - // Override extractAnswer to properly store student's answer + // Override extractAnswer — answer may be flattened (nodes + edges + config flags) protected extractAnswer = (provided: any): void => { - console.log('[Graph extractAnswer] Called with:', provided) - - if (!provided || typeof provided !== 'object') { - console.log('[Graph extractAnswer] No valid answer provided') - return - } + if (!provided || typeof provided !== 'object') return - const parsedAnswer = this.answerSchema.safeParse(provided) - if (!parsedAnswer.success) { - console.log('[Graph extractAnswer] Failed to parse answer:', parsedAnswer.error) - return + if (Array.isArray(provided.nodes) && Array.isArray(provided.edges)) { + this.answer = { nodes: provided.nodes, edges: provided.edges } + // Always read config flags from the flattened answer + const legacyEval = provided.evaluation_type + this.config = { + directed: provided.directed ?? false, + weighted: provided.weighted ?? false, + multigraph: provided.multigraph ?? false, + evaluation_type: Array.isArray(legacyEval) + ? (legacyEval[0] ?? '') + : (legacyEval ?? ''), + } } - - console.log('[Graph extractAnswer] Successfully extracted answer:', parsedAnswer.data) - this.answer = parsedAnswer.data } /* -------------------- Custom Check -------------------- */ customCheck = (): boolean => { - console.log('[Graph customCheck] Called') - console.log('[Graph customCheck] this.answer:', this.answer) - - // Validate the student's answer before submission - if (!this.answer || this.answer.nodes.length === 0) { - console.log('[Graph customCheck] No answer or empty graph - returning false') - return false // No answer to submit - } - - // Convert from SimpleGraph to Graph for validation - const graph = fromSimpleGraph(this.answer) - console.log('[Graph customCheck] Converted graph:', graph) - - // Validate the graph + if (!this.answer || this.answer.nodes.length === 0) return false + const graph = fromSimpleGraph(graphAnswerToSimple(this.answer, this.config)) const feedback = validateGraph(graph) - - console.log('[Graph customCheck] Validation feedback:', feedback) - - // Check if there are any errors - const hasErrors = feedback.errors.filter(e => e.type === 'error').length > 0 - - console.log('[Graph customCheck] Has errors:', hasErrors) - console.log('[Graph customCheck] Returning:', !hasErrors) - - // Return true if valid, false if there are errors - return !hasErrors + return feedback.errors.filter(e => e.type === 'error').length === 0 } /* -------------------- Input -------------------- */ InputComponent = (props: BaseResponseAreaProps) => { - console.log('[Graph InputComponent] Rendering with props.answer:', props.answer) - console.log('[Graph InputComponent] Current this.answer:', this.answer) - - // In teacher preview mode, edit the initial config - // In student mode, start with config and save to props.answer const isTeacherPreview = props.isTeacherMode && props.hasPreview - - // Determine the source of truth for the graph data - const initialGraph: SimpleGraph = (() => { + + // Resolve answer (topology only) — props.answer is the parent's managed state + const resolvedAnswer: GraphAnswer = (() => { if (props.answer) { - // If props.answer exists, use it (parent component's state) - const parsed = this.answerSchema.safeParse(props.answer) - if (parsed.success) { - console.log('[Graph InputComponent] Using parsed props.answer:', parsed.data) - return parsed.data - } else { - console.log('[Graph InputComponent] Failed to parse props.answer:', parsed.error) + const parsed = GraphAnswerSchema.safeParse(props.answer) + if (parsed.success) return parsed.data + // Legacy SimpleGraph in answer + const leg = props.answer as any + if (Array.isArray(leg.nodes) && Array.isArray(leg.edges)) + return { nodes: leg.nodes, edges: leg.edges } + } + return this.answer + })() + + // Resolve config — read from flattened answer first, then props.config, then tub state + const resolvedConfig: GraphConfig = (() => { + // Config flags may be flattened into the answer + const ans = props.answer as any + if (ans && (typeof ans.directed !== 'undefined' || typeof ans.evaluation_type !== 'undefined')) { + const eval_ = ans.evaluation_type + return { + directed: ans.directed ?? false, + weighted: ans.weighted ?? false, + multigraph: ans.multigraph ?? false, + evaluation_type: Array.isArray(eval_) ? (eval_[0] ?? '') : (eval_ ?? ''), } - } else { - console.log('[Graph InputComponent] No props.answer provided') } - - // Fallback to config (initial state) or answer (for teacher answer panel) - const fallback = isTeacherPreview ? this.config : (this.config ?? this.answer) - console.log('[Graph InputComponent] Using fallback:', fallback) - return fallback + if (props.config) { + const parsed = GraphConfigSchema.safeParse(props.config) + if (parsed.success) return parsed.data + const leg = props.config as any + return { + directed: leg.directed ?? false, + weighted: leg.weighted ?? false, + multigraph: leg.multigraph ?? false, + evaluation_type: leg.evaluation_type ?? '', + } + } + return this.config })() - /* ---------- Extract submitted feedback ---------- */ + // Parse submitted feedback const submittedFeedback: GraphFeedback | null = (() => { const raw = props.feedback?.feedback if (!raw) return null - try { const jsonPart = raw.split('
')[1]?.trim() if (!jsonPart) return null return JSON.parse(jsonPart) - } catch (e) { - console.error('Failed to parse feedback JSON:', e) + } catch { return null } })() - /* ---------- Effective feedback ---------- */ - const effectiveFeedback = submittedFeedback - - // Convert from SimpleGraph to Graph for editor - const graph: Graph = fromSimpleGraph(initialGraph) - + // Config is read-only here — set exclusively via WizardComponent. + // InputComponent only carries answer changes via props.handleChange. + const graph: Graph = fromSimpleGraph(graphAnswerToSimple(resolvedAnswer, resolvedConfig)) return ( { - console.log('[Graph onChange] Called with graph:', val) - - // Convert to SimpleGraph for backend - const simple = toSimpleGraph(val) - - if (isTeacherPreview) { - // Teacher is editing the initial config in preview section - console.log('[Graph onChange] Updating this.config') - this.config = simple - } else { - // Student is working - save their current answer for validation - console.log('[Graph onChange] Updating this.answer') - this.answer = simple - } - - console.log('[Graph onChange] After update, this.answer:', this.answer) - - console.log('[Graph onChange] Calling props.handleChange with simple graph data') - props.handleChange(simple) - console.log('[Graph onChange] props.handleChange called') + const newAnswer = simpleToAnswer(toSimpleGraph(val)) + this.answer = newAnswer + props.handleChange(newAnswer) }} /> ) @@ -229,27 +190,103 @@ export class GraphResponseAreaTub extends ResponseAreaTub { /* -------------------- Wizard -------------------- */ WizardComponent = (props: BaseResponseAreaWizardProps) => { - // Wizard shows correct answer for grading - // The separate "Response Area Preview" section handles the initial state (config) - const answerGraph: Graph = fromSimpleGraph(this.answer) - return ( - { - const simple = toSimpleGraph(val) - this.answer = simple - + { + this.config = config + this.answer = answer + // Flatten config fields into the answer — backend reads from answer, not config + const flatAnswer = { + ...answer, + directed: config.directed, + weighted: config.weighted, + multigraph: config.multigraph, + evaluation_type: config.evaluation_type, + } + console.log('[GraphTub] sending to backend:', flatAnswer) props.handleChange({ responseType: this.responseType, - config: this.config, - answer: simple, + answer: flatAnswer, }) }} /> ) } } + +/* ================================================================ + Stable sub-components — defined outside the class so React never + treats them as new component types on re-render, which would + unmount/remount GraphEditor and lose all Cytoscape canvas state. +================================================================ */ + +interface WizardPanelProps { + initialConfig: GraphConfig + initialAnswer: GraphAnswer + onChange: (config: GraphConfig, answer: GraphAnswer) => void +} + +const WizardPanel: React.FC = ({ + initialConfig, + initialAnswer, + onChange, +}) => { + const [config, setConfig] = useState(initialConfig) + const [answer, setAnswer] = useState(initialAnswer) + + // Emit initial state on mount so config is always persisted to DB, + // even if the teacher never interacts with the config panel. + useEffect(() => { + onChange(initialConfig, initialAnswer) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []) + + const handleConfigChange = (updatedConfig: GraphConfig) => { + setConfig(updatedConfig) + onChange(updatedConfig, answer) + } + + const handleAnswerChange = (val: Graph) => { + // evaluation_type is now a plain string; wrap for toSimpleGraph which expects string[] + const newAnswer = simpleToAnswer(toSimpleGraph(val, config.evaluation_type ? [config.evaluation_type] : [])) + setAnswer(newAnswer) + onChange(config, newAnswer) + } + + const isIsomorphism = config.evaluation_type?.includes('isomorphism') + const graph: Graph = fromSimpleGraph(graphAnswerToSimple(answer, config)) + + return ( +
+ + {isIsomorphism && ( +
+

Reference Graph (for Isomorphism)

+

Draw the graph the student's answer will be compared against.

+ +
+ )} +
+

Answer Graph

+ +
+
+ ) +} diff --git a/src/types/Graph/type.ts b/src/types/Graph/type.ts index ed2af8f..6ad1f12 100644 --- a/src/types/Graph/type.ts +++ b/src/types/Graph/type.ts @@ -48,7 +48,7 @@ export const GraphSchema = z.object({ export type Graph = z.infer // ----------------------------- -// Simplified Graph: for backend communication (like FSA) +// Simplified Graph: for internal editor use (topology + flags merged) // ----------------------------- export const SimpleGraphSchema = z.object({ // Nodes as pipe-delimited strings: "id|label|x|y" @@ -58,12 +58,38 @@ export const SimpleGraphSchema = z.object({ directed: z.boolean().default(false), weighted: z.boolean().default(false), multigraph: z.boolean().default(false), + evaluation_type: z.array(z.string()).default([]), }); export type SimpleGraph = z.infer +// ----------------------------- +// GraphConfig: teacher-configured params (stored in config, NOT in answer) +// directed, weighted, multigraph, evaluation_type live here +// ----------------------------- +export const GraphConfigSchema = z.object({ + directed: z.boolean().default(false), + weighted: z.boolean().default(false), + multigraph: z.boolean().default(false), + // Plain string — backend Pydantic EvaluationParams expects e.g. 'connectivity', not ['connectivity'] + evaluation_type: z.string().default(''), +}); + +export type GraphConfig = z.infer + +// ----------------------------- +// GraphAnswer: student/teacher answer — topology only (nodes + edges) +// flags (directed, weighted, etc.) come from GraphConfig, not stored here +// ----------------------------- +export const GraphAnswerSchema = z.object({ + nodes: z.array(z.string()), + edges: z.array(z.string()), +}); + +export type GraphAnswer = z.infer + // Helper functions to convert between Graph and SimpleGraph -export function toSimpleGraph(graph: Graph): SimpleGraph { +export function toSimpleGraph(graph: Graph, evaluationType?: string[]): SimpleGraph { return { nodes: graph.nodes.map(n => `${n.id}|${n.label || ''}|${n.x || 0}|${n.y || 0}` @@ -74,6 +100,28 @@ export function toSimpleGraph(graph: Graph): SimpleGraph { directed: graph.directed, weighted: graph.weighted, multigraph: graph.multigraph, + evaluation_type: evaluationType ?? [], + }; +} + +// Merge a GraphAnswer (topology) with a GraphConfig (flags) into a SimpleGraph for the editor +export function graphAnswerToSimple(answer: GraphAnswer, config: GraphConfig): SimpleGraph { + return { + nodes: answer.nodes, + edges: answer.edges, + directed: config.directed, + weighted: config.weighted, + multigraph: config.multigraph, + // Wrap the string into an array for internal SimpleGraph usage + evaluation_type: config.evaluation_type ? [config.evaluation_type] : [], + }; +} + +// Extract only topology from a SimpleGraph (strips config flags) +export function simpleToAnswer(simple: SimpleGraph): GraphAnswer { + return { + nodes: simple.nodes, + edges: simple.edges, }; } @@ -103,6 +151,7 @@ export function fromSimpleGraph(simple: SimpleGraph): Graph { weighted: simple.weighted, multigraph: simple.multigraph, metadata: {}, + // evaluation_type lives on SimpleGraph only, not on the rich Graph type }; } diff --git a/src/types/Graph/utils.ts b/src/types/Graph/utils.ts index 3b18715..ee4e7b5 100644 --- a/src/types/Graph/utils.ts +++ b/src/types/Graph/utils.ts @@ -28,7 +28,8 @@ export class GraphConverter { edges: graph.edges.map(e => `${e.source}|${e.target}|${e.weight || 1}|${e.label || ''}`), directed: graph.directed, weighted: graph.weighted, - multigraph: graph.multigraph + multigraph: graph.multigraph, + evaluation_type: [], } } From 01853f51ee6d1cd605c8dac4b87883e0fdd7c771 Mon Sep 17 00:00:00 2001 From: Harry Date: Sun, 1 Mar 2026 15:15:41 +0000 Subject: [PATCH 19/36] feat: removed redundant graph eidotr in answer response area --- src/types/Graph/index.tsx | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/types/Graph/index.tsx b/src/types/Graph/index.tsx index 930c4a5..1abfa6e 100644 --- a/src/types/Graph/index.tsx +++ b/src/types/Graph/index.tsx @@ -277,16 +277,6 @@ const WizardPanel: React.FC = ({ />
)} -
-

Answer Graph

- -
) } From 210e4c4330a5f45febb08170a6f7640b62cc3fbf Mon Sep 17 00:00:00 2001 From: Harry Date: Tue, 3 Mar 2026 23:43:00 +0000 Subject: [PATCH 20/36] feat: made markdown file for Graph and deleted FSA.md --- FSA.md | 113 ---------------------------------- Graph.md | 180 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 180 insertions(+), 113 deletions(-) delete mode 100644 FSA.md create mode 100644 Graph.md diff --git a/FSA.md b/FSA.md deleted file mode 100644 index 929f1eb..0000000 --- a/FSA.md +++ /dev/null @@ -1,113 +0,0 @@ -# FSA Module Documentation - -This module provides a visual editor for Finite State Automata (FSA) built with **React Flow**. It is designed to bridge the gap between a **Python-style backend** (deeply nested objects) and a **TypeScript/Zod frontend** (restricted to 2-level JSON nesting). - -## 1. The Core Data Structures - -### Frontend Schema (`FSA`) - -To satisfy the `jsonNestedSchema` (which permits only 2 levels of nesting), we use a "flattened" string format for transitions. - -```typescript -// level 1: Object properties -// level 2: String arrays -export interface FSA { - states: string[]; - alphabet: string[]; - transitions: string[]; // Format: "from_state|symbol|to_state" - initial_state: string; - accept_states: string[]; -} - -``` - -### Backend Schema (`BackendFSA`) - -The Python backend uses standard object-oriented nesting for transitions. - -```typescript -export interface BackendFSA { - states: string[]; - alphabet: string[]; - transitions: Array<{ - from_state: string; - to_state: string; - symbol: string; - }>; - initial_state: string; - accept_states: string[]; -} - -``` - ---- - -## 2. Key Components - -### `FSAInput.component.tsx` - -The primary visual editor. - -* **State Management**: Uses `useNodesState` and `useEdgesState` from React Flow. -* **Syncing**: Every change (adding a node, connecting an edge, deleting) triggers a `syncChanges` function that converts the visual graph back into the flattened `FSA` interface. -* **User Interactions**: -* **Add State**: Prompt-based creation of new nodes. -* **Connections**: Dragging from one node to another prompts for a transition symbol (defaults to `ε`). -* **Deletion**: Selecting a node/edge and pressing **Backspace** or **Delete** removes the element and cleans up orphaned transitions. - - - -### `FSAResponseAreaTub.ts` - -The controller class that integrates the editor into the application wizard. - -* **Resilience**: Uses `defaultFSA` to prevent `undefined` errors. -* **Validation**: Uses `fsaAnswerSchema.safeParse()` to guard against corrupted data. - ---- - -## 3. Transformation Logic (`FSAConverter`) - -Since the frontend and backend see the data differently, the `FSAConverter` utility is used at the network boundary. - -| Method | Source | Target | Reason | -| --- | --- | --- | --- | -| `toFrontend()` | `BackendFSA` | `FSA` | Unpacks objects into `"q0 | -| `toBackend()` | `FSA` | `BackendFSA` | Packs strings back into objects for the Python service logic. | - ---- - -## 4. Usage in the Pipeline - -1. **Load**: Data is fetched from the backend (`BackendFSA`). -2. **Convert**: `FSAConverter.toFrontend()` is called. -3. **Edit**: The user interacts with `FSAInput`. The `answer` state stays in the flattened `FSA` format. -4. [TODO] **Save**: On `onSubmit` or `onChange`, `FSAConverter.toBackend()` is called to transform the data back to the format the server expects. - ---- - -## 5. Important Implementation Notes - -* **Unique Identifiers**: Edge IDs in React Flow are generated as ``e-${from}-${symbol}-${to}``. If the automaton is Non-Deterministic (NFA), ensure symbol uniqueness or add a UUID to the ID string. -* **Visual Cues**: -* **Initial State**: Nodes matching `initial_state` are colored with a light teal background. -* **Accept States**: Nodes in `accept_states` are rendered with a double-border (4px double). - - -* **Alphabet Consistency**: The `alphabet` array is automatically derived from the unique labels present in the transitions during the sync process. - -## 6. Dev Notice: - -There is a temporary folder `/dev`, all the development stuff should be tested there - -run `yarn vite` or `yarn dev:fsa` to run - -also take notice in order for yarn to be configured correctly, there is a extra config - -```json -root: 'dev', // for dev only -``` - -in the vite.config.ts - -remember to remove it when we get to production \ No newline at end of file diff --git a/Graph.md b/Graph.md new file mode 100644 index 0000000..da01b45 --- /dev/null +++ b/Graph.md @@ -0,0 +1,180 @@ +# Graph Module Documentation + +This module provides a visual editor for Graphs built with **Cytoscape.js** and **Paper.js**. It bridges a **Python-style backend** (deeply nested objects) and a **TypeScript/Zod frontend** (restricted to 2-level JSON nesting via pipe-delimited strings). + +--- + +## 1. The Core Data Structures + +### Frontend Schema (`Graph`) + +The full graph type used internally by the editor. + +```typescript +export interface Graph { + nodes: Array<{ + id: string; + label?: string; + x?: number; + y?: number; + metadata: Record; + }>; + edges: Array<{ + source: string; + target: string; + weight?: number; // currently unused + label?: string; + id?: string; + metadata: Record; + }>; + directed: boolean; + weighted: boolean; // currently unused + multigraph: boolean; // currently unused + metadata: Record; +} +``` + +### Flattened Schema (`SimpleGraph`) + +To satisfy the `jsonNestedSchema` (2-level nesting limit, Shimmy communication issues), nodes and edges are serialised as pipe-delimited strings. + +```typescript +export interface SimpleGraph { + nodes: string[]; // Format: "id|label|x|y" + edges: string[]; // Format: "source|target|weight|label" + directed: boolean; + weighted: boolean; + multigraph: boolean; + evaluation_type: string[]; +} +``` + +### Config Schema (`GraphConfig`) + +Teacher-configured parameters stored separately in `config`, **not** in the answer. + +```typescript +export interface GraphConfig { + directed: boolean; + weighted: boolean; + multigraph: boolean; + evaluation_type: string; // e.g. 'connectivity', 'isomorphism', ... +} +``` + +### Answer Schema (`GraphAnswer`) + +Topology-only answer — config flags are kept in `GraphConfig`, not here. Currently only used if `evaluation_type` is `isomorphim`. + +```typescript +export interface GraphAnswer { + nodes: string[]; // pipe-delimited node strings + edges: string[]; // pipe-delimited edge strings +} +``` + +--- + +## 2. Key Components + +### `Graph.component.tsx` — `GraphEditor` + +The primary visual editor. + +- **Rendering**: Uses **Cytoscape.js** for graph rendering and interaction. +- **Draw Mode**: Uses **Paper.js** (overlaid canvas) for freehand drawing: + - **Draw a circle** → creates a new node at the circle's centre. + - **Draw a line between nodes** → creates a new edge between the two closest nodes. + - **Click two nodes** (while in draw mode) → creates an edge between them. +- **Selection**: Clicking a node or edge selects it, displaying its properties for editing in the side panel. +- **Sync**: Every mutation (add/delete/edit) calls `syncToGraph()`, which reads the Cytoscape state and fires `onChange(graph)`. + +### `components/ConfigPanel.tsx` + +Teacher-facing configuration panel. + +- Toggle **Directed / Undirected**. +- Select an **Evaluation Type** (e.g. `isomorphism`, `connectivity`, `tree`, ...). +- For `isomorphism`, a second `GraphEditor` is rendered as the reference graph. + +### `components/GraphFeedbackPanel.tsx` + +`validateGraph()` makes some basic checks of graph for validation, however, this should be done by the backend. Even then, preview should be used from the back end for simple feedback, but not sure what good preview feedback there should be for Graph. + +### `components/ItemPropertiesPanel.tsx` + +Side panel for editing selected nodes/edges: + +- Edit **Display Name** of a node. +- Edit **Edge Label**. +- **Delete** selected element. + +--- + +## 3. Transformation Logic + +Since the frontend editor and the backend see the data differently, conversion utilities are used at the network boundary. + +| Function | Source | Target | Location | +|---|---|---|---| +| `toSimpleGraph()` | `Graph` | `SimpleGraph` | `type.ts` | +| `fromSimpleGraph()` | `SimpleGraph` | `Graph` | `type.ts` | +| `graphAnswerToSimple()` | `GraphAnswer` + `GraphConfig` | `SimpleGraph` | `type.ts` | +| `simpleToAnswer()` | `SimpleGraph` | `GraphAnswer` | `type.ts` | +| `GraphConverter.toBackend()` | `SimpleGraph` | `BackendGraph` | `utils.ts` | + +--- + +## 4. Usage in the Pipeline + +1. **Load**: Data is fetched from the backend as a flattened answer + config object. +2. **Convert**: `fromSimpleGraph(graphAnswerToSimple(answer, config))` reconstructs the rich `Graph` for the editor. +3. **Edit**: The user interacts with `GraphEditor`. Internal state stays in the rich `Graph` format. +4. **Save**: On change, `simpleToAnswer(toSimpleGraph(graph))` flattens the topology back. Config flags are merged directly into the answer object before sending to the backend: + ```typescript + const flatAnswer = { + ...answer, + directed: config.directed, + weighted: config.weighted, + multigraph: config.multigraph, + evaluation_type: config.evaluation_type, + } + ``` + +--- + +## 5. Important Implementation Notes + +- **Node IDs**: Nodes are auto-generated as `n0`, `n1`, `n2`, ... The counter tracks the highest existing ID to avoid duplicates on reload. +- **Edge IDs**: Generated as `` `e-${source}-${target}-${Date.now()}` `` to guarantee uniqueness, including in multigraphs. +- **Config is flattened into answer**: The backend reads `directed`, `weighted`, `multigraph`, and `evaluation_type` from the answer object, not from a separate config field. This is handled in `GraphResponseAreaTub.WizardComponent`. +- **Cytoscape vs Paper.js layering**: Paper.js canvas sits on top of Cytoscape (`zIndex: 10`) with `pointerEvents: none` when not in draw mode, and `pointerEvents: auto` + `cursor: crosshair` when draw mode is active. +- **Arrow direction**: The Cytoscape edge style `target-arrow-shape` is reactively updated whenever `graph.directed` changes. +- **Isomorphism mode**: When `evaluation_type === 'isomorphism'`, the Wizard renders a second `GraphEditor` for the teacher to define the reference graph. + +--- + +## 6. Supported Evaluation Types + +``` +isomorphism, connectivity, bipartite, cycle_detection, +graph_coloring, planarity, tree, forest, dag, eulerian, +semi_eulerian, regular, complete, degree_sequence, +subgraph, hamiltonian_path, hamiltonian_cycle, clique_number +``` + +--- + +## 7. Dev Notice + +There is a temporary folder `/dev` — all development work should be tested there. + +Run `yarn vite` or `yarn dev` to start. + +Note: for dev mode only, there is an extra config in `vite.config.ts`: + +```json +root: 'dev', // for dev only +``` + +Remember to remove it before going to production. \ No newline at end of file From 684c86dfb9bdc4e0e6f79b5cd7ccac212f8a6de4 Mon Sep 17 00:00:00 2001 From: Harry Date: Tue, 3 Mar 2026 23:47:10 +0000 Subject: [PATCH 21/36] fix: removed unnecessary gitignore items --- .gitignore | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 8605a82..2fc9aa1 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,4 @@ yarn-error.log* tsconfig.tsbuildinfo # env files (copy .env.example) -.env -FSA -PropositionalLogic \ No newline at end of file +.env \ No newline at end of file From 0406b541a1156df5a624cdf0d1d8cad6ee1f05ab Mon Sep 17 00:00:00 2001 From: Harry Date: Tue, 3 Mar 2026 23:49:56 +0000 Subject: [PATCH 22/36] fix: removed mention and FSA in README.md and replaced with Graph references --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index aab700e..754832c 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,9 @@ Develop local TypeScript code to create custom response area types, and see them previewed live in the main application. When they're ready, provide your code to the Lambda Feedback team, who will consider including it in the main application after (human) review. -## FSA Response Section +## Graph Response Area -please refer to [FSA.md](./FSA.md) +Please refer to [Graph.md](./Graph.md) ## Overview From af2e31d3d320c43133f0f4f4c5ec6d3528dbb1ff Mon Sep 17 00:00:00 2001 From: Harry Date: Tue, 3 Mar 2026 23:56:34 +0000 Subject: [PATCH 23/36] fix: removed commented out code --- src/types/index.ts | 59 ++++++---------------------------------------- 1 file changed, 7 insertions(+), 52 deletions(-) diff --git a/src/types/index.ts b/src/types/index.ts index b562ae2..e16bc6a 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -10,7 +10,6 @@ import { MultipleChoiceResponseAreaTub } from './MultipleChoice' import { NumberResponseAreaTub } from './NumberInput' import { NumericUnitsResponseAreaTub } from './NumericUnits' import { ResponseAreaTub } from './response-area-tub' -import { isResponseAreaSandboxType } from './sandbox' import { TableResponseAreaTub } from './Table' import { TextResponseAreaTub } from './TextInput' import { TrueFalseResponseAreaTub } from './TrueFalse' @@ -29,57 +28,13 @@ export const supportedResponseTypes = [ 'ESSAY', 'CODE', 'MILKDOWN', - 'HANDDRAWNGRAPH' + 'LIKERT', + 'MATH_SINGLE_LINE', + 'MATH_MULTI_LINES', + 'IMAGES', ] -if (typeof window !== 'undefined') { - const TUBIFY_NAME = localStorage.getItem('tubify-name') - const TUBIFY_URL = localStorage.getItem('tubify-url') - if (TUBIFY_NAME && TUBIFY_URL) { - console.debug('ENABLING TUBIFY', { - name: TUBIFY_NAME, - url: TUBIFY_URL, - }) - const loadComponent = async () => { - const response = await fetch(`${JSON.parse(TUBIFY_URL)}/tubify.iife.js`) - const componentCode = await response.text() - - const script = document.createElement('script') - script.textContent = componentCode - document.head.appendChild(script) - } - loadComponent().then(() => { - supportedResponseTypes.push(TUBIFY_NAME) - }) - } -} - -class VoidResponseAreaTub extends ResponseAreaTub { - // public readonly responseType = 'VOID' - public readonly responseType = 'SANDBOX' - - protected answerSchema = z.any() - - toResponse = (): IModularResponseSchema => { - return { - responseType: '', - answer: null, - } - } - - InputComponent = () => { - return null - } - - WizardComponent = () => { - return null - } -} - const createReponseAreaTub = (type: string): ResponseAreaTub => { - if (isResponseAreaSandboxType(type) && 'SandboxComponent' in window) { - return new (window.SandboxComponent as new () => ResponseAreaTub)() - } switch (type) { case 'BOOLEAN': @@ -100,8 +55,6 @@ const createReponseAreaTub = (type: string): ResponseAreaTub => { return new EssayResponseAreaTub() case 'CODE': return new CodeResponseAreaTub() - case 'HANDDRAWNGRAPH': - return new GraphResponseAreaTub() case 'LIKERT': return new LikertResponseAreaTub() case 'MATH_SINGLE_LINE': @@ -110,10 +63,12 @@ const createReponseAreaTub = (type: string): ResponseAreaTub => { return new MathMultiLinesResponseAreaTub() case 'IMAGES': return new ImagesResponseAreaTub() + case 'HANDDRAWNGRAPH': + return new GraphResponseAreaTub() case 'VOID': return new VoidResponseAreaTub() default: - console.error('Unknown ResponseAreaTub', { type }) + console.error('Unknown response area Tub type: ' + type) return new VoidResponseAreaTub() } } From 3dc8d168818a7aeffe366304db7987fc4115b42c Mon Sep 17 00:00:00 2001 From: Harry Date: Wed, 4 Mar 2026 00:23:52 +0000 Subject: [PATCH 24/36] feat: removed validation on the front end, and removed preview panel (unused) --- src/types/Graph/Graph.component.tsx | 11 +-- src/types/Graph/index.tsx | 25 +------ src/types/Graph/validateGraph.ts | 104 ---------------------------- 3 files changed, 2 insertions(+), 138 deletions(-) delete mode 100644 src/types/Graph/validateGraph.ts diff --git a/src/types/Graph/Graph.component.tsx b/src/types/Graph/Graph.component.tsx index e923f97..bfe0812 100644 --- a/src/types/Graph/Graph.component.tsx +++ b/src/types/Graph/Graph.component.tsx @@ -3,8 +3,7 @@ import cytoscape, { Core, NodeSingular, EdgeSingular } from 'cytoscape' import paper from 'paper' import React, { useEffect, useRef, useState, useCallback } from 'react' -import { GraphFeedbackPanel } from './components/GraphFeedbackPanel' -import { Graph, Node, Edge, GraphFeedback, CheckPhase } from './type' +import { Graph, Node, Edge } from './type' /* ----------------------------- Local Styles ----------------------------- */ export const useLocalStyles = makeStyles()((theme) => ({ @@ -77,15 +76,11 @@ export const useLocalStyles = makeStyles()((theme) => ({ interface GraphEditorProps { graph: Graph onChange: (graph: Graph) => void - feedback?: GraphFeedback | null - phase?: CheckPhase } export const GraphEditor: React.FC = ({ graph, onChange, - feedback = null, - phase = CheckPhase.Idle }) => { const { classes } = useLocalStyles() @@ -742,10 +737,6 @@ export const GraphEditor: React.FC = ({ ) : null} - {/* -------------------- Validation Feedback Panel -------------------- */} -
- -
{/* -------------------- Cytoscape + Paper Canvas -------------------- */} diff --git a/src/types/Graph/index.tsx b/src/types/Graph/index.tsx index 1abfa6e..3b2b3e4 100644 --- a/src/types/Graph/index.tsx +++ b/src/types/Graph/index.tsx @@ -13,14 +13,11 @@ import { GraphConfigSchema, GraphAnswer, GraphAnswerSchema, - GraphFeedback, - CheckPhase, toSimpleGraph, fromSimpleGraph, graphAnswerToSimple, simpleToAnswer, } from './type' -import { validateGraph } from './validateGraph' const DEFAULT_CONFIG: GraphConfig = { directed: false, @@ -107,10 +104,7 @@ export class GraphResponseAreaTub extends ResponseAreaTub { /* -------------------- Custom Check -------------------- */ customCheck = (): boolean => { - if (!this.answer || this.answer.nodes.length === 0) return false - const graph = fromSimpleGraph(graphAnswerToSimple(this.answer, this.config)) - const feedback = validateGraph(graph) - return feedback.errors.filter(e => e.type === 'error').length === 0 + return !!(this.answer && this.answer.nodes.length > 0) } /* -------------------- Input -------------------- */ @@ -157,19 +151,6 @@ export class GraphResponseAreaTub extends ResponseAreaTub { return this.config })() - // Parse submitted feedback - const submittedFeedback: GraphFeedback | null = (() => { - const raw = props.feedback?.feedback - if (!raw) return null - try { - const jsonPart = raw.split('
')[1]?.trim() - if (!jsonPart) return null - return JSON.parse(jsonPart) - } catch { - return null - } - })() - // Config is read-only here — set exclusively via WizardComponent. // InputComponent only carries answer changes via props.handleChange. const graph: Graph = fromSimpleGraph(graphAnswerToSimple(resolvedAnswer, resolvedConfig)) @@ -177,8 +158,6 @@ export class GraphResponseAreaTub extends ResponseAreaTub { { const newAnswer = simpleToAnswer(toSimpleGraph(val)) this.answer = newAnswer @@ -271,8 +250,6 @@ const WizardPanel: React.FC = ({
diff --git a/src/types/Graph/validateGraph.ts b/src/types/Graph/validateGraph.ts deleted file mode 100644 index 72395d4..0000000 --- a/src/types/Graph/validateGraph.ts +++ /dev/null @@ -1,104 +0,0 @@ -import { Graph } from './type' -import { GraphFeedback, ValidationError, CheckPhase } from './type' - -/** - * Validates a graph for common errors - */ -export function validateGraph(graph: Graph): GraphFeedback { - const errors: ValidationError[] = [] - - // Check if graph has nodes - if (graph.nodes.length === 0) { - errors.push({ - type: 'error', - message: 'Graph must have at least one node', - field: 'nodes', - }) - } - - // Check for duplicate node IDs - const nodeIds = new Set() - const duplicateIds = new Set() - - graph.nodes.forEach((node) => { - if (nodeIds.has(node.id)) { - duplicateIds.add(node.id) - } - nodeIds.add(node.id) - }) - - if (duplicateIds.size > 0) { - errors.push({ - type: 'error', - message: `Duplicate node IDs found: ${Array.from(duplicateIds).join(', ')}`, - field: 'nodes', - }) - } - - // Check for edges with invalid node references - graph.edges.forEach((edge, index) => { - if (!nodeIds.has(edge.source)) { - errors.push({ - type: 'error', - message: `Edge ${index + 1}: source node "${edge.source}" does not exist`, - field: 'edges', - }) - } - if (!nodeIds.has(edge.target)) { - errors.push({ - type: 'error', - message: `Edge ${index + 1}: target node "${edge.target}" does not exist`, - field: 'edges', - }) - } - }) - - // Check for self-loops (if not allowed) - const selfLoops = graph.edges.filter((edge) => edge.source === edge.target) - if (selfLoops.length > 0) { - errors.push({ - type: 'warning', - message: `Graph contains ${selfLoops.length} self-loop(s)`, - field: 'edges', - }) - } - - // Check if weighted graph has weights on all edges - if (graph.weighted) { - const edgesWithoutWeight = graph.edges.filter( - (edge) => edge.weight === undefined || edge.weight === null - ) - if (edgesWithoutWeight.length > 0) { - errors.push({ - type: 'warning', - message: `${edgesWithoutWeight.length} edge(s) are missing weights in a weighted graph`, - field: 'edges', - }) - } - } - - // Check for disconnected nodes (nodes with no edges) - const connectedNodes = new Set() - graph.edges.forEach((edge) => { - connectedNodes.add(edge.source) - connectedNodes.add(edge.target) - }) - - const disconnectedNodes = graph.nodes.filter( - (node) => !connectedNodes.has(node.id) - ) - - if (disconnectedNodes.length > 0 && graph.edges.length > 0) { - errors.push({ - type: 'warning', - message: `${disconnectedNodes.length} node(s) are not connected to the graph`, - field: 'nodes', - }) - } - - return { - valid: errors.filter((e) => e.type === 'error').length === 0, - errors, - phase: CheckPhase.Idle, - } -} From 8a520b45b1117aa690fcd310c251444c663ff1aa Mon Sep 17 00:00:00 2001 From: Harry Date: Wed, 4 Mar 2026 00:36:46 +0000 Subject: [PATCH 25/36] feat: removed utils.ts, duplicated algorithm --- src/types/Graph/utils.ts | 96 ---------------------------------------- 1 file changed, 96 deletions(-) delete mode 100644 src/types/Graph/utils.ts diff --git a/src/types/Graph/utils.ts b/src/types/Graph/utils.ts deleted file mode 100644 index ee4e7b5..0000000 --- a/src/types/Graph/utils.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { Graph, SimpleGraph } from './type' - -// BackendGraph type: what backend expects -export interface BackendGraph { - nodes: Array<{ - id: string; - label?: string; - position: { x: number; y: number }; - }>; - edges: Array<{ - source: string; - target: string; - weight?: number; - label?: string; - }>; - directed: boolean; - weighted: boolean; - multigraph: boolean; -} - -export class GraphConverter { - /** - * Convert from frontend Graph to flattened SimpleGraph - */ - static toSimple(graph: Graph): SimpleGraph { - return { - nodes: graph.nodes.map(n => `${n.id}|${n.label || ''}|${n.x || 0}|${n.y || 0}`), - edges: graph.edges.map(e => `${e.source}|${e.target}|${e.weight || 1}|${e.label || ''}`), - directed: graph.directed, - weighted: graph.weighted, - multigraph: graph.multigraph, - evaluation_type: [], - } - } - - /** - * Convert from flattened SimpleGraph to backend format - */ - static toBackend(simple: SimpleGraph): BackendGraph { - return { - nodes: simple.nodes.map(nodeStr => { - const [id = '', label = '', x = '0', y = '0'] = nodeStr.split('|'); - return { - id, - label: label || undefined, - position: { x: parseFloat(x), y: parseFloat(y) } - } - }), - edges: simple.edges.map(edgeStr => { - const [source = '', target = '', weight = '1', label = ''] = edgeStr.split('|'); - return { - source, - target, - weight: parseFloat(weight), - label: label || undefined - } - }), - directed: simple.directed, - weighted: simple.weighted, - multigraph: simple.multigraph - } - } - - /** - * Convert from SimpleGraph back to frontend Graph - */ - static fromSimple(simple: SimpleGraph): Graph { - return { - nodes: simple.nodes.map(nodeStr => { - const [id = '', label = '', x = '0', y = '0'] = nodeStr.split('|'); - return { - id, - label: label || undefined, - x: parseFloat(x), - y: parseFloat(y), - metadata: {} - } - }), - edges: simple.edges.map((edgeStr, idx) => { - const [source = '', target = '', weight = '1', label = ''] = edgeStr.split('|'); - return { - id: `e-${source}-${target}-${idx}`, - source, - target, - weight: parseFloat(weight), - label: label || undefined, - metadata: {} - } - }), - directed: simple.directed, - weighted: simple.weighted, - multigraph: simple.multigraph, - metadata: {} - } - } -} From 594188d7f99cf161ffbbb68cc45f8eaa69cc5e14 Mon Sep 17 00:00:00 2001 From: Harry Date: Wed, 4 Mar 2026 01:55:37 +0000 Subject: [PATCH 26/36] fix: student response area not influenced by reference answer graph editor --- src/types/Graph/index.tsx | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/src/types/Graph/index.tsx b/src/types/Graph/index.tsx index 3b2b3e4..f61f384 100644 --- a/src/types/Graph/index.tsx +++ b/src/types/Graph/index.tsx @@ -111,18 +111,8 @@ export class GraphResponseAreaTub extends ResponseAreaTub { InputComponent = (props: BaseResponseAreaProps) => { const isTeacherPreview = props.isTeacherMode && props.hasPreview - // Resolve answer (topology only) — props.answer is the parent's managed state - const resolvedAnswer: GraphAnswer = (() => { - if (props.answer) { - const parsed = GraphAnswerSchema.safeParse(props.answer) - if (parsed.success) return parsed.data - // Legacy SimpleGraph in answer - const leg = props.answer as any - if (Array.isArray(leg.nodes) && Array.isArray(leg.edges)) - return { nodes: leg.nodes, edges: leg.edges } - } - return this.answer - })() + // Student always starts with an empty graph — never pre-filled from the answer + const [studentAnswer, setStudentAnswer] = useState({ nodes: [], edges: [] }) // Resolve config — read from flattened answer first, then props.config, then tub state const resolvedConfig: GraphConfig = (() => { @@ -153,13 +143,14 @@ export class GraphResponseAreaTub extends ResponseAreaTub { // Config is read-only here — set exclusively via WizardComponent. // InputComponent only carries answer changes via props.handleChange. - const graph: Graph = fromSimpleGraph(graphAnswerToSimple(resolvedAnswer, resolvedConfig)) + const graph: Graph = fromSimpleGraph(graphAnswerToSimple(studentAnswer, resolvedConfig)) return ( { const newAnswer = simpleToAnswer(toSimpleGraph(val)) + setStudentAnswer(newAnswer) this.answer = newAnswer props.handleChange(newAnswer) }} From 85dd5a26f37294fc45842cb7de56c584c3147eff Mon Sep 17 00:00:00 2001 From: Harry Date: Thu, 5 Mar 2026 02:22:51 +0000 Subject: [PATCH 27/36] fix: moved Graph.component styles into its own file --- .../{styles.ts => Graph.component.styles.ts} | 11 +++ src/types/Graph/Graph.component.tsx | 69 +------------------ 2 files changed, 12 insertions(+), 68 deletions(-) rename src/types/Graph/{styles.ts => Graph.component.styles.ts} (91%) diff --git a/src/types/Graph/styles.ts b/src/types/Graph/Graph.component.styles.ts similarity index 91% rename from src/types/Graph/styles.ts rename to src/types/Graph/Graph.component.styles.ts index 8ed068e..2f70654 100644 --- a/src/types/Graph/styles.ts +++ b/src/types/Graph/Graph.component.styles.ts @@ -99,5 +99,16 @@ export const useLocalStyles = makeStyles()((theme) => ({ cyWrapper: { flexGrow: 1, + position: 'relative', + }, + + drawCanvas: { + position: 'absolute', + top: 0, + left: 0, + width: '100%', + height: '100%', + pointerEvents: 'none', + zIndex: 10, }, })) \ No newline at end of file diff --git a/src/types/Graph/Graph.component.tsx b/src/types/Graph/Graph.component.tsx index bfe0812..c87742b 100644 --- a/src/types/Graph/Graph.component.tsx +++ b/src/types/Graph/Graph.component.tsx @@ -1,76 +1,9 @@ -import { makeStyles } from '@styles' import cytoscape, { Core, NodeSingular, EdgeSingular } from 'cytoscape' import paper from 'paper' import React, { useEffect, useRef, useState, useCallback } from 'react' import { Graph, Node, Edge } from './type' - -/* ----------------------------- Local Styles ----------------------------- */ -export const useLocalStyles = makeStyles()((theme) => ({ - container: { - width: '100%', - height: 600, - display: 'flex', - border: '1px solid #ddd', - fontFamily: 'sans-serif', - position: 'relative' - }, - panel: { - width: 280, - padding: theme.spacing(2), - borderRight: '1px solid #ddd', - backgroundColor: '#fafafa', - display: 'flex', - flexDirection: 'column', - gap: theme.spacing(2), - overflowY: 'auto' - }, - panelTitle: { - fontWeight: 600, - fontSize: 16, - borderBottom: '1px solid #eee', - paddingBottom: theme.spacing(1) - }, - field: { - display: 'flex', - flexDirection: 'column', - gap: theme.spacing(0.5) - }, - inputField: { - padding: '6px 8px', - border: '1px solid #ccc', - borderRadius: 4 - }, - addButton: { - padding: '6px 10px', - backgroundColor: '#fff', - border: '1px solid #ccc', - borderRadius: 4, - cursor: 'pointer' - }, - deleteButton: { - padding: '6px', - backgroundColor: '#fff1f0', - color: '#cf1322', - border: '1px solid #ffa39e', - borderRadius: 4, - cursor: 'pointer', - fontWeight: 600 - }, - cyWrapper: { - flexGrow: 1, - position: 'relative' - }, - drawCanvas: { - position: 'absolute', - top: 0, - left: 0, - width: '100%', - height: '100%', - pointerEvents: 'none', - zIndex: 10 - } -})) +import { useLocalStyles } from './Graph.component.styles' /* ----------------------------- Graph Editor ----------------------------- */ interface GraphEditorProps { From c891e3693441eda7192cd4f5f71901f93b296e30 Mon Sep 17 00:00:00 2001 From: Harry Date: Thu, 5 Mar 2026 02:44:10 +0000 Subject: [PATCH 28/36] fix: moved styles from ConfigPanel out into its own files --- .../Graph/components/ConfigPanel.styles.ts | 63 +++++++ src/types/Graph/components/ConfigPanel.tsx | 38 ++-- .../Graph/components/GraphFeedbackPanel.tsx | 163 ------------------ 3 files changed, 75 insertions(+), 189 deletions(-) create mode 100644 src/types/Graph/components/ConfigPanel.styles.ts delete mode 100644 src/types/Graph/components/GraphFeedbackPanel.tsx diff --git a/src/types/Graph/components/ConfigPanel.styles.ts b/src/types/Graph/components/ConfigPanel.styles.ts new file mode 100644 index 0000000..2bbafa0 --- /dev/null +++ b/src/types/Graph/components/ConfigPanel.styles.ts @@ -0,0 +1,63 @@ +import { makeStyles } from '@styles' + +export const useConfigPanelStyles = makeStyles()(() => ({ + container: { + display: 'flex', + flexDirection: 'column', + gap: '20px', + maxWidth: 400, + }, + + sectionHeading: { + marginBottom: 8, + fontWeight: 600, + fontSize: 18, + }, + + radioGroupRow: { + display: 'flex', + gap: '12px', + }, + + radioGroupColumn: { + display: 'flex', + flexDirection: 'column', + gap: '8px', + }, + + radioLabel: { + display: 'flex', + alignItems: 'center', + padding: '10px 20px', + cursor: 'pointer', + border: '1px solid #d9d9d9', + background: '#fff', + borderRadius: '8px', + fontWeight: 400, + color: '#333', + transition: 'all 0.2s', + boxShadow: 'none', + }, + + radioLabelActive: { + border: '2px solid #0057b8', + background: '#cce6ff', + fontWeight: 700, + color: '#0057b8', + boxShadow: '0 0 8px #0057b833', + }, + + radioInput: { + accentColor: '#0057b8', + marginRight: 10, + }, + + radioInputWide: { + accentColor: '#0057b8', + marginRight: 16, + }, + + radioOptionText: { + fontSize: 15, + }, +})) diff --git a/src/types/Graph/components/ConfigPanel.tsx b/src/types/Graph/components/ConfigPanel.tsx index a717866..e5dd619 100644 --- a/src/types/Graph/components/ConfigPanel.tsx +++ b/src/types/Graph/components/ConfigPanel.tsx @@ -1,5 +1,6 @@ import React from 'react'; import { GraphConfig } from '../type'; +import { useConfigPanelStyles } from './ConfigPanel.styles'; const evaluationOptions = [ 'isomorphism', @@ -29,6 +30,7 @@ interface ConfigPanelProps { } export const ConfigPanel: React.FC = ({ config, onChange, AnswerPanel }) => { + const { classes, cx } = useConfigPanelStyles() const [selectedType, setSelectedType] = React.useState(config.evaluation_type ?? '') const [directed, setDirected] = React.useState(config.directed ?? false) @@ -46,35 +48,22 @@ export const ConfigPanel: React.FC = ({ config, onChange, Answ updateConfig({ directed: val }); }; - const radioStyle = (active: boolean): React.CSSProperties => ({ - display: 'flex', - alignItems: 'center', - padding: '10px 20px', - cursor: 'pointer', - border: active ? '2px solid #0057b8' : '1px solid #d9d9d9', - background: active ? '#cce6ff' : '#fff', - borderRadius: '8px', - fontWeight: active ? 700 : 400, - color: active ? '#0057b8' : '#333', - transition: 'all 0.2s', - }); - return ( -
+
{/* ---- Directed / Undirected toggle ---- */}
-

Graph Type

-
+

Graph Type

+
{([false, true] as const).map(val => ( -