@jsonpages/cli 3.0.47 → 3.0.49
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/src_tenant_alpha.sh +240 -79
- package/package.json +1 -1
|
@@ -439,13 +439,14 @@ END_OF_FILE_CONTENT
|
|
|
439
439
|
echo "Creating src/components/SaveDrawer/SaveDrawer.tsx..."
|
|
440
440
|
cat << 'END_OF_FILE_CONTENT' > "src/components/SaveDrawer/SaveDrawer.tsx"
|
|
441
441
|
/**
|
|
442
|
-
* SaveDrawer — Identical to prototype DopaDrawer.
|
|
442
|
+
* SaveDrawer — Identical to prototype DopaDrawer (dopa-save).
|
|
443
443
|
* Bottom sheet for Cloud save; driven by real SSE (save-stream).
|
|
444
|
-
* Done: "
|
|
444
|
+
* Done: countdown 3s + "Chiusura in Xs" + Chiudi + Open site → auto-close. Error: Annulla + Retry.
|
|
445
445
|
*/
|
|
446
446
|
import { useState } from 'react';
|
|
447
447
|
import { cn } from '@/lib/utils';
|
|
448
448
|
import { Separator } from '@/components/ui/separator';
|
|
449
|
+
import { Progress } from '@/components/ui/progress';
|
|
449
450
|
import { DeployNode } from './DeployNode';
|
|
450
451
|
import { DeployConnector } from './DeployConnector';
|
|
451
452
|
import { Particles, BuildBars, SuccessBurst, ElapsedTimer } from './Visuals';
|
|
@@ -464,7 +465,18 @@ export interface SaveDrawerProps {
|
|
|
464
465
|
export function SaveDrawer({ cloudUrl, apiKey, state, slug, onClose }: SaveDrawerProps) {
|
|
465
466
|
const [burst, setBurst] = useState(false);
|
|
466
467
|
|
|
467
|
-
const {
|
|
468
|
+
const {
|
|
469
|
+
phase,
|
|
470
|
+
currentStep,
|
|
471
|
+
stepState,
|
|
472
|
+
doneSteps,
|
|
473
|
+
progress,
|
|
474
|
+
deployUrl,
|
|
475
|
+
countdown,
|
|
476
|
+
logs,
|
|
477
|
+
errorMessage,
|
|
478
|
+
retry,
|
|
479
|
+
} = useSaveStream({
|
|
468
480
|
cloudUrl,
|
|
469
481
|
apiKey,
|
|
470
482
|
state,
|
|
@@ -567,8 +579,12 @@ export function SaveDrawer({ cloudUrl, apiKey, state, slug, onClose }: SaveDrawe
|
|
|
567
579
|
</div>
|
|
568
580
|
) : isError ? (
|
|
569
581
|
<>
|
|
570
|
-
<p className="text-base font-semibold text-white leading-snug">
|
|
571
|
-
|
|
582
|
+
<p className="text-base font-semibold text-white leading-snug">
|
|
583
|
+
Deploy failed at build.
|
|
584
|
+
</p>
|
|
585
|
+
<p className="text-sm text-white/35 mt-0.5">
|
|
586
|
+
{errorMessage ?? 'Check your Vercel logs or retry below'}
|
|
587
|
+
</p>
|
|
572
588
|
</>
|
|
573
589
|
) : currentStep ? (
|
|
574
590
|
<>
|
|
@@ -580,7 +596,24 @@ export function SaveDrawer({ cloudUrl, apiKey, state, slug, onClose }: SaveDrawe
|
|
|
580
596
|
</div>
|
|
581
597
|
|
|
582
598
|
<div className="flex flex-col items-end gap-1.5 shrink-0 ml-6">
|
|
583
|
-
{
|
|
599
|
+
{isDone ? (
|
|
600
|
+
<div className="flex flex-col items-end gap-1.5 save-fade-up">
|
|
601
|
+
<span
|
|
602
|
+
className="font-mono text-xs text-emerald-400 font-semibold"
|
|
603
|
+
aria-live="polite"
|
|
604
|
+
>
|
|
605
|
+
Chiusura in {countdown}s
|
|
606
|
+
</span>
|
|
607
|
+
<div className="w-24 h-0.5 bg-white/10 rounded-full overflow-hidden">
|
|
608
|
+
<div
|
|
609
|
+
className="h-full bg-emerald-400 rounded-full countdown-bar"
|
|
610
|
+
style={{ boxShadow: '0 0 6px #34d39988' }}
|
|
611
|
+
/>
|
|
612
|
+
</div>
|
|
613
|
+
</div>
|
|
614
|
+
) : (
|
|
615
|
+
<ElapsedTimer running={isRunning} />
|
|
616
|
+
)}
|
|
584
617
|
</div>
|
|
585
618
|
</div>
|
|
586
619
|
|
|
@@ -608,12 +641,21 @@ export function SaveDrawer({ cloudUrl, apiKey, state, slug, onClose }: SaveDrawe
|
|
|
608
641
|
|
|
609
642
|
<Separator className="mb-5 bg-white/[0.06]" />
|
|
610
643
|
|
|
644
|
+
{isRunning && currentStep && (logs[currentStep.id]?.length ?? 0) > 0 && (
|
|
645
|
+
<div className="mb-4 max-h-16 overflow-y-auto rounded-md bg-white/[0.04] px-3 py-2 font-mono text-[11px] text-white/50 leading-relaxed">
|
|
646
|
+
{logs[currentStep.id]?.map((line, i) => (
|
|
647
|
+
<div key={i}>{line}</div>
|
|
648
|
+
))}
|
|
649
|
+
</div>
|
|
650
|
+
)}
|
|
651
|
+
|
|
611
652
|
<div className="flex items-center gap-4">
|
|
612
|
-
<div className="flex-1
|
|
613
|
-
<
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
653
|
+
<div className="flex-1">
|
|
654
|
+
<Progress
|
|
655
|
+
value={progress}
|
|
656
|
+
className="h-[2px] bg-white/[0.06]"
|
|
657
|
+
indicatorClassName="transition-none"
|
|
658
|
+
indicatorStyle={{
|
|
617
659
|
background: `linear-gradient(90deg, ${DEPLOY_STEPS[0].color}, ${activeColor})`,
|
|
618
660
|
}}
|
|
619
661
|
/>
|
|
@@ -624,17 +666,19 @@ export function SaveDrawer({ cloudUrl, apiKey, state, slug, onClose }: SaveDrawe
|
|
|
624
666
|
<div className="flex gap-2 save-fade-up">
|
|
625
667
|
<button
|
|
626
668
|
type="button"
|
|
627
|
-
onClick={
|
|
628
|
-
className="
|
|
669
|
+
onClick={onClose}
|
|
670
|
+
className="h-9 rounded-md px-3 inline-flex items-center justify-center text-sm font-medium border border-white/20 bg-white/5 text-white/90 hover:bg-white/10 transition-colors"
|
|
629
671
|
>
|
|
630
|
-
|
|
672
|
+
Chiudi
|
|
631
673
|
</button>
|
|
632
674
|
<button
|
|
633
675
|
type="button"
|
|
634
|
-
onClick={
|
|
635
|
-
|
|
676
|
+
onClick={() =>
|
|
677
|
+
deployUrl ? window.open(deployUrl, '_blank') : window.location.reload()
|
|
678
|
+
}
|
|
679
|
+
className="h-9 rounded-md px-3 inline-flex items-center justify-center gap-1.5 text-sm font-semibold bg-emerald-400 text-zinc-900 hover:bg-emerald-300 transition-colors"
|
|
636
680
|
>
|
|
637
|
-
|
|
681
|
+
<span aria-hidden>↗</span> Open site
|
|
638
682
|
</button>
|
|
639
683
|
</div>
|
|
640
684
|
)}
|
|
@@ -644,14 +688,14 @@ export function SaveDrawer({ cloudUrl, apiKey, state, slug, onClose }: SaveDrawe
|
|
|
644
688
|
<button
|
|
645
689
|
type="button"
|
|
646
690
|
onClick={onClose}
|
|
647
|
-
className="px-3
|
|
691
|
+
className="h-9 rounded-md px-3 inline-flex items-center justify-center text-sm font-medium text-white/80 hover:bg-white/10 transition-colors"
|
|
648
692
|
>
|
|
649
693
|
Annulla
|
|
650
694
|
</button>
|
|
651
695
|
<button
|
|
652
696
|
type="button"
|
|
653
697
|
onClick={retry}
|
|
654
|
-
className="
|
|
698
|
+
className="h-9 rounded-md px-3 inline-flex items-center justify-center text-sm font-medium bg-red-600 text-white hover:bg-red-500 transition-colors"
|
|
655
699
|
>
|
|
656
700
|
Retry
|
|
657
701
|
</button>
|
|
@@ -875,7 +919,9 @@ END_OF_FILE_CONTENT
|
|
|
875
919
|
echo "Creating src/components/SaveDrawer/useSaveStream.ts..."
|
|
876
920
|
cat << 'END_OF_FILE_CONTENT' > "src/components/SaveDrawer/useSaveStream.ts"
|
|
877
921
|
/**
|
|
878
|
-
* useSaveStream — Same API as prototype useSaveOrchestrator,
|
|
922
|
+
* useSaveStream — Same API as prototype useSaveOrchestrator, driven by SSE from save-stream.
|
|
923
|
+
* Event handling aligned with CreateTenantFlow (parseSSE, step/log/error/done, post-loop, try/catch).
|
|
924
|
+
* Payload is read from a ref at effect start so the effect does not depend on state.page (avoids re-run and stream kill).
|
|
879
925
|
*/
|
|
880
926
|
import { useState, useCallback, useEffect, useRef } from 'react';
|
|
881
927
|
import type { ProjectState } from '@jsonpages/core';
|
|
@@ -913,15 +959,25 @@ export function useSaveStream({
|
|
|
913
959
|
apiKey,
|
|
914
960
|
state,
|
|
915
961
|
slug,
|
|
916
|
-
onClose
|
|
962
|
+
onClose,
|
|
917
963
|
}: UseSaveStreamOptions) {
|
|
918
964
|
const [phase, setPhase] = useState<DeployPhase>('running');
|
|
919
965
|
const [doneSteps, setDoneSteps] = useState<StepId[]>([]);
|
|
920
966
|
const [activeStepId, setActiveStepId] = useState<StepId | null>('commit');
|
|
921
967
|
const [progress, setProgress] = useState(0);
|
|
968
|
+
const [deployUrl, setDeployUrl] = useState<string | null>(null);
|
|
969
|
+
const [countdown, setCountdown] = useState(3);
|
|
970
|
+
const [logs, setLogs] = useState<Record<string, string[]>>({});
|
|
971
|
+
const [errorMessage, setErrorMessage] = useState<string | null>(null);
|
|
922
972
|
const progressRef = useRef(0);
|
|
923
973
|
const [runId, setRunId] = useState(0);
|
|
924
974
|
|
|
975
|
+
const payloadRef = useRef<{ path: string; content: unknown; message: string } | null>(null);
|
|
976
|
+
|
|
977
|
+
const addLog = useCallback((stepId: string, message: string) => {
|
|
978
|
+
setLogs((prev) => ({ ...prev, [stepId]: [...(prev[stepId] || []), message] }));
|
|
979
|
+
}, []);
|
|
980
|
+
|
|
925
981
|
const stepState = useCallback(
|
|
926
982
|
(i: number): StepState => {
|
|
927
983
|
const step = DEPLOY_STEPS[i];
|
|
@@ -937,81 +993,116 @@ export function useSaveStream({
|
|
|
937
993
|
|
|
938
994
|
useEffect(() => {
|
|
939
995
|
let cancelled = false;
|
|
996
|
+
let gotDone = false;
|
|
940
997
|
setPhase('running');
|
|
941
998
|
setDoneSteps([]);
|
|
942
999
|
setActiveStepId('commit');
|
|
943
1000
|
setProgress(0);
|
|
1001
|
+
setDeployUrl(null);
|
|
1002
|
+
setCountdown(3);
|
|
1003
|
+
setLogs({});
|
|
1004
|
+
setErrorMessage(null);
|
|
944
1005
|
progressRef.current = 0;
|
|
945
1006
|
|
|
946
|
-
|
|
1007
|
+
payloadRef.current = {
|
|
1008
|
+
path: `src/data/pages/${slug}.json`,
|
|
1009
|
+
content: state.page,
|
|
1010
|
+
message: `Content update for ${slug} via Visual Editor`,
|
|
1011
|
+
};
|
|
1012
|
+
|
|
1013
|
+
const base = cloudUrl.replace(/\/$/, '');
|
|
1014
|
+
const url = base.includes('/api/v1')
|
|
1015
|
+
? `${base}/save-stream`
|
|
1016
|
+
: `${base}/api/v1/save-stream`;
|
|
947
1017
|
|
|
948
1018
|
const run = async () => {
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
'Content-Type': 'application/json',
|
|
953
|
-
Authorization: `Bearer ${apiKey}`,
|
|
954
|
-
},
|
|
955
|
-
body: JSON.stringify({
|
|
956
|
-
path: `src/data/pages/${slug}.json`,
|
|
957
|
-
content: state.page,
|
|
958
|
-
message: `Content update for ${slug} via Visual Editor`,
|
|
959
|
-
}),
|
|
960
|
-
});
|
|
1019
|
+
try {
|
|
1020
|
+
const payload = payloadRef.current;
|
|
1021
|
+
if (!payload || cancelled) return;
|
|
961
1022
|
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
1023
|
+
const res = await fetch(url, {
|
|
1024
|
+
method: 'POST',
|
|
1025
|
+
headers: {
|
|
1026
|
+
'Content-Type': 'application/json',
|
|
1027
|
+
Authorization: `Bearer ${apiKey}`,
|
|
1028
|
+
},
|
|
1029
|
+
body: JSON.stringify(payload),
|
|
1030
|
+
});
|
|
966
1031
|
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
1032
|
+
if (!res.ok || !res.body) {
|
|
1033
|
+
if (!cancelled) {
|
|
1034
|
+
setPhase('error');
|
|
1035
|
+
setErrorMessage(res.status ? `HTTP ${res.status}` : 'Request failed');
|
|
1036
|
+
}
|
|
1037
|
+
return;
|
|
1038
|
+
}
|
|
970
1039
|
|
|
971
|
-
|
|
972
|
-
const
|
|
973
|
-
|
|
974
|
-
buffer += dec.decode(value, { stream: true });
|
|
975
|
-
const parts = buffer.split('\n\n');
|
|
976
|
-
buffer = parts.pop() || '';
|
|
1040
|
+
const reader = res.body.getReader();
|
|
1041
|
+
const dec = new TextDecoder();
|
|
1042
|
+
let buffer = '';
|
|
977
1043
|
|
|
978
|
-
|
|
979
|
-
const
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
1044
|
+
while (true) {
|
|
1045
|
+
const { value, done: streamDone } = await reader.read();
|
|
1046
|
+
if (streamDone || cancelled) break;
|
|
1047
|
+
buffer += dec.decode(value, { stream: true });
|
|
1048
|
+
const parts = buffer.split('\n\n');
|
|
1049
|
+
buffer = parts.pop() || '';
|
|
1050
|
+
|
|
1051
|
+
for (const part of parts) {
|
|
1052
|
+
const parsed = parseSSE(part + '\n\n');
|
|
1053
|
+
for (const { event: ev, data: raw } of parsed) {
|
|
1054
|
+
if (cancelled) return;
|
|
1055
|
+
try {
|
|
1056
|
+
const data = JSON.parse(raw) as Record<string, unknown>;
|
|
1057
|
+
if (ev === 'step') {
|
|
1058
|
+
const id = data.id as StepId | undefined;
|
|
1059
|
+
const status = data.status as string | undefined;
|
|
1060
|
+
if (id && status === 'running') {
|
|
1061
|
+
setActiveStepId(id);
|
|
1062
|
+
const idx = DEPLOY_STEPS.findIndex((s) => s.id === id);
|
|
1063
|
+
if (idx >= 0) progressRef.current = Math.round((idx / DEPLOY_STEPS.length) * 100);
|
|
1064
|
+
setProgress(progressRef.current);
|
|
1065
|
+
}
|
|
1066
|
+
if (id && status === 'done') {
|
|
1067
|
+
setDoneSteps((prev) => [...prev, id].filter(Boolean));
|
|
1068
|
+
setActiveStepId(null);
|
|
1069
|
+
const idx = DEPLOY_STEPS.findIndex((s) => s.id === id);
|
|
1070
|
+
progressRef.current = idx >= 0 ? Math.round(((idx + 1) / DEPLOY_STEPS.length) * 100) : 100;
|
|
1071
|
+
setProgress(progressRef.current);
|
|
1072
|
+
}
|
|
1073
|
+
} else if (ev === 'log' && data.stepId && data.message) {
|
|
1074
|
+
addLog(data.stepId as string, data.message as string);
|
|
1075
|
+
} else if (ev === 'error') {
|
|
1076
|
+
if (!cancelled) {
|
|
1077
|
+
setErrorMessage((data.message as string) || 'Errore');
|
|
1078
|
+
setPhase('error');
|
|
1079
|
+
}
|
|
1080
|
+
return;
|
|
1081
|
+
} else if (ev === 'done') {
|
|
1082
|
+
gotDone = true;
|
|
1083
|
+
setDeployUrl((data.deployUrl as string) ?? null);
|
|
1084
|
+
setProgress(100);
|
|
1085
|
+
setDoneSteps(['commit', 'push', 'build', 'live']);
|
|
995
1086
|
setActiveStepId(null);
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
setProgress(progressRef.current);
|
|
1087
|
+
setPhase('done');
|
|
1088
|
+
return;
|
|
999
1089
|
}
|
|
1000
|
-
}
|
|
1001
|
-
|
|
1002
|
-
return;
|
|
1003
|
-
} else if (ev === 'done') {
|
|
1004
|
-
setProgress(100);
|
|
1005
|
-
setDoneSteps(['commit', 'push', 'build', 'live']);
|
|
1006
|
-
setActiveStepId(null);
|
|
1007
|
-
setPhase('done');
|
|
1008
|
-
return;
|
|
1090
|
+
} catch {
|
|
1091
|
+
// ignore parse errors
|
|
1009
1092
|
}
|
|
1010
|
-
} catch {
|
|
1011
|
-
// ignore
|
|
1012
1093
|
}
|
|
1013
1094
|
}
|
|
1014
1095
|
}
|
|
1096
|
+
|
|
1097
|
+
if (!cancelled && !gotDone) {
|
|
1098
|
+
setErrorMessage('Operazione interrotta o build non completato.');
|
|
1099
|
+
setPhase('error');
|
|
1100
|
+
}
|
|
1101
|
+
} catch (e) {
|
|
1102
|
+
if (!cancelled) {
|
|
1103
|
+
setErrorMessage(e instanceof Error ? e.message : 'Errore');
|
|
1104
|
+
setPhase('error');
|
|
1105
|
+
}
|
|
1015
1106
|
}
|
|
1016
1107
|
};
|
|
1017
1108
|
|
|
@@ -1019,7 +1110,23 @@ export function useSaveStream({
|
|
|
1019
1110
|
return () => {
|
|
1020
1111
|
cancelled = true;
|
|
1021
1112
|
};
|
|
1022
|
-
}, [cloudUrl, apiKey, slug,
|
|
1113
|
+
}, [cloudUrl, apiKey, slug, runId, addLog]);
|
|
1114
|
+
|
|
1115
|
+
useEffect(() => {
|
|
1116
|
+
if (phase !== 'done') return;
|
|
1117
|
+
setCountdown(3);
|
|
1118
|
+
const iv = setInterval(() => {
|
|
1119
|
+
setCountdown((s) => {
|
|
1120
|
+
if (s <= 1) {
|
|
1121
|
+
clearInterval(iv);
|
|
1122
|
+
onClose();
|
|
1123
|
+
return 0;
|
|
1124
|
+
}
|
|
1125
|
+
return s - 1;
|
|
1126
|
+
});
|
|
1127
|
+
}, 1000);
|
|
1128
|
+
return () => clearInterval(iv);
|
|
1129
|
+
}, [phase, onClose]);
|
|
1023
1130
|
|
|
1024
1131
|
const retry = useCallback(() => setRunId((r) => r + 1), []);
|
|
1025
1132
|
|
|
@@ -1029,6 +1136,10 @@ export function useSaveStream({
|
|
|
1029
1136
|
stepState,
|
|
1030
1137
|
doneSteps,
|
|
1031
1138
|
progress,
|
|
1139
|
+
deployUrl,
|
|
1140
|
+
countdown,
|
|
1141
|
+
logs,
|
|
1142
|
+
errorMessage,
|
|
1032
1143
|
retry,
|
|
1033
1144
|
};
|
|
1034
1145
|
}
|
|
@@ -3871,6 +3982,47 @@ export { Label }
|
|
|
3871
3982
|
|
|
3872
3983
|
|
|
3873
3984
|
|
|
3985
|
+
END_OF_FILE_CONTENT
|
|
3986
|
+
echo "Creating src/components/ui/progress.tsx..."
|
|
3987
|
+
cat << 'END_OF_FILE_CONTENT' > "src/components/ui/progress.tsx"
|
|
3988
|
+
import * as React from 'react';
|
|
3989
|
+
import { cn } from '@/lib/utils';
|
|
3990
|
+
|
|
3991
|
+
interface ProgressProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> {
|
|
3992
|
+
value?: number | null;
|
|
3993
|
+
indicatorClassName?: string;
|
|
3994
|
+
indicatorStyle?: React.CSSProperties;
|
|
3995
|
+
}
|
|
3996
|
+
|
|
3997
|
+
const Progress = React.forwardRef<HTMLDivElement, ProgressProps>(
|
|
3998
|
+
({ className, value, indicatorClassName, indicatorStyle, ...props }, ref) => (
|
|
3999
|
+
<div
|
|
4000
|
+
ref={ref}
|
|
4001
|
+
role="progressbar"
|
|
4002
|
+
aria-valuenow={value ?? 0}
|
|
4003
|
+
aria-valuemin={0}
|
|
4004
|
+
aria-valuemax={100}
|
|
4005
|
+
className={cn('relative h-2 w-full overflow-hidden rounded-full bg-secondary', className)}
|
|
4006
|
+
{...props}
|
|
4007
|
+
>
|
|
4008
|
+
<div
|
|
4009
|
+
className={cn(
|
|
4010
|
+
'h-full min-w-full bg-primary transition-all duration-700 ease-out',
|
|
4011
|
+
indicatorClassName
|
|
4012
|
+
)}
|
|
4013
|
+
style={{
|
|
4014
|
+
width: '100%',
|
|
4015
|
+
transform: `translateX(-${100 - (value ?? 0)}%)`,
|
|
4016
|
+
...indicatorStyle,
|
|
4017
|
+
}}
|
|
4018
|
+
/>
|
|
4019
|
+
</div>
|
|
4020
|
+
)
|
|
4021
|
+
);
|
|
4022
|
+
Progress.displayName = 'Progress';
|
|
4023
|
+
|
|
4024
|
+
export { Progress };
|
|
4025
|
+
|
|
3874
4026
|
END_OF_FILE_CONTENT
|
|
3875
4027
|
echo "Creating src/components/ui/select.tsx..."
|
|
3876
4028
|
cat << 'END_OF_FILE_CONTENT' > "src/components/ui/select.tsx"
|
|
@@ -5340,6 +5492,15 @@ html {
|
|
|
5340
5492
|
to { stroke-dashoffset: 0; }
|
|
5341
5493
|
}
|
|
5342
5494
|
|
|
5495
|
+
/* Countdown drain — 3s linear (dopa-save identical) */
|
|
5496
|
+
.countdown-bar {
|
|
5497
|
+
animation: countdown-drain 3s linear forwards;
|
|
5498
|
+
}
|
|
5499
|
+
@keyframes countdown-drain {
|
|
5500
|
+
from { width: 100%; }
|
|
5501
|
+
to { width: 0%; }
|
|
5502
|
+
}
|
|
5503
|
+
|
|
5343
5504
|
|
|
5344
5505
|
END_OF_FILE_CONTENT
|
|
5345
5506
|
mkdir -p "src/lib"
|