From b29aa5f7a11111df1c1100533fbc26c427e5f1bf Mon Sep 17 00:00:00 2001 From: yanggggjie <60206592+yanggggjie@users.noreply.github.com> Date: Tue, 27 Feb 2024 21:50:32 +0800 Subject: [PATCH] fix: use react hooks to separate UI and logic (#100) * use hooks to separate-UI-and-logic * fix: use correct icon file * fix: refine style --- .../editor/editors/CustomFunctionEditor.tsx | 52 -- app/components/editor/editors/ModalEditor.tsx | 49 -- .../editor/editors/PolicyEditor.tsx | 49 -- .../editor/editors/RequestEditor.tsx | 49 -- .../editor/editors/RequestResultEditor.tsx | 52 -- .../editor/{copy.tsx => hooks/useCopy.tsx} | 34 +- app/components/editor/hooks/useIndex.tsx | 126 ++++ .../{run-test.tsx => hooks/useRunTest.tsx} | 21 +- .../useSetupEnforceContext.tsx} | 54 +- .../{share.tsx => hooks/useShareInfo.tsx} | 19 +- app/components/editor/index.tsx | 594 +++++++++++++----- app/components/editor/parts/ButtonGroup.tsx | 95 --- .../editor/parts/EnforcementResult.tsx | 30 - app/components/editor/parts/Modal.tsx | 53 -- app/components/editor/parts/Policy.tsx | 29 - app/components/editor/parts/Request.tsx | 43 -- app/components/editor/parts/Settings.tsx | 64 -- app/components/editor/select-model.tsx | 45 -- app/components/editor/syntax.tsx | 41 -- app/favicon.ico | Bin 4286 -> 0 bytes app/icon.png | Bin 0 -> 10527 bytes app/page.tsx | 9 +- 22 files changed, 616 insertions(+), 892 deletions(-) delete mode 100644 app/components/editor/editors/CustomFunctionEditor.tsx delete mode 100644 app/components/editor/editors/ModalEditor.tsx delete mode 100644 app/components/editor/editors/PolicyEditor.tsx delete mode 100644 app/components/editor/editors/RequestEditor.tsx delete mode 100644 app/components/editor/editors/RequestResultEditor.tsx rename app/components/editor/{copy.tsx => hooks/useCopy.tsx} (68%) create mode 100644 app/components/editor/hooks/useIndex.tsx rename app/components/editor/{run-test.tsx => hooks/useRunTest.tsx} (95%) rename app/components/editor/{setup-enforce-context.tsx => hooks/useSetupEnforceContext.tsx} (56%) rename app/components/editor/{share.tsx => hooks/useShareInfo.tsx} (89%) delete mode 100644 app/components/editor/parts/ButtonGroup.tsx delete mode 100644 app/components/editor/parts/EnforcementResult.tsx delete mode 100644 app/components/editor/parts/Modal.tsx delete mode 100644 app/components/editor/parts/Policy.tsx delete mode 100644 app/components/editor/parts/Request.tsx delete mode 100644 app/components/editor/parts/Settings.tsx delete mode 100755 app/components/editor/select-model.tsx delete mode 100755 app/components/editor/syntax.tsx delete mode 100644 app/favicon.ico create mode 100644 app/icon.png diff --git a/app/components/editor/editors/CustomFunctionEditor.tsx b/app/components/editor/editors/CustomFunctionEditor.tsx deleted file mode 100644 index 7a86758..0000000 --- a/app/components/editor/editors/CustomFunctionEditor.tsx +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2024 The casbin Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import CodeMirror from '@uiw/react-codemirror'; -import { monokai } from '@uiw/codemirror-theme-monokai'; -import { basicSetup } from 'codemirror'; -import { indentUnit, StreamLanguage } from '@codemirror/language'; -import { go } from '@codemirror/legacy-modes/mode/go'; -import { EditorView } from '@codemirror/view'; -import React, { CSSProperties } from 'react'; -export interface EditorProps { - text: string; - onChange: (text: string) => void; - style?: CSSProperties; -} -export const CustomFunctionEditor = (props: EditorProps) => { - return ( -
- { - props.onChange(value); - }} - theme={monokai} - basicSetup={{ - lineNumbers: true, - highlightActiveLine: true, - bracketMatching: true, - indentOnInput: true, - }} - extensions={[ - basicSetup, - StreamLanguage.define(go), - indentUnit.of(' '), - EditorView.lineWrapping, - ]} - className={'function'} - value={props.text} - /> -
- ); -}; diff --git a/app/components/editor/editors/ModalEditor.tsx b/app/components/editor/editors/ModalEditor.tsx deleted file mode 100644 index 27829e1..0000000 --- a/app/components/editor/editors/ModalEditor.tsx +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2024 The casbin Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { EditorProps } from '@/app/components/editor/editors/CustomFunctionEditor'; -import CodeMirror from '@uiw/react-codemirror'; -import { monokai } from '@uiw/codemirror-theme-monokai'; -import { basicSetup } from 'codemirror'; -import { CasbinConfSupport } from '@/app/components/editor/casbin-mode/casbin-conf'; -import { indentUnit } from '@codemirror/language'; -import { EditorView } from '@codemirror/view'; -import React from 'react'; - -export const ModelEditor = (props: EditorProps) => { - return ( -
- { - props.onChange(value); - }} - basicSetup={{ - lineNumbers: true, - highlightActiveLine: true, - bracketMatching: true, - indentOnInput: true, - }} - extensions={[ - basicSetup, - CasbinConfSupport(), - indentUnit.of(' '), - EditorView.lineWrapping, - ]} - className={'function'} - value={props.text} - /> -
- ); -}; diff --git a/app/components/editor/editors/PolicyEditor.tsx b/app/components/editor/editors/PolicyEditor.tsx deleted file mode 100644 index 721cbe1..0000000 --- a/app/components/editor/editors/PolicyEditor.tsx +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2024 The casbin Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { EditorProps } from '@/app/components/editor/editors/CustomFunctionEditor'; -import CodeMirror from '@uiw/react-codemirror'; -import { basicSetup } from 'codemirror'; -import { CasbinPolicySupport } from '@/app/components/editor/casbin-mode/casbin-csv'; -import { indentUnit } from '@codemirror/language'; -import { EditorView } from '@codemirror/view'; -import { monokai } from '@uiw/codemirror-theme-monokai'; -import React from 'react'; - -export const PolicyEditor = (props: EditorProps) => { - return ( -
- { - props.onChange(value); - }} - className={'function'} - value={props.text} - /> -
- ); -}; diff --git a/app/components/editor/editors/RequestEditor.tsx b/app/components/editor/editors/RequestEditor.tsx deleted file mode 100644 index 7d49362..0000000 --- a/app/components/editor/editors/RequestEditor.tsx +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2024 The casbin Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { EditorProps } from '@/app/components/editor/editors/CustomFunctionEditor'; -import CodeMirror from '@uiw/react-codemirror'; -import { monokai } from '@uiw/codemirror-theme-monokai'; -import { basicSetup } from 'codemirror'; -import { CasbinPolicySupport } from '@/app/components/editor/casbin-mode/casbin-csv'; -import { indentUnit } from '@codemirror/language'; -import { EditorView } from '@codemirror/view'; -import React from 'react'; - -export const RequestEditor = (props: EditorProps) => { - return ( -
- { - props.onChange(value); - }} - extensions={[ - basicSetup, - CasbinPolicySupport(), - indentUnit.of(' '), - EditorView.lineWrapping, - ]} - basicSetup={{ - lineNumbers: true, - highlightActiveLine: true, - bracketMatching: true, - indentOnInput: true, - }} - className={'function'} - value={props.text} - /> -
- ); -}; diff --git a/app/components/editor/editors/RequestResultEditor.tsx b/app/components/editor/editors/RequestResultEditor.tsx deleted file mode 100644 index d0bf095..0000000 --- a/app/components/editor/editors/RequestResultEditor.tsx +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2024 The casbin Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import React, { CSSProperties } from 'react'; -import CodeMirror from '@uiw/react-codemirror'; -import { monokai } from '@uiw/codemirror-theme-monokai'; -import { basicSetup } from 'codemirror'; -import { javascriptLanguage } from '@codemirror/lang-javascript'; -import { indentUnit } from '@codemirror/language'; -import { EditorView } from '@codemirror/view'; - -interface RequestResultEditorProps { - value: string; - style?: CSSProperties; -} - -export const RequestResultEditor = (props: RequestResultEditorProps) => { - return ( -
- { - return; - }} - theme={monokai} - extensions={[ - basicSetup, - javascriptLanguage, - indentUnit.of(' '), - EditorView.lineWrapping, - ]} - basicSetup={{ - lineNumbers: true, - highlightActiveLine: true, - bracketMatching: true, - indentOnInput: true, - }} - value={props.value} - /> -
- ); -}; diff --git a/app/components/editor/copy.tsx b/app/components/editor/hooks/useCopy.tsx similarity index 68% rename from app/components/editor/copy.tsx rename to app/components/editor/hooks/useCopy.tsx index 6ba154a..6870d43 100644 --- a/app/components/editor/copy.tsx +++ b/app/components/editor/hooks/useCopy.tsx @@ -12,15 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -import React from 'react'; - -interface CopyProps { - content: string; - cb: () => void; -} - -const Copy = (props: CopyProps) => { - function copy(data: string): void { +export default function useCopy() { + function copy(cb: any, data: string): void { const listener = (e: ClipboardEvent) => { if (!e.clipboardData) { throw new Error('Clipboard API unavailable.'); @@ -31,21 +24,10 @@ const Copy = (props: CopyProps) => { }; document.addEventListener('copy', listener); document.execCommand('copy'); - props.cb(); + // props.cb(); + cb(); } - - return ( - - ); -}; - -export default Copy; + return { + copy, + }; +} diff --git a/app/components/editor/hooks/useIndex.tsx b/app/components/editor/hooks/useIndex.tsx new file mode 100644 index 0000000..7998f32 --- /dev/null +++ b/app/components/editor/hooks/useIndex.tsx @@ -0,0 +1,126 @@ +import React, { isValidElement, ReactNode, useEffect, useState } from 'react'; +import { + defaultCustomConfig, + defaultEnforceContext, + example, + ModelKind, +} from '@/app/components/editor/casbin-mode/example'; +import { ShareFormat } from '@/app/components/editor/hooks/useShareInfo'; +import { defaultEnforceContextData } from '@/app/components/editor/hooks/useSetupEnforceContext'; + +export default function useIndex() { + const [modelKind, setModelKind] = useState('basic'); + const [modelText, setModelText] = useState(''); + const [policy, setPolicy] = useState(''); + const [request, setRequest] = useState(''); + const [echo, setEcho] = useState(<>); + const [requestResult, setRequestResult] = useState(''); + const [customConfig, setCustomConfig] = useState(''); + const [share, setShare] = useState(''); + const [enforceContextData, setEnforceContextData] = useState( + new Map(defaultEnforceContextData), + ); + + function setPolicyPersistent(text: string): void { + setPolicy(text); + } + + function setModelTextPersistent(text: string): void { + setModelText(text); + } + + function setCustomConfigPersistent(text: string): void { + setCustomConfig(text); + } + + function setRequestPersistent(text: string): void { + setRequest(text); + } + + function setEnforceContextDataPersistent(map: Map): void { + const text = JSON.stringify(Object.fromEntries(map)); + setEnforceContextData(new Map(map)); + } + + useEffect(() => { + const hash = window.location.hash.slice(1); + if (hash) { + setEcho(
Loading Shared Content...
); + fetch(`https://dpaste.com/${hash}.txt`) + .then((resp) => { + return resp.text(); + }) + .then((content) => { + const sharedContent = JSON.parse(content) as ShareFormat; + setPolicyPersistent(sharedContent.policy); + setModelTextPersistent(sharedContent.model); + setCustomConfigPersistent(sharedContent.customConfig); + setRequestPersistent(sharedContent.request); + setRequestPersistent(sharedContent.request); + if (sharedContent.enforceContext) { + setEnforceContextDataPersistent( + new Map(Object.entries(sharedContent.enforceContext)), + ); + } + setRequestResult(''); + window.location.hash = ''; // prevent duplicate load + setEcho(
Shared Content Loaded.
); + }) + .catch(() => { + setEcho(
Failed to load Shared Content.
); + }); + } + }, []); + + useEffect(() => { + setPolicy(example[modelKind].policy); + setModelText(example[modelKind].model); + setRequest(example[modelKind].request); + setCustomConfig(defaultCustomConfig); + setEnforceContextData( + new Map( + Object.entries( + JSON.parse( + example[modelKind].enforceContext || defaultEnforceContext, + ), + ), + ), + ); + }, [modelKind]); + + function handleShare(v: ReactNode | string) { + if (isValidElement(v)) { + setEcho(v); + } else { + const currentPath = window.location.origin + window.location.pathname; + setShare(v as string); + setEcho(
{`Shared at ${currentPath}#${v}`}
); + } + } + return { + modelKind, + setModelKind, + modelText, + setModelText, + policy, + setPolicy, + request, + setRequest, + echo, + setEcho, + requestResult, + setRequestResult, + customConfig, + setCustomConfig, + share, + setShare, + enforceContextData, + setEnforceContextData, + setPolicyPersistent, + setModelTextPersistent, + setCustomConfigPersistent, + setRequestPersistent, + setEnforceContextDataPersistent, + handleShare, + }; +} diff --git a/app/components/editor/run-test.tsx b/app/components/editor/hooks/useRunTest.tsx similarity index 95% rename from app/components/editor/run-test.tsx rename to app/components/editor/hooks/useRunTest.tsx index 97c01a9..5456de4 100755 --- a/app/components/editor/run-test.tsx +++ b/app/components/editor/hooks/useRunTest.tsx @@ -20,7 +20,7 @@ import { StringAdapter, Util, } from 'casbin'; -import { newEnforceContext } from './setup-enforce-context'; +import { newEnforceContext } from '@/app/components/editor/hooks/useSetupEnforceContext'; interface RunTestProps { model: string; @@ -214,17 +214,8 @@ async function enforcer(props: RunTestProps) { } } -const RunTest = (props: RunTestProps) => { - return ( - - ); -}; - -export default RunTest; +export default function useRunTest() { + return { + enforcer, + }; +} diff --git a/app/components/editor/setup-enforce-context.tsx b/app/components/editor/hooks/useSetupEnforceContext.tsx similarity index 56% rename from app/components/editor/setup-enforce-context.tsx rename to app/components/editor/hooks/useSetupEnforceContext.tsx index 5bd7c54..a8a348d 100644 --- a/app/components/editor/setup-enforce-context.tsx +++ b/app/components/editor/hooks/useSetupEnforceContext.tsx @@ -20,10 +20,10 @@ interface SetupEnforceContextProps { onChange: (data: Map) => void; } -const r = 'r'; -const p = 'p'; -const e = 'e'; -const m = 'm'; +export const r = 'r'; +export const p = 'p'; +export const e = 'e'; +export const m = 'm'; export const defaultEnforceContextData = new Map([ [r, r], @@ -41,10 +41,10 @@ export const newEnforceContext = (data: Map) => { ); }; -export const SetupEnforceContext = ({ +export default function useSetupEnforceContext({ onChange, data, -}: SetupEnforceContextProps) => { +}: SetupEnforceContextProps) { const [enforceContextData, setEnforceContextData] = useState( new Map(defaultEnforceContextData), ); @@ -56,40 +56,8 @@ export const SetupEnforceContext = ({ setEnforceContextData(data); }, [data]); - return ( -
- { - return handleEnforceContextChange(r, event.target.value); - }} - /> - { - return handleEnforceContextChange(p, event.target.value); - }} - /> - { - return handleEnforceContextChange(e, event.target.value); - }} - /> - { - return handleEnforceContextChange(m, event.target.value); - }} - /> -
- ); -}; + return { + setupEnforceContextData: enforceContextData, + setupHandleEnforceContextChange: handleEnforceContextChange, + }; +} diff --git a/app/components/editor/share.tsx b/app/components/editor/hooks/useShareInfo.tsx similarity index 89% rename from app/components/editor/share.tsx rename to app/components/editor/hooks/useShareInfo.tsx index f69df82..02c02a9 100644 --- a/app/components/editor/share.tsx +++ b/app/components/editor/hooks/useShareInfo.tsx @@ -35,7 +35,7 @@ async function dpaste(content: string) { return response.text(); } -const Share = (props: ShareProps) => { +export default function useShareInfo() { const [sharing, setSharing] = useState(false); function shareInfo(props: ShareProps) { @@ -56,16 +56,7 @@ const Share = (props: ShareProps) => { }); } - return ( - - ); -}; - -export default Share; + return { + shareInfo, + }; +} diff --git a/app/components/editor/index.tsx b/app/components/editor/index.tsx index 30513c2..31181f4 100755 --- a/app/components/editor/index.tsx +++ b/app/components/editor/index.tsx @@ -13,169 +13,479 @@ // limitations under the License. 'use client'; -import React, { isValidElement, ReactNode, useEffect, useState } from 'react'; +import React, { isValidElement, useState } from 'react'; +import { example, ModelKind } from './casbin-mode/example'; import { - defaultCustomConfig, - defaultEnforceContext, - example, - ModelKind, -} from './casbin-mode/example'; -import { Settings } from './parts/Settings'; -import { ShareFormat } from './share'; -import { defaultEnforceContextData } from './setup-enforce-context'; + e, + m, + p, + r, +} from '@/app/components/editor/hooks/useSetupEnforceContext'; -import Modal from '@/app/components/editor/parts/Modal'; -import Policy from '@/app/components/editor/parts/Policy'; -import Request from '@/app/components/editor/parts/Request'; -import EnforcementResult from '@/app/components/editor/parts/EnforcementResult'; -import ButtonGroup from '@/app/components/editor/parts/ButtonGroup'; import { clsx } from 'clsx'; +import CodeMirror from '@uiw/react-codemirror'; +import { monokai } from '@uiw/codemirror-theme-monokai'; +import { basicSetup } from 'codemirror'; +import { indentUnit, StreamLanguage } from '@codemirror/language'; +import { go } from '@codemirror/legacy-modes/mode/go'; +import { EditorView } from '@codemirror/view'; +import { CasbinConfSupport } from '@/app/components/editor/casbin-mode/casbin-conf'; +import { CasbinPolicySupport } from '@/app/components/editor/casbin-mode/casbin-csv'; +import { Config } from 'casbin'; +import { javascriptLanguage } from '@codemirror/lang-javascript'; +import useRunTest from '@/app/components/editor/hooks/useRunTest'; +import useShareInfo from '@/app/components/editor/hooks/useShareInfo'; +import useCopy from '@/app/components/editor/hooks/useCopy'; +import useSetupEnforceContext from '@/app/components/editor/hooks/useSetupEnforceContext'; +import useIndex from '@/app/components/editor/hooks/useIndex'; export const EditorScreen = () => { - const [modelKind, setModelKind] = useState('basic'); - const [modelText, setModelText] = useState(''); - const [policy, setPolicy] = useState(''); - const [request, setRequest] = useState(''); - const [echo, setEcho] = useState(<>); - const [requestResult, setRequestResult] = useState(''); - const [customConfig, setCustomConfig] = useState(''); - const [share, setShare] = useState(''); - const [enforceContextData, setEnforceContextData] = useState( - new Map(defaultEnforceContextData), - ); - - function setPolicyPersistent(text: string): void { - setPolicy(text); - } - - function setModelTextPersistent(text: string): void { - setModelText(text); - } - - function setCustomConfigPersistent(text: string): void { - setCustomConfig(text); - } - - function setRequestPersistent(text: string): void { - setRequest(text); - } - - function setEnforceContextDataPersistent(map: Map): void { - const text = JSON.stringify(Object.fromEntries(map)); - setEnforceContextData(new Map(map)); - } - - useEffect(() => { - const hash = window.location.hash.slice(1); - if (hash) { - setEcho(
Loading Shared Content...
); - fetch(`https://dpaste.com/${hash}.txt`) - .then((resp) => { - return resp.text(); - }) - .then((content) => { - const sharedContent = JSON.parse(content) as ShareFormat; - setPolicyPersistent(sharedContent.policy); - setModelTextPersistent(sharedContent.model); - setCustomConfigPersistent(sharedContent.customConfig); - setRequestPersistent(sharedContent.request); - setRequestPersistent(sharedContent.request); - if (sharedContent.enforceContext) { - setEnforceContextDataPersistent( - new Map(Object.entries(sharedContent.enforceContext)), - ); - } - setRequestResult(''); - window.location.hash = ''; // prevent duplicate load - setEcho(
Shared Content Loaded.
); - }) - .catch(() => { - setEcho(
Failed to load Shared Content.
); - }); - } - }, []); + const { + modelKind, + setModelKind, + modelText, + setModelText, + policy, + setPolicy, + request, + setRequest, + echo, + setEcho, + requestResult, + setRequestResult, + customConfig, + setCustomConfig, + share, + setShare, + enforceContextData, + setEnforceContextData, + setPolicyPersistent, + setModelTextPersistent, + setCustomConfigPersistent, + setRequestPersistent, + setEnforceContextDataPersistent, + handleShare, + } = useIndex(); - useEffect(() => { - setPolicy(example[modelKind].policy); - setModelText(example[modelKind].model); - setRequest(example[modelKind].request); - setCustomConfig(defaultCustomConfig); - setEnforceContextData( - new Map( - Object.entries( - JSON.parse( - example[modelKind].enforceContext || defaultEnforceContext, - ), - ), - ), - ); - }, [modelKind]); - - function handleShare(v: ReactNode | string) { - if (isValidElement(v)) { - setEcho(v); - } else { - const currentPath = window.location.origin + window.location.pathname; - setShare(v as string); - setEcho(
{`Shared at ${currentPath}#${v}`}
); - } - } + const [open, setOpen] = useState(true); + const { enforcer } = useRunTest(); + const { shareInfo } = useShareInfo(); + const { copy } = useCopy(); + const { setupEnforceContextData, setupHandleEnforceContextChange } = + useSetupEnforceContext({ + onChange: setEnforceContextDataPersistent, + data: enforceContextData, + }); return ( -
-
- { - setCustomConfigPersistent(v); - }} - /> +
+
+
+ + +
+ {open &&
Custom config
} +
+
+ {open && ( +
+
+ +
+
+ )} +
+
-
+
- +
+
+
+ Model +
+ + +
+
+ +
+
- +
+
+ Policy +
+
+ +
+
- +
+
+
Request
+
+ { + return setupHandleEnforceContextChange( + r, + event.target.value, + ); + }} + /> + { + return setupHandleEnforceContextChange( + p, + event.target.value, + ); + }} + /> + { + return setupHandleEnforceContextChange( + e, + event.target.value, + ); + }} + /> + { + return setupHandleEnforceContextChange( + m, + event.target.value, + ); + }} + /> +
+
+
+ { + setRequestPersistent(value); + }} + extensions={[ + basicSetup, + CasbinPolicySupport(), + indentUnit.of(' '), + EditorView.lineWrapping, + ]} + basicSetup={{ + lineNumbers: true, + highlightActiveLine: true, + bracketMatching: true, + indentOnInput: true, + }} + className={'function'} + value={request} + /> +
+
- +
+
+ Enforcement Result +
+
+ { + return; + }} + theme={monokai} + extensions={[ + basicSetup, + javascriptLanguage, + indentUnit.of(' '), + EditorView.lineWrapping, + ]} + basicSetup={{ + lineNumbers: true, + highlightActiveLine: true, + bracketMatching: true, + indentOnInput: true, + }} + value={requestResult} + /> +
+
- +
+ + + {!share ? ( + + + + ) : ( + + )} +
{echo}
+
); diff --git a/app/components/editor/parts/ButtonGroup.tsx b/app/components/editor/parts/ButtonGroup.tsx deleted file mode 100644 index ab68802..0000000 --- a/app/components/editor/parts/ButtonGroup.tsx +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright 2024 The casbin Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import Syntax from '@/app/components/editor/syntax'; -import RunTest from '@/app/components/editor/run-test'; -import React, { isValidElement, ReactNode } from 'react'; -import Share from '@/app/components/editor/share'; -import Copy from '@/app/components/editor/copy'; -interface Props { - modelText: string; - echo: ReactNode; - setEcho: (value: ReactNode) => void; - modelKind: string; - policy: string; - customConfig: string; - request: string; - enforceContextData: Map; - setRequestResult: (value: string) => void; - share: string; - setShare: (value: string) => void; - handleShare: (value: ReactNode | string) => void; -} - -export default function ButtonGroup({ - echo, - modelText, - modelKind, - setEcho, - policy, - customConfig, - request, - enforceContextData, - setRequestResult, - share, - setShare, - handleShare, -}: Props) { - return ( -
- { - return setEcho(component); - }} - /> - { - if (isValidElement(v)) { - setEcho(v); - } else if (Array.isArray(v)) { - setRequestResult(v.join('\n')); - } - }} - /> - {!share ? ( - { - return handleShare(v); - }} - model={modelText} - policy={policy} - customConfig={customConfig} - request={request} - enforceContext={Object.entries(enforceContextData)} - /> - ) : ( - { - setShare(''); - setEcho(
Copied.
); - }} - /> - )} -
{echo}
-
- ); -} diff --git a/app/components/editor/parts/EnforcementResult.tsx b/app/components/editor/parts/EnforcementResult.tsx deleted file mode 100644 index 715a7cf..0000000 --- a/app/components/editor/parts/EnforcementResult.tsx +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2024 The casbin Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { RequestResultEditor } from '@/app/components/editor/editors/RequestResultEditor'; -import React from 'react'; -interface Props { - requestResult: string; -} - -export default function EnforcementResult({ requestResult }: Props) { - return ( -
-
- Enforcement Result -
- -
- ); -} diff --git a/app/components/editor/parts/Modal.tsx b/app/components/editor/parts/Modal.tsx deleted file mode 100644 index 6bb06b5..0000000 --- a/app/components/editor/parts/Modal.tsx +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2024 The casbin Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import SelectModel from '@/app/components/editor/select-model'; -import { ModelKind } from '@/app/components/editor/casbin-mode/example'; -import { ModelEditor } from '@/app/components/editor/editors/ModalEditor'; -import React from 'react'; -interface Props { - setModelKind: (value: string) => void; - modelText: string; - setModelTextPersistent: (value: string) => void; -} - -export default function Modal({ - setModelKind, - modelText, - setModelTextPersistent, -}: Props) { - return ( -
-
-
Model
- { - setModelKind(value as ModelKind); - }} - /> - -
- -
- ); -} diff --git a/app/components/editor/parts/Policy.tsx b/app/components/editor/parts/Policy.tsx deleted file mode 100644 index 220d4c1..0000000 --- a/app/components/editor/parts/Policy.tsx +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2024 The casbin Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { PolicyEditor } from '@/app/components/editor/editors/PolicyEditor'; -import React from 'react'; -interface Props { - policy: string; - setPolicyPersistent: (value: string) => void; -} - -export default function Policy({ policy, setPolicyPersistent }: Props) { - return ( -
-
Policy
- -
- ); -} diff --git a/app/components/editor/parts/Request.tsx b/app/components/editor/parts/Request.tsx deleted file mode 100644 index 49ae151..0000000 --- a/app/components/editor/parts/Request.tsx +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2024 The casbin Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { SetupEnforceContext } from '@/app/components/editor/setup-enforce-context'; -import { RequestEditor } from '@/app/components/editor/editors/RequestEditor'; -import React from 'react'; -interface Props { - request: string; - setRequestPersistent: (value: string) => void; - enforceContextData: Map; - setEnforceContextDataPersistent: (value: Map) => void; -} - -export default function Request({ - request, - setRequestPersistent, - enforceContextData, - setEnforceContextDataPersistent, -}: Props) { - return ( -
-
-
Request
- -
- -
- ); -} diff --git a/app/components/editor/parts/Settings.tsx b/app/components/editor/parts/Settings.tsx deleted file mode 100644 index a6a2061..0000000 --- a/app/components/editor/parts/Settings.tsx +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2024 The casbin Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import React, { useState } from 'react'; - -import { CustomFunctionEditor } from '@/app/components/editor/editors/CustomFunctionEditor'; - -interface SettingsProps { - text: string; - onCustomConfigChange: (text: string) => void; -} - -export function Settings(props: SettingsProps) { - const [open, setOpen] = useState(true); - - return ( -
-
- Custom config -
{ - return setOpen(!open); - }} - > - - - -
-
-
- {open && ( -
- -
- )} -
-
- ); -} diff --git a/app/components/editor/select-model.tsx b/app/components/editor/select-model.tsx deleted file mode 100755 index 9cd9a87..0000000 --- a/app/components/editor/select-model.tsx +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2024 The casbin Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import React from 'react'; -import { ModelKind, example } from './casbin-mode/example'; - -interface SelectModelProps { - onChange: (value: string) => void; -} - -const SelectModel = (props: SelectModelProps) => { - return ( - - ); -}; - -export default SelectModel; diff --git a/app/components/editor/syntax.tsx b/app/components/editor/syntax.tsx deleted file mode 100755 index 41f721b..0000000 --- a/app/components/editor/syntax.tsx +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2024 The casbin Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import React from 'react'; -import { Config } from 'casbin'; - -interface SyntaxProps { - model: string; - onResponse: (com: JSX.Element) => void; -} - -const Syntax = (props: SyntaxProps) => { - return ( - - ); -}; - -export default Syntax; diff --git a/app/favicon.ico b/app/favicon.ico deleted file mode 100644 index 55382c5158e0d6e5f26b6763997983612ff3e0da..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4286 zcmdT{dr(x@89!8~THB_{WEx|ptv1sTaoILXqh{I~+Bnp!Jm z6rYUZ${WH%F%U!zD(u4Y5Lj4XcUjm41M*s7Up$swd_>b}LhIw#@7$FQ%R^!ES1^bM&^ZU;C`@Zko%a|AaEnCL;e|F(%#-3x0Eho?r^C$3e;i9?!$15pM#bgy)b|j;E z{eiUVgK^oF8=Si27o55jKSxtifTJn#uOph1p@ePJe~a+?jh1BZf(u12#-vpT9ZawK z(}~lyi;ib2XTANr#yG~_W-Ow3-NG&BmOmF%;P)MA)|C%V=U#rOE6*Ms(Zm}GpAqL6 z;Q;}UaG%rm!+mX>h4e z*Vc0R8D`MDh-Wj?r2v2{*95Mo0cuXi@5JxiFvA6fPb7Aae| zFjOHbiXeZF?|nLcuEjq2YIM~fn5zwUC^y!SAExk!$b4C7@y}L=*NART{m`kJy#_oNTPm^>*&28ye57*wU}x#8Z2Kj@r^6~l`luGp&$-t^<@gw1 z-2WVRzqkptRfhfQUwYc05JRVYcUBrWv{JNJr}J85KAKhg<6{$QVNlV_Y|AI!&xcm} z^zyim)QgbX8-yEU11Pi|LPmEuY&Wh$Yb)`vVSAYfs+uS`wFz$j3UNM7Uh-V+txZ8% zj$C^%qv89B)lL1UHU18soNpDVvB{uAFG zQG3S!^!!un56^*!`jr%eu6*?1*Cvt%PprxRq5S{dh&I8!cm89x{NuSl-n#UUz)0H# zSd9h@wwHp=y~oAE=fu61&VniYTMNCPr@Cs~P5HlDP0;uXb)UyW1mz^pH=gq$SC)gH z0UW_{9~8=?QL3myN$oXURy3f#GzUZK8U1jZG+0US;18zS7hEh_6f6_%lqgmnkSJEY z7gj1hLbdqJ_A8-WEczAObpZH2J*G{Mr_f`Ly#3X zcB}Ox!kNWRZDKdi-ywY-O#MdmnQiFoF`}PZYi~0K)yZftiGf*p41*mCn9OE$8~f3F z-2{`x1{>MQ^N;X=&Yn$^>+ekb4qYlE{E+-*;?LVU>(Fl*Kw?rd{D19_l(aPBvSILQ z5!FB`?9G|5^!GqnSC5TvzKLzywxR9Q4w(D8Vb^^q*g)?e!6WJuGf{esbDao3B5n({ zuy(73Jp~2^!q0CNBoYaFXpgpQO3<1ZGUUTzwIL%j3+vzb4PJWbCn&G1g2mj2;pQyj zm%^b-4xKIks3D6v^_fdZFM8LM3R-f1=H`8jqp`85s#Xdb*gCGb`1QFkn@!NSUPV^+ zDI7X{7#&@`6bA;_wMl&c4=MjQPp&^6n|{_we^uw+#ZHyWUD4jgRL!?-TgEa4wF|$74Ww z2wjEYu&Ym4DGp!cKA7&}@2AAUXx`7RmgWpMo=B&)KA|!Gtucvjw*7cLJ)1y2I7>Mg zI%>$C=WO|KI(lJ@^Q+$f0lU+6&a3mxMvB9XZ<7uGpz&LCwBa84PDZ}=w$nG&U1Oow zXiFL+|If%h-2{JUrkI>hK3Pw8TqYZMEq|OZz9gNSDc*Ka4S9z9n#c6lxgL!Di`mt2 zjQsX3T5mP2{fF%ZX31f_X?~V%#tc>Hvl z(S6Qm4<}`!Oat0_TJlp&GM~3|T1q&GvZ)NCrAVx84GP(6RiwkoN3m#wq;CBqps&So zed7mHlNy~-i=iUo4AupVtpsAbs^HYgdX}ZZ67%H5XHNY${RV1>Qi4RP=_5ZHcbVMh zF2YHl%}1XuQR3;_nVA_7iG~w27neJMG8rME{0vNSQ7X*;)2ws~jq+T}7w=-kV1_h> zS3?Sg6r+0LgYQNC-nyiKzOZInZDax!v4oyx(8M3E4WZ~S`^T%qtOicZse_TO6Ul%Z zVkgKWw;XSP1Em+y)HO*jWXb&d6a?S8qsx*>99yiA+foajA2Ui5GMxxPQHX_~4$%9}lg3_}Othl~FV)O!NF$OP8m&CR-Os zAHt!E5Nh`885Dx{zwS!L8I`BOvelatN5ABXHHhVL>dp%iZq6Jc8SkNbWJA$utp7l{ z%CWm?Wo{-AIzP;O%*peq3vnQw)|Z zRBBGaY@L-)^5@Tj*{jJ-HXmt{NVS}X+SREym&R|;LGj@y8teG&GcXjCtgWcXUh&Zo z8I?})V?yoq?N2pd;5kcM$a9JjsU-71==>_SLiR3|guVUJ?{^rG zgS4G2=A^OZa{1oX?QO~*r@VEpy01l$5>F6U6Mx%eYvNy;XlZ$BI$4y0g`s-q-?MD4 z9t3^qrD%2=7={q=>|65^v=D#Af?- zj=VY|udVi=$h`pjb#}0^x`7Ri(Zr54n6Njn*(oQ|6+Fr7(=VBSE!fp4?TMh3$?DIJ z?~HpI=U*Kl2LBVlKEGe1cv`Kq->;S#mY?2TGVkzr_M}-(;PBTYCm82xyInO>7m&fb z!t2$gplm<=^1Fa+|9+B+OZ^o?IWz2zI9Ttlj$xl8Fj6!Sj6+9u$Ptjf5T1E<4b@{3 zmyLOzPTnOosJ(XaK$ICAdE$SMCe0nopD+;e#dB);t;@>>7sgszO+AwY3(21%ljk+2 z$f2<3HQRm=`a?&D21BGz`B&>}iNC+y58k=3I@NWj#zOkBrc9->y`Pyql0v*G=a!q= z%%zJAVt!=ve@o@8KAJpdCoy#@Ffyfb z*<-gm1A-jGjrrI%4cQ*^M4K`If>-d?2zY$7*dj?Oy0UuEHGPsT62s?$J=`@X5@^!! zrp7c}RI;Af_)du+t(%dJV`eAGp8yx+jk>57VZ2P2^v$ae52a+F-&Ea*K!It)KRIH2VMfFL zY`e&DJ?GnXr0b7`;s)iMCvIRsw$k*qgOMz`C>8`!VmI+3*ti6^`M+1ksIPG)GKrmf z(s8IH!X$ljGhfN$qTi1qQPtHLE9=COtL5e3U411nu^kg3mM(5zswveY)C;3PWC96t zv%;bG5+TqEdvZ-sclqp&Dgq(oUO*Le-}JdFtLkDYX6aB4S0>p87inR4R;uMs$)Oz#6o`&f`6E7eSswAU@Q-UY6*s{DVIY3IF{-2 zTds9~iV%~4%3l~?l9(G9nPMs-b&At@YXxSR`b-T|=XO-&Q-Hjv3-7<+bG8!-jj@sS zf;$_uc3v;P{?F_)PQY+U*F62u59HH^^zlTPWSOrxUO(s*?hN~<5qT1&UJSt>FKN+oc`lHA;a=O#v$C+-$iY(}4eRt2T6G-YN!pmM- zUOTSDU7*^p7K0&qw%sOo0lmsLpZ7k^H&5YVn}VTBwHEZ)&|)5BRTO5ch2JINrsiiYywe|S+&(l0$j&xhY<_WAXz$$rLVqBN@HfsT9BLlKrmZ|L{AjKjWqu; z+QU@Mwy~z~AW|@O4c-WdU>8^ec7YpcWL3DRRY`xO%fvxAAQ@tF#LiP5Q!3;gBuJ8@ z_5KRp(ZXrvHV)b#fwRY%?FP~2kWZ&^Vmy8&0hiyjbO=C%z=b`7zzWf;ARRJ)FCP7) zL;PZ0BLGyjsXP<)gKl$YiU=ogi6P8FyY?>TnA%O>-XGQavoDm)%o0ZTJo_z^vN z^HeC`#(3lae^(4UD+l{LN8B31un~r2UbI~V&!3BUCpKsv+Z}S~wAUnNZ0h~V-lRy; zh-L^fShHyRN}8!+6lCoLi}Th5}*33I=m-B5}o(q1@Eu^%CFct+*H;<|`t#rY$P9S{z1e zY8wM(5Z%7dz*5XO+5|HW`{gQOg1r9uuXE%|a;N*<)%60jg|#fx=^2vPQh<*-T6pi$ zN!ZG=eaM!9{UI_SIkel!G#NTNnQ(ee!HB$u{3+)y3jqWuUCY$Lc)FKA6L3H-OZPgA zIYTw9FmlW#VE?31jyQ;WIK^*+5+7xEzw+zxF&|J&Z0`6PKjM;N;gsW- zjuzN;a-t0=WakqwmEhEfHSwhHIpo|qS9HBT?6n_#qIY(m8Ilc?cDKGgs%oSb%PgfN2d zuOvasD|9Dsi%LUK^rQRRmN@yW2&QzS|$KZvXb>^N){vgB~}L z(-OM)oLh-_xBQ7jp5mKisL2y1F$S07^HHJd%K}WR)z{ElUA9DoZqGdCyC0itgHYq~ zSigK0h=zy+fA)MLm(0e<2EWs7uC^Fy@Sh}on$O{D+%$f^sn&P9-7o6wA)7owO;~d} z@(^8gzd8rrG>rcUG)5Tge*Nr5&r*J0%kc`7QSN35(X7uOgJn(e)?urTm&FtsqZU~W ze(fm7>Rz6~SO*QUq5S;$bD*ct_eNg+AZ1|1QMyv@+xqO%QWSXR25%iTHO^zN_o(x{ z7!T+u+s(^m2MJT9A;Y*a4BGOnzCZ-@zn*AV{rL3P{$v=vuOTY=>1QLUlJGZ>^1bYO#3Bhfz9N@z$^5Z;#g=3)39y!A z)YivS{nxabgiV{?y|um`@4)M%@boc?Esxzi(CZG7H)M)QPiY$+6>agt2h%6?v>m>i zg%lpHa*ZFS;MAXu@quW}o@)`yWE~DKk6cNfkUGlbKxv`${$!dfIoO4DXg2dQQT#<5 zzcS7u%(H*U4d<4uQJl=qNT~81_V#d6u^iNt^w@BMI6$oO9@VVNM8~2M!u@+J&W@9V zn);hat{WH7GOuDF#dibNVbFTrlMDe$C(zwK-Ht-0$Ua>cON@@4S9>hR*3ypM^-@5nPCkguZmd*0_SQYvHszkh*Cs z1)ZxgNa)^lY>6b@PMhRhafW5L*EQp+3O&$$9FC!;O@%O|=#9ULfx=jGU($b9R3P*` zJx^b`JZ{9tQQ^8kE5}2#wsu~H*cOPIG#Hh0J{f~!s-|1x$pOb>cmn0e-uYl+g?UhP zx9=>)+sn@9P=Rp3b$>jo`PRp~Yx`e1M#eusZ*_KGPI+Pr<)dp*U@0Qljl_|dt?m9i z)pi})2fqBQx*99|h&d_@5kGrA;zL{1^0$;!-jo+3Z=`OrsrA-d@fktd7fOI ze7z7e`^1H{6t=O{noGVYK{H!|^=O8O7kH?q;kZ3*HogyVO(~{9LA+Q%U z<-+Jnu+&!grFgQ>TMnt>?HmFSDm^MA%HNX6Wl}}LfOTl_LjLvXDyV3_w(Z^7hvIW( zV-hxRo|wS{$x*0!=*<)WDL1;5;RnFE2%)T2Me-dCeY-nqv_2rg{Mi)Qn9N<+=nW9t zMak8WIp)hI75D7Gd4i*aTO`upBE^ctL4+bbR8ywZwl6U2xi5pA*wA($FnbbeY^dYv zBrjLbRceOD$EC2BVZv+i)F(q>`|3*c(DjZbkbE{cBF4&>6uD_CWw&UkIcX3xh-U(f ztS*EpQ+)f1eBk+HcNgWUYla-JSW6$NM!&^qVoNg(H0gKREas_s3Mm ziAGkGuq)q^mr9`?D1(&Iz(1sTb5`99pha{3oKGmMu5cMTBJ~9%tu4%5Gch4qq;gX@ zRLfQz0RW48ZEvUfj_u{m8ZV*sI60`~S;yjmzu?zp3x-MkKam5Xx8R1$KQrzcC7%9l zr^vF3Tys*7NI235Y}nP@w@o=Fffa3Tq%AcRWl3p*e#;Dw2o1 zX(?G4a!+o*@@*2()K3o5k)uJ%pB5;VZ41ZrCU1%!_^-aZo?r+i6x{^g`%2s{IwOXP3;j9us@i^1FsId``PjY7b866m!~t}#?~O#)r^I}Y`?j3R(| z5(ZxKwUj(c1l?V9C4u1Qh%H7E5Sory2!&X1Nw=~5TuQ&?fp6pK*cEOjSi!Nc)4^@|R#%Bd(~(Ezp`$Qc1M0Vr5?xC>X^UA7 zkf*z41Jytt&3Os`mwVz6sZ<~PR|!ci{!RJYPMX8odx$g}_Q!pPW5*{dnfNk4b#WBn z-x0CmX7fp@=yHRe=asB!$Menmd&^U43@&QtS#g(sJ54G&E*$1Tjnlyj(|@>40{nwD zJPY|b#&92|2qrI&vNNX{E&1RCS(xc zM!{te{8TTl4gFRllUjq1Q}nj`u~@z(6WbxY^V+9|&)Rtl*>WxtGZk#pzeJO_8UcIH z-DYZs%wo`p@&fZtPC1Br?AQk2Z~U!d4|8Ext>iKn7&pR@f~MMf(%qFD;!^lJ0K2DH z2pi!fd2j#uwAFXLJoEIL&g&fL(e|K4dKmtnM63=*K@pdPsCe8jL}SD#ZPo zQpTkce(Z=O81Yjv!u#DUCH${Ri|I4jd400Fvv~*w(m3%7vq$t>kW7{erVkI+&4vXN z9Xxs8=U_*Iq#OJrBavEP;F zBW^ONgi2J*3;PHkdUmt&3^T}QaC~pv;i&VMKJ8FQ*@D7!X8Fc^yTXTuA;BX7$I4Bt z-b!ldY5$NxJ}d!~W4=(hf)uA^Jti4LKa?}Xh+&OmaHuMOV?aijgUvSGS5Mvg-M0do z#|K{NBUo+Qw59^q$P$>u6E<@qb`s%_M~&ulr1wP#50(g#l1l)prwV=c`{!SN$`^{K z2`zjUr?6#x#tM_gOAusSYpVm)D73Evt3S3GCEpk22yAAp%31-Mg>twT|N1RuGD{|D zxvMYFi(9D(;d8LIP@|l_X;4mI`imA`og-dU{H$;(N-@bwk)4HPZJaswxN{iN@or02gC56ekW8-;Oa{rI(3h8b>N;?E7F62Ag$ z)qXOK;G_{oJS{YE*wclX)9W?0kwIb?P~y6|3Po?GG%1}DHWd4%IJy_WOp{;z3}F4; zA6FyuD>cGfM~xk5OG1Sv4#@j@2f&EV54f)CUdC-zq@<6F)Xjy@iO`&$y}0ZnKJSU( zEcQ)vOWe05k+Q3)uk91&)(KZ)FIrPk73}soB=B7c-EFa(h%uk+^rOYVf?=s^35AcI z`mUI@VsZu07`$#ySG6>(iE^~9)`e=#FbP{{6p7+kqxle>kiMpUbQYR>h4b&fXqtLn zu2aZ8ni)X_Z~e!U^!uO;GZm|O^Hn^}Q6k{4#-{Dl=K9mynNk%1o>xITBrk~{hFNS7 zrUv7F?$HSIU6DFhOAkWOfX~jy-tk=xL0gO6{O-t9-s0FQ1=1gP^fF%j_e=Bt0T8w8MAc;ROG4ab!B+k<)ZnEc5sf9<)Opc|yw4!(3>-*WNv9vcdUCzv zyAZf0dirWurFum*&GH-G0~Kx7Lul@ ziaPY)RFC2_rfyVmz9)&dIBNx7+;Bc-w_nRF&XZiAG4a zCC`2El=Fm~B0{H0TV7QmLgm7YG53qtxK`F*uav!O!QDUb?J57{(~7KVzaLxCniV_# zTuD`}^T(Em8d#2HHxNw$>jXO|Vx_9t9!Ys{MP=^;b(6YsYO!PoB_By1pjg}**1Vgj^enLLvTiGnsfCR<29Pg_z(#O;I{lni9>v;MZ+Q@A9i5An8GpAe?9r)hE9GY z;{VbGrtZps4y)fr{fL}(*P0IJh~2X&UqMj^Ww%M^9)-Gs*Pl=L2J{64f5~ZJyUGve z*-~Cg3vq>pd-7kt{Vb3S#p`r1w6U)@#+Li(DggabHSFA+tAESN=rGw}GnEolXI@tC z`MLiYPa>7$|AJFM@OGQrYsRKttO8h$w3 zCKJ7p2BW`vy47h&%=afm3o2>9j;c@rXNmi>>zNmQu_K%9AEi0zRN8_wa_#hH|7qXH z<#u|1!x-4I@lEseVW^F^>*MgYqF=Zk$5_XR#NNJ1W>o<$OOA4|B2(cRME^7!si$Lk z)(L8{XtRvSh<@rkLi53xo(zaw%g~Ko2y95IlLjxo%4m;gKuzC%6_SfItFeFB=NE~V z&!LBkQv%sHXm;^Ee;V=ykZX7M!k0%)94K`$wBSI*Rf`tI{AWZ-Cl#2&F$t1e$dF5< zg4;o zSJkI%ceH-5B>s-Lb>Wm|2Xg6EZN*-8=G2%AtYa9|?nY%V;A5k8&dWi9`H7N+c3kX{ zr`|z))_%BVe+1eE;oTn);Nuwdh8@!z^;0WZ3A}w0Mnq6~3o$z3k7BFiXJNwaOIiXA zXh3!xqOdpZL#mzlCC((KT{n|Z6TDAsf;?hwSn3wa10Bh65 z=0_?>sNtApR`Une{8fZbBDriyQM0i;bVLHMtWYeQSq)!1oiFT+=Jza&Xog0EZ4uq=QEx|`n*k%qqfWnY-8s$wa1%QdrIJD`JT(R!X ztK4n{3;78)-zm}@Z)5MiUTDL=lA+|vwha0;x$=<%fX0u|IV(%A$jZ}G7C*jVlZ6A; zm3E3>qC#aC^(}V{_;kzx9ROXF1(CAxks7gotgCR_WuvbVSK;?~#zqk~{B@M|UdbGi z%=!~mT8))6wk@3NnJko5P?cO+H>$>Wi9OI5^UlOuF!~5F_f>7d$+wQA(5bub^Msrn zw!9~3j7D-xc-MwsTYB3&J&n)EhS-o>LIu#<5d_OF%;x!LO!bR%l_R5aXtvZD)c;s& z>K#1fDT>}oLa@i9$wa?>g}XVR?DvU5^Dn0wH?Zj;q)P2tkd0IUlj#CP2Ne`42W84N z+(}t^5z*)E-vG%m9{&*i4k^^7{j4VLtb`@z76WV@1k)*bd9D16{Mm6qzCseW|J$36bCmR0v{ju{siF zyi~z;59zBcyf!dl^*^@ozz*ue`|aa6CB*SqCdn5|yh;6N!L#oE$DpoxmmF>$65#T; z{;lc)9bY*N#nmmhNuX(B$-ywYpkGXp_Cm=Ah*`WM%yf@_ak_7K+1^ z+t+OTX()bL3}A@h<&ujgp<*yWPa!cP#qJI8NYfVT(64@$EZCcDNGIy$E}zx@ofygV z$0)DptnGWV{=reHSnjVy-=_P55V$L)w*U!W>``ygShA#|-VDeX47?B4zJr5S2M?hP zpb>bL6?ovNX~IShtQXK!dF#Q!e#pjJ*J(%kvJehA#MoF&UIzL8E{-nZiE8AutSnS7 zSJ*F3mZ9M>(JJQ7N|gVY$8j+8IQ`R3N(I?6EYW52H9TpyAfP-Tc_wvcHC~KRBVeqQ zA*CKsRowOC#9-zyZ+f^aDX&Q&S_150qn%B5p3>I5_apzy1MC9BRkzxk1D4StGn5|* z<|^RrctR~b<%P$TpmOkC>8!=`57$z@5B&IhFi5(L#Y#5}WX(76t{)WhTi~H$1yh0| zKC-Jp0VJO?!?x3+5Olc@k~rl51yB*ns)GnL#$-agEynP82{E;!|9EaEt6arL%T4F> ztUjy>)A<^}MI>=w;OEO)$VYgAN_Ef2@l`ye>o-`T{G7p{UXWZwBGph@n{-=!R^7;g z<7up}K?CM^yeLihJ)yj9kDJ>`u0D})aSJ*2ewi4XHjs5K&@mpv`+Z}dd)3DLH=sRs z29qmy^2j3fUSj1Wr@0~(Gn$77&&A%2ZFrH#lEaTNW%Td;j^y%x*_JA)`}EOLI*jg7 z?e}p9hmYXoVHeD)ssjYAEaCr{!@4Dzsx54QtT+OU#Xa`=>wC1w^+#{fQqToyAKY;Lo&-D!S2~G9aDr@l z(@rC6qXQ?~ObPgrX||zM(>4`|ZuiPgc|R5$SsyE%Oena36QM4q5YeA+!-7%2r{4gf zZ2--f7W41+s4@zNb`tWCB`y%$K z!1;$`xCCz$?V5=4HprPDODaG#w31&wym8SO3@6VLuAUKZ z$7P?-%`UcANd%(f8Rm8FurdmSkWR;lcRT8u-scfceD}}FLVZ2mA1xiVT)9sCnbnd3 zXVl+MM>bI_Va9(XE{a1zVdjvre#nFgaix-&8Qi)g(_`RJ=CinF%U-KTs;AT1bNhz~qLfoNu{yu! znD}{e5=EH|vBF^XrLpnVy9UNI+qWW{f#KDCFN~VKu}a@z@*E;UIiG&@5Cw@>y}Ay} z!tb8i##Eo&%lsRXg;8=`{k4E_-4hVFdQoF7IFGuWP{+MaaJ^_wCRoLE*Thon{;|th z0Q|re9)5i1+rKx=GmqFcn^?+LG|&#NZLqsWeD2Y(w#~ewYU@zNBVBzMCVh&66J?cB z;|wZ*SJAHSyo<7`&b~-3c>|a7%$^|XK-)s7L4KE7@%Hv7-V4D~JbaO&FwRDxQs zhP{regMXL)WVi8{w1<+1tPKctE8xGr-szr7hK&xWiu!iJ5Jr{&5tV4QL|#>WsAe_4 zOWd@0CvN8#{`1t}F(ImQ7K1j1e{R=Q(}f2$AxVw$-Dt4b)ej_YI^*bbm#4_!{G*o^Z##tgn(-uKR+S=xORxz(n^D0AmpW0q-rEg!~PF^ C=}~6@ literal 0 HcmV?d00001 diff --git a/app/page.tsx b/app/page.tsx index 4f69f24..d90578d 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -14,11 +14,17 @@ 'use client'; import { EditorScreen } from '@/app/components/editor'; +import { clsx } from 'clsx'; export default function Home() { return (
-
+
+
Copyright © {new Date().getFullYear()} Casbin contributors.