import { useState } from 'react'; import type { Meta, StoryObj } from '@storybook/react-vite'; import { Typography } from 'antd'; import { ThemeProvider } from '@/hooks/useTheme'; import JsonEditor from './JsonEditor'; const warpOutbound = JSON.stringify( { tag: 'warp-out', protocol: 'wireguard', settings: { secretKey: 'yFXfmXX3Zn5tnpNJ7HAcbLvqcMVioqPDGV1GXn2FeV0=', address: ['172.16.0.2/32', '2606:4700:110:8f81::2/128'], peers: [ { publicKey: 'bmXOC+F1FxEMF9dyiK2H5/1SUtzH0JuVo51h2wPfgyo=', allowedIPs: ['0.0.0.0/0', '::/0'], endpoint: 'engage.cloudflareclient.com:2408', }, ], mtu: 1280, }, }, null, 2, ); const realityStreamSettings = JSON.stringify( { network: 'tcp', security: 'reality', realitySettings: { show: false, dest: 'yahoo.com:443', xver: 0, serverNames: ['yahoo.com', 'www.yahoo.com'], privateKey: 'wLc4dpQvRt8mK1nS9jH2fXaU7yEoB3iZ6vNqTgCkW5A', shortIds: ['6ba85179e30d4fc2'], }, }, null, 2, ); const brokenInboundSettings = [ '{', ' "clients": [', ' {', ' "id": "9f4c3a2b-7d61-4e8a-b5c0-1f2e3d4a5b6c",', ' "email": "user1@node-de",', ' "flow": "xtls-rprx-vision",', ' }', ' ],', ' "decryption": "none"', ].join('\n'); function ControlledDemo() { const [value, setValue] = useState(warpOutbound); let parseError = ''; try { JSON.parse(value); } catch (err) { parseError = err instanceof Error ? err.message : String(err); } return (
{parseError ? `Parse error: ${parseError}` : `Valid JSON (${value.length} chars)`}
); } const meta = { title: 'Form/JsonEditor', component: JsonEditor, tags: ['autodocs'], parameters: { layout: 'padded', docs: { description: { component: 'CodeMirror-based JSON editor with syntax highlighting, live parse linting, and theme-aware styling. The panel uses it for raw xray config snippets — inbound settings, stream settings, and outbound JSON in the modals and settings pages.', }, }, }, decorators: [ (Story) => ( ), ], argTypes: { value: { description: 'JSON document text; the editor resyncs when this prop changes.' }, onChange: { description: 'Called with the full document text on every edit.' }, minHeight: { description: 'CSS min-height of the scrollable editor area.' }, maxHeight: { description: 'CSS max-height before the editor scrolls internally.' }, readOnly: { description: 'Disables editing while keeping selection and scrolling.' }, }, } satisfies Meta; export default meta; type Story = StoryObj; export const Default: Story = { args: { value: warpOutbound }, }; export const ReadOnly: Story = { args: { value: realityStreamSettings, readOnly: true, minHeight: '200px' }, }; export const LintErrors: Story = { args: { value: brokenInboundSettings, minHeight: '220px' }, }; export const Controlled: Story = { args: { value: '' }, parameters: { a11y: { config: { rules: [{ id: 'scrollable-region-focusable', enabled: false }], }, }, }, render: () => , };