Compare commits

...

2 Commits

Author SHA1 Message Date
ssa
d87d86bacb Merge pull request 'feat: JSON comments support + full-height layout' (#9) from feat into main
Some checks failed
CI / build-and-test (push) Has been cancelled
Deploy to Server / deploy (push) Has been cancelled
Build Docker Image / build (push) Has been cancelled
Reviewed-on: #9
2026-02-20 16:42:18 +03:00
sokol
9f51379df9 feat: JSON comments support + full-height layout
Some checks failed
CI / build-and-test (push) Has been cancelled
CI / build-and-test (pull_request) Has been cancelled
2026-02-20 16:40:13 +03:00
4 changed files with 44 additions and 35 deletions

View File

@@ -98,7 +98,7 @@ function App() {
return ( return (
<div className="min-h-screen bg-gradient-to-br from-slate-50 to-slate-100"> <div className="min-h-screen bg-gradient-to-br from-slate-50 to-slate-100">
<main className="container mx-auto px-4 py-6 max-w-[1920px]"> <main className="container mx-auto px-4 py-6 max-w-[1920px] min-h-[calc(100vh-48px)]">
{/* Header */} {/* Header */}
<div className="mb-6"> <div className="mb-6">
<FileChooser <FileChooser
@@ -112,10 +112,10 @@ function App() {
</div> </div>
{envs.length > 0 ? ( {envs.length > 0 ? (
<div className="grid grid-cols-1 lg:grid-cols-12 gap-6"> <div className="grid grid-cols-1 lg:grid-cols-12 gap-6 min-h-[calc(100vh-200px)]">
{/* Environment Panel - 5/12 width */} {/* Environment Panel - 5/12 width */}
<section className="lg:col-span-5 xl:col-span-5 2xl:col-span-5"> <section className="lg:col-span-5 xl:col-span-5 2xl:col-span-5">
<div className="sticky top-6"> <div className="sticky top-6 h-full">
<Environment <Environment
envs={envs} envs={envs}
onChanged={async (e) => await handleEnvChanged(e)} onChanged={async (e) => await handleEnvChanged(e)}
@@ -127,7 +127,7 @@ function App() {
</section> </section>
{/* Content Panel - 7/12 width */} {/* Content Panel - 7/12 width */}
<section className="lg:col-span-7 xl:col-span-7 2xl:col-span-7"> <section className="lg:col-span-7 xl:col-span-7 2xl:col-span-7 h-full">
<Content <Content
env={currentEnv} env={currentEnv}
config={config} config={config}

View File

@@ -34,33 +34,40 @@ export function ConfigTemplateEditor({ config, onSaved }: ConfigTemplateEditorPr
setMode('view'); setMode('view');
} }
function handleSave() { function validateJson(value: string): boolean {
// Validate JSON before saving (with placeholder support)
try { try {
const sanitizedValue = draftContent.replace(/@[^@]+@/g, '1'); if (!value.trim()) {
setJsonError(null);
return true;
}
// Strip comments (// single-line and /* */ multi-line) and placeholders
const sanitizedValue = value
.replace(/\/\*[\s\S]*?\*\//g, '') // Remove /* */ comments
.replace(/\/\/.*$/gm, '') // Remove // comments
.replace(/@[^@]+@/g, '1'); // Replace placeholders
JSON.parse(sanitizedValue); JSON.parse(sanitizedValue);
setJsonError(null); setJsonError(null);
onSaved(draftContent); return true;
setMode('view');
} catch (e) { } catch (e) {
setJsonError((e as Error).message); setJsonError((e as Error).message);
return false;
}
}
function handleSave() {
// Validate JSON before saving (with comment and placeholder support)
if (validateJson(draftContent)) {
onSaved(draftContent);
setMode('view');
} }
} }
function handleDraftChange(value: string) { function handleDraftChange(value: string) {
setDraftContent(value); setDraftContent(value);
// Validate JSON on every change // Validate JSON on every change
try { validateJson(value);
if (value.trim()) {
const sanitizedValue = value.replace(/@[^@]+@/g, '1');
JSON.parse(sanitizedValue);
setJsonError(null);
} else {
setJsonError(null);
}
} catch (e) {
setJsonError((e as Error).message);
}
} }
function handleKeyDown(e: React.KeyboardEvent<HTMLTextAreaElement>) { function handleKeyDown(e: React.KeyboardEvent<HTMLTextAreaElement>) {

View File

@@ -31,22 +31,24 @@ export function Content({ config, env, onTemplateSaved }: ContentProps) {
]; ];
return ( return (
<div className="bg-white rounded-xl shadow-lg border border-slate-200 overflow-hidden"> <div className="bg-white rounded-xl shadow-lg border border-slate-200 overflow-hidden h-full flex flex-col">
<Tabs tabs={tabs} activeTab={activeTab} onChange={setActiveTab} /> <div className="flex-shrink-0">
<Tabs tabs={tabs} activeTab={activeTab} onChange={setActiveTab} />
<div className="p-4"> </div>
<div className="flex-1 overflow-y-auto p-4 min-h-0">
<TabPanel isActive={activeTab === 'env'}> <TabPanel isActive={activeTab === 'env'}>
<ContentParams env={env} /> <ContentParams env={env} />
</TabPanel> </TabPanel>
<TabPanel isActive={activeTab === 'template'}> <TabPanel isActive={activeTab === 'template'}>
<ConfigTemplateEditor config={config} onSaved={onTemplateSaved} /> <ConfigTemplateEditor config={config} onSaved={onTemplateSaved} />
</TabPanel> </TabPanel>
<TabPanel isActive={activeTab === 'raw'}> <TabPanel isActive={activeTab === 'raw'}>
<ContentRaw config={config} env={env} /> <ContentRaw config={config} env={env} />
</TabPanel> </TabPanel>
<TabPanel isActive={activeTab === 'test'}> <TabPanel isActive={activeTab === 'test'}>
<ContentTest config={config} env={env} /> <ContentTest config={config} env={env} />
</TabPanel> </TabPanel>

View File

@@ -94,10 +94,10 @@ export function Environment({ envs, onChanged, onSelected, onAdd, onRemove }: En
)); ));
return ( return (
<Card variant="bordered" padding="none" className="h-full"> <Card variant="bordered" padding="none" className="h-full overflow-hidden flex flex-col">
<CardBody className="space-y-1"> <CardBody className="space-y-2 flex flex-col h-full overflow-hidden">
{/* Environment Selector */} {/* Environment Selector */}
<div className="flex gap-2"> <div className="flex-shrink-0 flex gap-2">
<div className="flex-1"> <div className="flex-1">
<Select <Select
label="Environment" label="Environment"
@@ -112,7 +112,7 @@ export function Environment({ envs, onChanged, onSelected, onAdd, onRemove }: En
/> />
</div> </div>
<div className="flex flex-col justify-center gap-2 pt-6"> <div className="flex flex-col justify-center gap-2 pt-6 flex-shrink-0">
<div className="flex gap-2"> <div className="flex gap-2">
<Button <Button
variant="success" variant="success"
@@ -134,13 +134,13 @@ export function Environment({ envs, onChanged, onSelected, onAdd, onRemove }: En
</div> </div>
</div> </div>
{/* Parameters Section */} {/* Parameters Section - Scrollable */}
<div> <div className="flex-1 overflow-hidden flex flex-col min-h-0">
<h3 className="text-sm font-semibold text-slate-700 mb-1 uppercase tracking-wide"> <h3 className="text-sm font-semibold text-slate-700 mb-1 uppercase tracking-wide flex-shrink-0">
Parameters Parameters
</h3> </h3>
<div className="space-y-0"> <div className="flex-1 overflow-y-auto space-y-0 pr-2 -mr-2">
{paramCtrls} {paramCtrls}
<EnvironmentParam <EnvironmentParam