Skip to content

Commit 4f6ec39

Browse files
committed
update CSS, add tools and blog on AI prompt engineering
1 parent d2e03f4 commit 4f6ec39

11 files changed

Lines changed: 1309 additions & 13 deletions

File tree

207 KB
Binary file not shown.

src/components/JsonTool.astro

Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
---
2+
---
3+
4+
<div class="space-y-6">
5+
<div class="space-y-2">
6+
<label for="json-input" class="block text-sm font-medium text-zinc-700 dark:text-zinc-200">Input</label>
7+
<textarea
8+
id="json-input"
9+
name="json-input"
10+
rows={12}
11+
placeholder='Paste JSON here (or a base64 string if you want to decode)'
12+
class="w-full rounded-2xl border border-zinc-200 bg-white px-4 py-3 text-sm text-zinc-900 shadow-sm outline-none focus:border-zinc-400 dark:border-zinc-800 dark:bg-zinc-900 dark:text-zinc-50"
13+
></textarea>
14+
</div>
15+
16+
<div class="flex flex-wrap gap-3">
17+
<button
18+
id="json-pretty"
19+
type="button"
20+
class="rounded-xl border border-zinc-200 bg-white px-4 py-2 text-sm font-medium text-zinc-800 shadow-sm hover:bg-zinc-50 dark:border-zinc-800 dark:bg-zinc-950 dark:text-zinc-100 dark:hover:bg-zinc-800"
21+
>
22+
Pretty
23+
</button>
24+
<button
25+
id="json-minify"
26+
type="button"
27+
class="rounded-xl border border-zinc-200 bg-white px-4 py-2 text-sm font-medium text-zinc-800 shadow-sm hover:bg-zinc-50 dark:border-zinc-800 dark:bg-zinc-950 dark:text-zinc-100 dark:hover:bg-zinc-800"
28+
>
29+
Minify
30+
</button>
31+
<button
32+
id="json-validate"
33+
type="button"
34+
class="rounded-xl border border-zinc-200 bg-white px-4 py-2 text-sm font-medium text-zinc-800 shadow-sm hover:bg-zinc-50 dark:border-zinc-800 dark:bg-zinc-950 dark:text-zinc-100 dark:hover:bg-zinc-800"
35+
>
36+
Validate
37+
</button>
38+
<button
39+
id="json-to-base64"
40+
type="button"
41+
class="rounded-xl border border-zinc-200 bg-white px-4 py-2 text-sm font-medium text-zinc-800 shadow-sm hover:bg-zinc-50 dark:border-zinc-800 dark:bg-zinc-950 dark:text-zinc-100 dark:hover:bg-zinc-800"
42+
>
43+
To base64
44+
</button>
45+
<button
46+
id="json-from-base64"
47+
type="button"
48+
class="rounded-xl border border-zinc-200 bg-white px-4 py-2 text-sm font-medium text-zinc-800 shadow-sm hover:bg-zinc-50 dark:border-zinc-800 dark:bg-zinc-950 dark:text-zinc-100 dark:hover:bg-zinc-800"
49+
>
50+
From base64
51+
</button>
52+
<button
53+
id="json-copy"
54+
type="button"
55+
class="rounded-xl border border-zinc-200 bg-white px-4 py-2 text-sm font-medium text-zinc-800 shadow-sm hover:bg-zinc-50 dark:border-zinc-800 dark:bg-zinc-950 dark:text-zinc-100 dark:hover:bg-zinc-800"
56+
>
57+
Copy output
58+
</button>
59+
<button
60+
id="json-swap"
61+
type="button"
62+
class="rounded-xl border border-zinc-200 bg-white px-4 py-2 text-sm font-medium text-zinc-800 shadow-sm hover:bg-zinc-50 dark:border-zinc-800 dark:bg-zinc-950 dark:text-zinc-100 dark:hover:bg-zinc-800"
63+
>
64+
Replace input with output
65+
</button>
66+
<button
67+
id="json-clear"
68+
type="button"
69+
class="rounded-xl border border-zinc-200 bg-white px-4 py-2 text-sm font-medium text-zinc-800 shadow-sm hover:bg-zinc-50 dark:border-zinc-800 dark:bg-zinc-950 dark:text-zinc-100 dark:hover:bg-zinc-800"
70+
>
71+
Clear
72+
</button>
73+
<div id="json-status" class="ml-auto text-sm text-zinc-500 dark:text-zinc-400"></div>
74+
</div>
75+
76+
<div class="space-y-3">
77+
<div class="text-sm font-medium text-zinc-700 dark:text-zinc-200">Output</div>
78+
<pre class="relative overflow-auto rounded-2xl border border-zinc-200 bg-white p-4 text-sm text-zinc-900 shadow-sm dark:border-zinc-800 dark:bg-zinc-950 dark:text-zinc-100"><code id="json-output"></code></pre>
79+
</div>
80+
81+
<div id="json-error-panel" class="hidden space-y-2">
82+
<div class="text-sm font-medium text-zinc-700 dark:text-zinc-200">Error</div>
83+
<div id="json-error" class="text-sm text-zinc-600 dark:text-zinc-300"></div>
84+
</div>
85+
</div>
86+
87+
<script is:inline>
88+
(() => {
89+
const inputEl = document.getElementById('json-input');
90+
const outputEl = document.getElementById('json-output');
91+
const errorEl = document.getElementById('json-error');
92+
const prettyBtn = document.getElementById('json-pretty');
93+
const minifyBtn = document.getElementById('json-minify');
94+
const validateBtn = document.getElementById('json-validate');
95+
const toB64Btn = document.getElementById('json-to-base64');
96+
const fromB64Btn = document.getElementById('json-from-base64');
97+
const copyBtn = document.getElementById('json-copy');
98+
const swapBtn = document.getElementById('json-swap');
99+
const clearBtn = document.getElementById('json-clear');
100+
const statusEl = document.getElementById('json-status');
101+
const errorPanelEl = document.getElementById('json-error-panel');
102+
103+
if (!inputEl || !outputEl || !errorEl || !prettyBtn || !minifyBtn || !validateBtn || !toB64Btn || !fromB64Btn || !copyBtn || !swapBtn || !clearBtn || !statusEl || !errorPanelEl) return;
104+
105+
let currentOutput = '';
106+
107+
const setStatus = (text) => {
108+
statusEl.textContent = text;
109+
if (text) {
110+
setTimeout(() => {
111+
if (statusEl.textContent === text) statusEl.textContent = '';
112+
}, 1200);
113+
}
114+
};
115+
116+
const setError = (text) => {
117+
errorEl.textContent = text || '';
118+
if (text) {
119+
errorPanelEl.classList.remove('hidden');
120+
} else {
121+
errorPanelEl.classList.add('hidden');
122+
}
123+
};
124+
125+
const setOutput = (text) => {
126+
currentOutput = String(text || '');
127+
outputEl.textContent = currentOutput;
128+
};
129+
130+
const copyToClipboard = async (text) => {
131+
try {
132+
if (navigator.clipboard && navigator.clipboard.writeText) {
133+
await navigator.clipboard.writeText(text);
134+
return true;
135+
}
136+
} catch {
137+
return false;
138+
}
139+
140+
try {
141+
const el = document.createElement('textarea');
142+
el.value = text;
143+
el.setAttribute('readonly', '');
144+
el.style.position = 'fixed';
145+
el.style.left = '-9999px';
146+
document.body.appendChild(el);
147+
el.select();
148+
document.execCommand('copy');
149+
document.body.removeChild(el);
150+
return true;
151+
} catch {
152+
return false;
153+
}
154+
};
155+
156+
const parseJson = (text) => {
157+
const raw = String(text || '').trim();
158+
if (!raw) throw new Error('Input is empty');
159+
return JSON.parse(raw);
160+
};
161+
162+
const pretty = () => {
163+
try {
164+
setError('');
165+
const obj = parseJson(inputEl.value);
166+
setOutput(JSON.stringify(obj, null, 2) + '\n');
167+
setStatus('Pretty');
168+
} catch (e) {
169+
setError(e instanceof Error ? e.message : String(e));
170+
setStatus('Failed');
171+
}
172+
};
173+
174+
const minify = () => {
175+
try {
176+
setError('');
177+
const obj = parseJson(inputEl.value);
178+
setOutput(JSON.stringify(obj));
179+
setStatus('Minified');
180+
} catch (e) {
181+
setError(e instanceof Error ? e.message : String(e));
182+
setStatus('Failed');
183+
}
184+
};
185+
186+
const validate = () => {
187+
try {
188+
setError('');
189+
parseJson(inputEl.value);
190+
setStatus('Valid');
191+
} catch (e) {
192+
setError(e instanceof Error ? e.message : String(e));
193+
setStatus('Invalid');
194+
}
195+
};
196+
197+
const toBase64 = () => {
198+
try {
199+
setError('');
200+
const raw = String(inputEl.value || '');
201+
const bytes = new TextEncoder().encode(raw);
202+
let bin = '';
203+
for (const b of bytes) bin += String.fromCharCode(b);
204+
setOutput(btoa(bin));
205+
setStatus('Encoded');
206+
} catch (e) {
207+
setError(e instanceof Error ? e.message : String(e));
208+
setStatus('Failed');
209+
}
210+
};
211+
212+
const fromBase64 = () => {
213+
try {
214+
setError('');
215+
const raw = String(inputEl.value || '').trim();
216+
if (!raw) throw new Error('Input is empty');
217+
218+
const bin = atob(raw);
219+
const bytes = new Uint8Array(bin.length);
220+
for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i);
221+
const decoded = new TextDecoder().decode(bytes);
222+
223+
try {
224+
const obj = JSON.parse(decoded);
225+
setOutput(JSON.stringify(obj, null, 2) + '\n');
226+
} catch {
227+
setOutput(decoded);
228+
}
229+
230+
setStatus('Decoded');
231+
} catch (e) {
232+
setError(e instanceof Error ? e.message : String(e));
233+
setStatus('Failed');
234+
}
235+
};
236+
237+
const init = () => {
238+
setOutput('');
239+
setError('');
240+
241+
prettyBtn.addEventListener('click', pretty);
242+
minifyBtn.addEventListener('click', minify);
243+
validateBtn.addEventListener('click', validate);
244+
toB64Btn.addEventListener('click', toBase64);
245+
fromB64Btn.addEventListener('click', fromBase64);
246+
247+
copyBtn.addEventListener('click', async () => {
248+
const text = currentOutput || '';
249+
const ok = await copyToClipboard(text);
250+
setStatus(ok ? 'Copied' : 'Copy failed');
251+
});
252+
253+
swapBtn.addEventListener('click', () => {
254+
const text = currentOutput || '';
255+
if (!text) return;
256+
inputEl.value = text;
257+
setStatus('Replaced');
258+
});
259+
260+
clearBtn.addEventListener('click', () => {
261+
inputEl.value = '';
262+
setOutput('');
263+
setError('');
264+
setStatus('Cleared');
265+
inputEl.focus();
266+
});
267+
};
268+
269+
if (document.readyState === 'loading') {
270+
document.addEventListener('DOMContentLoaded', init);
271+
} else {
272+
init();
273+
}
274+
})();
275+
</script>

0 commit comments

Comments
 (0)