json-toon-parser 1.0.1 → 1.1.0

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/README.md CHANGED
@@ -7,8 +7,20 @@
7
7
  npm install -g json-toon-parser
8
8
  ```
9
9
 
10
+ ## Quick Start - Web UI
11
+ After installation, start the web server:
12
+ ```bash
13
+ json-toon serve
14
+ ```
15
+ Then open your browser to `http://localhost:3000` and start converting JSON to TOON with a beautiful interface!
16
+
10
17
  ## CLI Usage
11
18
  ```bash
19
+ # Start web UI server (recommended for easy usage)
20
+ json-toon serve
21
+ # Or with custom port:
22
+ json-toon serve --port 8080
23
+
12
24
  # Compare JSON with TOON format
13
25
  json-toon compare data.json
14
26
 
@@ -38,12 +50,14 @@ const toonString = jsonToToon({ hello: "world" });
38
50
 
39
51
  ## Features
40
52
 
53
+ - 🌐 **Web UI** - Beautiful web interface for easy conversion
41
54
  - ✅ Convert JSON to TOON format
42
55
  - 📊 Compare token efficiency between formats
43
56
  - 💾 Auto-save TOON files with timestamps
44
57
  - 🔧 CLI tool for easy usage
45
58
  - 📦 Programmatic API for Node.js
46
59
  - 🎯 TypeScript support with full type definitions
60
+ - ⚡ Real-time conversion with live stats
47
61
 
48
62
  ## API
49
63
 
@@ -103,9 +117,3 @@ Output File: output/data_2024-01-15T10-30-45.toon
103
117
  ## License
104
118
 
105
119
  MIT © Bhushan Chaudhari
106
-
107
- ## Links
108
-
109
- - [GitHub Repository](https://github.com/bhushan44/json-toon-parser)
110
- - [Report Issues](https://github.com/bhushan44/json-toon-parser/issues)
111
- - [NPM Package](https://www.npmjs.com/package/json-toon-parser)
package/dist/cli.js CHANGED
@@ -1,11 +1,16 @@
1
1
  #!/usr/bin/env node
2
2
  import { Command } from "commander";
3
3
  import { compareFile, convertFile, formatResult, updateJsonFile } from "./index.js";
4
+ import { spawn } from "node:child_process";
5
+ import { fileURLToPath } from "node:url";
6
+ import path from "node:path";
7
+ const __filename = fileURLToPath(import.meta.url);
8
+ const __dirname = path.dirname(__filename);
4
9
  const program = new Command();
5
10
  program
6
11
  .name("json-toon")
7
12
  .description("Compare and convert JSON to TOON format")
8
- .version("1.0.0");
13
+ .version("1.0.1");
9
14
  program
10
15
  .command("compare")
11
16
  .description("Compare JSON file with TOON format")
@@ -58,5 +63,21 @@ program
58
63
  process.exit(1);
59
64
  }
60
65
  });
66
+ program
67
+ .command("serve")
68
+ .description("Start web UI server for JSON to TOON conversion")
69
+ .option("-p, --port <port>", "Port number", "3000")
70
+ .action((options) => {
71
+ const serverPath = path.join(__dirname, "server.js");
72
+ const env = { ...process.env, PORT: options.port };
73
+ const serverProcess = spawn("node", [serverPath], {
74
+ env,
75
+ stdio: "inherit",
76
+ });
77
+ serverProcess.on("error", (error) => {
78
+ console.error("Failed to start server:", error.message);
79
+ process.exit(1);
80
+ });
81
+ });
61
82
  program.parse();
62
83
  //# sourceMappingURL=cli.js.map
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":""}
package/dist/server.js ADDED
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/env node
2
+ import http from "node:http";
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ const __filename = fileURLToPath(import.meta.url);
7
+ const __dirname = path.dirname(__filename);
8
+ const PORT = process.env.PORT || 3000;
9
+ const server = http.createServer((req, res) => {
10
+ // Set CORS headers
11
+ res.setHeader("Access-Control-Allow-Origin", "*");
12
+ res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
13
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type");
14
+ if (req.method === "OPTIONS") {
15
+ res.writeHead(200);
16
+ res.end();
17
+ return;
18
+ }
19
+ if (req.url === "/" || req.url === "/index.html") {
20
+ // Serve the HTML file
21
+ const htmlPath = path.join(__dirname, "..", "public", "index.html");
22
+ if (!fs.existsSync(htmlPath)) {
23
+ res.writeHead(404, { "Content-Type": "text/plain" });
24
+ res.end("HTML file not found");
25
+ return;
26
+ }
27
+ const html = fs.readFileSync(htmlPath, "utf-8");
28
+ res.writeHead(200, { "Content-Type": "text/html" });
29
+ res.end(html);
30
+ }
31
+ else if (req.url === "/api/convert" && req.method === "POST") {
32
+ // API endpoint for conversion
33
+ let body = "";
34
+ req.on("data", (chunk) => {
35
+ body += chunk.toString();
36
+ });
37
+ req.on("end", async () => {
38
+ try {
39
+ const { encode } = await import("@toon-format/toon");
40
+ const { encode: tokenize } = await import("gpt-3-encoder");
41
+ const jsonData = JSON.parse(body);
42
+ const toonOutput = encode(jsonData);
43
+ const jsonString = JSON.stringify(jsonData, null, 2);
44
+ const jsonTokens = tokenize(jsonString).length;
45
+ const toonTokens = tokenize(toonOutput).length;
46
+ const savings = jsonTokens - toonTokens;
47
+ const savingsPercentage = ((savings / jsonTokens) * 100).toFixed(2);
48
+ res.writeHead(200, { "Content-Type": "application/json" });
49
+ res.end(JSON.stringify({
50
+ success: true,
51
+ toonOutput,
52
+ jsonTokens,
53
+ toonTokens,
54
+ savings,
55
+ savingsPercentage,
56
+ }));
57
+ }
58
+ catch (error) {
59
+ res.writeHead(400, { "Content-Type": "application/json" });
60
+ res.end(JSON.stringify({
61
+ success: false,
62
+ error: error instanceof Error ? error.message : "Invalid JSON",
63
+ }));
64
+ }
65
+ });
66
+ }
67
+ else {
68
+ res.writeHead(404, { "Content-Type": "text/plain" });
69
+ res.end("Not Found");
70
+ }
71
+ });
72
+ server.listen(PORT, () => {
73
+ console.log(`
74
+ ╔═══════════════════════════════════════════════════════════╗
75
+ ║ ║
76
+ ║ 🚀 JSON-TOON Converter Server ║
77
+ ║ ║
78
+ ║ Server running at: http://localhost:${PORT} ║
79
+ ║ ║
80
+ ║ 📝 Open your browser and start converting! ║
81
+ ║ ⚡ Real-time JSON to TOON conversion ║
82
+ ║ 💾 Token efficiency analysis ║
83
+ ║ ║
84
+ ║ Press Ctrl+C to stop the server ║
85
+ ║ ║
86
+ ╚═══════════════════════════════════════════════════════════╝
87
+ `);
88
+ });
89
+ process.on("SIGINT", () => {
90
+ console.log("\n\n👋 Shutting down server...");
91
+ server.close(() => {
92
+ console.log("✅ Server stopped");
93
+ process.exit(0);
94
+ });
95
+ });
96
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "json-toon-parser",
3
- "version": "1.0.1",
4
- "description": "Compare and convert JSON to TOON format with token efficiency analysis",
3
+ "version": "1.1.0",
4
+ "description": "Compare and convert JSON to TOON format with token efficiency analysis - Now with Web UI!",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
7
7
  "bin": {
@@ -10,12 +10,15 @@
10
10
  "type": "module",
11
11
  "scripts": {
12
12
  "build": "tsc",
13
- "start": "npm run build && node dist/cli.js update data/data.json",
13
+ "start": "npm run build && node dist/server.js",
14
+ "serve": "npm run build && node dist/server.js",
15
+ "dev": "tsc && node dist/server.js",
14
16
  "prepublishOnly": "npm run build",
15
17
  "test": "echo \"No tests specified\" && exit 0"
16
18
  },
17
19
  "files": [
18
20
  "dist",
21
+ "public",
19
22
  "README.md",
20
23
  "LICENSE"
21
24
  ],
@@ -35,8 +38,10 @@
35
38
  "license": "MIT",
36
39
  "dependencies": {
37
40
  "@toon-format/toon": "^1.0.0",
41
+ "1.0.1": "^1.0.0",
42
+ "commander": "^12.0.0",
38
43
  "gpt-3-encoder": "^1.1.4",
39
- "commander": "^12.0.0"
44
+ "json-toon-parser": "^1.0.1"
40
45
  },
41
46
  "devDependencies": {
42
47
  "@types/node": "^20.0.0",
@@ -0,0 +1,706 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>JSON to TOON Converter - Live Tool</title>
7
+ <style>
8
+ * {
9
+ margin: 0;
10
+ padding: 0;
11
+ box-sizing: border-box;
12
+ }
13
+
14
+ body {
15
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
16
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
17
+ min-height: 100vh;
18
+ padding: 20px;
19
+ }
20
+
21
+ .container {
22
+ max-width: 1400px;
23
+ margin: 0 auto;
24
+ background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
25
+ border-radius: 24px;
26
+ box-shadow: 0 30px 60px rgba(0,0,0,0.5);
27
+ overflow: hidden;
28
+ }
29
+
30
+ /* Header */
31
+ .header {
32
+ padding: 40px;
33
+ border-bottom: 1px solid rgba(255,255,255,0.1);
34
+ background: rgba(255,255,255,0.02);
35
+ }
36
+
37
+ .header-content {
38
+ display: flex;
39
+ justify-content: space-between;
40
+ align-items: center;
41
+ flex-wrap: wrap;
42
+ gap: 20px;
43
+ }
44
+
45
+ .brand {
46
+ display: flex;
47
+ align-items: center;
48
+ gap: 16px;
49
+ }
50
+
51
+ .logo {
52
+ width: 50px;
53
+ height: 50px;
54
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
55
+ border-radius: 12px;
56
+ display: flex;
57
+ align-items: center;
58
+ justify-content: center;
59
+ font-size: 24px;
60
+ box-shadow: 0 10px 30px rgba(102, 126, 234, 0.4);
61
+ }
62
+
63
+ .brand-text h1 {
64
+ font-size: 28px;
65
+ font-weight: 800;
66
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
67
+ -webkit-background-clip: text;
68
+ -webkit-text-fill-color: transparent;
69
+ background-clip: text;
70
+ }
71
+
72
+ .brand-text p {
73
+ font-size: 14px;
74
+ color: #94a3b8;
75
+ margin-top: 4px;
76
+ }
77
+
78
+ .actions {
79
+ display: flex;
80
+ gap: 12px;
81
+ }
82
+
83
+ .btn {
84
+ padding: 12px 24px;
85
+ border-radius: 10px;
86
+ font-size: 14px;
87
+ font-weight: 600;
88
+ cursor: pointer;
89
+ border: none;
90
+ transition: all 0.3s ease;
91
+ display: flex;
92
+ align-items: center;
93
+ gap: 8px;
94
+ }
95
+
96
+ .btn-primary {
97
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
98
+ color: white;
99
+ box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
100
+ }
101
+
102
+ .btn-primary:hover {
103
+ transform: translateY(-2px);
104
+ box-shadow: 0 6px 20px rgba(102, 126, 234, 0.6);
105
+ }
106
+
107
+ .btn-secondary {
108
+ background: rgba(255,255,255,0.1);
109
+ color: #e2e8f0;
110
+ border: 1px solid rgba(255,255,255,0.2);
111
+ }
112
+
113
+ .btn-secondary:hover {
114
+ background: rgba(255,255,255,0.15);
115
+ }
116
+
117
+ /* Main Content */
118
+ .main-content {
119
+ padding: 40px;
120
+ }
121
+
122
+ /* Editor Section */
123
+ .editor-section {
124
+ display: grid;
125
+ grid-template-columns: 1fr 1fr;
126
+ gap: 20px;
127
+ margin-bottom: 30px;
128
+ }
129
+
130
+ .editor-panel {
131
+ background: rgba(255,255,255,0.03);
132
+ border: 1px solid rgba(255,255,255,0.1);
133
+ border-radius: 16px;
134
+ overflow: hidden;
135
+ }
136
+
137
+ .panel-header {
138
+ padding: 20px;
139
+ background: rgba(255,255,255,0.05);
140
+ border-bottom: 1px solid rgba(255,255,255,0.1);
141
+ display: flex;
142
+ justify-content: space-between;
143
+ align-items: center;
144
+ }
145
+
146
+ .panel-title {
147
+ display: flex;
148
+ align-items: center;
149
+ gap: 10px;
150
+ font-size: 16px;
151
+ font-weight: 700;
152
+ color: #ffffff;
153
+ }
154
+
155
+ .title-icon {
156
+ font-size: 20px;
157
+ }
158
+
159
+ .panel-actions {
160
+ display: flex;
161
+ gap: 8px;
162
+ }
163
+
164
+ .icon-btn {
165
+ width: 32px;
166
+ height: 32px;
167
+ border-radius: 8px;
168
+ background: rgba(255,255,255,0.1);
169
+ border: none;
170
+ color: #94a3b8;
171
+ cursor: pointer;
172
+ display: flex;
173
+ align-items: center;
174
+ justify-content: center;
175
+ transition: all 0.3s ease;
176
+ }
177
+
178
+ .icon-btn:hover {
179
+ background: rgba(255,255,255,0.15);
180
+ color: #e2e8f0;
181
+ }
182
+
183
+ .panel-content {
184
+ padding: 0;
185
+ }
186
+
187
+ textarea {
188
+ width: 100%;
189
+ height: 400px;
190
+ background: rgba(0,0,0,0.3);
191
+ border: none;
192
+ color: #e2e8f0;
193
+ padding: 20px;
194
+ font-family: 'Monaco', 'Courier New', monospace;
195
+ font-size: 14px;
196
+ line-height: 1.6;
197
+ resize: vertical;
198
+ outline: none;
199
+ }
200
+
201
+ textarea::placeholder {
202
+ color: #64748b;
203
+ }
204
+
205
+ /* Stats Section */
206
+ .stats-section {
207
+ background: rgba(255,255,255,0.03);
208
+ border: 1px solid rgba(255,255,255,0.1);
209
+ border-radius: 16px;
210
+ padding: 30px;
211
+ margin-bottom: 20px;
212
+ }
213
+
214
+ .stats-grid {
215
+ display: grid;
216
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
217
+ gap: 20px;
218
+ }
219
+
220
+ .stat-card {
221
+ background: rgba(255,255,255,0.05);
222
+ border: 1px solid rgba(255,255,255,0.1);
223
+ border-radius: 12px;
224
+ padding: 24px;
225
+ text-align: center;
226
+ transition: all 0.3s ease;
227
+ }
228
+
229
+ .stat-card:hover {
230
+ background: rgba(255,255,255,0.08);
231
+ transform: translateY(-2px);
232
+ }
233
+
234
+ .stat-icon {
235
+ font-size: 32px;
236
+ margin-bottom: 12px;
237
+ }
238
+
239
+ .stat-value {
240
+ font-size: 36px;
241
+ font-weight: 800;
242
+ color: #ffffff;
243
+ margin-bottom: 8px;
244
+ }
245
+
246
+ .stat-label {
247
+ font-size: 13px;
248
+ color: #94a3b8;
249
+ text-transform: uppercase;
250
+ letter-spacing: 1px;
251
+ }
252
+
253
+ .stat-card.highlight {
254
+ background: linear-gradient(135deg, rgba(16, 185, 129, 0.2) 0%, rgba(5, 150, 105, 0.2) 100%);
255
+ border-color: rgba(16, 185, 129, 0.3);
256
+ }
257
+
258
+ .stat-card.highlight .stat-value {
259
+ color: #10b981;
260
+ }
261
+
262
+ /* Error/Success Messages */
263
+ .message {
264
+ padding: 16px 20px;
265
+ border-radius: 12px;
266
+ margin-bottom: 20px;
267
+ display: none;
268
+ align-items: center;
269
+ gap: 12px;
270
+ font-size: 14px;
271
+ }
272
+
273
+ .message.show {
274
+ display: flex;
275
+ }
276
+
277
+ .message-error {
278
+ background: rgba(239, 68, 68, 0.2);
279
+ border: 1px solid rgba(239, 68, 68, 0.3);
280
+ color: #fca5a5;
281
+ }
282
+
283
+ .message-success {
284
+ background: rgba(16, 185, 129, 0.2);
285
+ border: 1px solid rgba(16, 185, 129, 0.3);
286
+ color: #6ee7b7;
287
+ }
288
+
289
+ /* Sample Data Section */
290
+ .samples {
291
+ display: flex;
292
+ gap: 12px;
293
+ flex-wrap: wrap;
294
+ margin-bottom: 20px;
295
+ }
296
+
297
+ .sample-btn {
298
+ padding: 10px 16px;
299
+ border-radius: 8px;
300
+ background: rgba(255,255,255,0.05);
301
+ border: 1px solid rgba(255,255,255,0.1);
302
+ color: #94a3b8;
303
+ font-size: 13px;
304
+ cursor: pointer;
305
+ transition: all 0.3s ease;
306
+ }
307
+
308
+ .sample-btn:hover {
309
+ background: rgba(255,255,255,0.1);
310
+ border-color: rgba(102, 126, 234, 0.5);
311
+ color: #667eea;
312
+ }
313
+
314
+ /* Footer */
315
+ .footer {
316
+ padding: 30px 40px;
317
+ border-top: 1px solid rgba(255,255,255,0.1);
318
+ text-align: center;
319
+ color: #64748b;
320
+ font-size: 13px;
321
+ }
322
+
323
+ .footer-links {
324
+ display: flex;
325
+ justify-content: center;
326
+ gap: 24px;
327
+ margin-top: 16px;
328
+ }
329
+
330
+ .footer-link {
331
+ color: #94a3b8;
332
+ text-decoration: none;
333
+ transition: color 0.3s ease;
334
+ }
335
+
336
+ .footer-link:hover {
337
+ color: #667eea;
338
+ }
339
+
340
+ /* Responsive */
341
+ @media (max-width: 968px) {
342
+ .editor-section {
343
+ grid-template-columns: 1fr;
344
+ }
345
+
346
+ .stats-grid {
347
+ grid-template-columns: repeat(2, 1fr);
348
+ }
349
+ }
350
+
351
+ @media (max-width: 640px) {
352
+ .header-content {
353
+ flex-direction: column;
354
+ align-items: flex-start;
355
+ }
356
+
357
+ .stats-grid {
358
+ grid-template-columns: 1fr;
359
+ }
360
+ }
361
+ </style>
362
+ </head>
363
+ <body>
364
+ <div class="container">
365
+ <!-- Header -->
366
+ <div class="header">
367
+ <div class="header-content">
368
+ <div class="brand">
369
+ <div class="logo">📦</div>
370
+ <div class="brand-text">
371
+ <h1>JSON to TOON Converter</h1>
372
+ <p>Optimize your LLM token usage in real-time</p>
373
+ </div>
374
+ </div>
375
+ <div class="actions">
376
+ <button class="btn btn-primary" onclick="convertJson()">
377
+ <span>⚡</span>
378
+ <span>Convert</span>
379
+ </button>
380
+ <button class="btn btn-secondary" onclick="clearAll()">
381
+ <span>🔄</span>
382
+ <span>Clear</span>
383
+ </button>
384
+ </div>
385
+ </div>
386
+ </div>
387
+
388
+ <!-- Main Content -->
389
+ <div class="main-content">
390
+ <!-- Messages -->
391
+ <div id="errorMessage" class="message message-error">
392
+ <span>⚠️</span>
393
+ <span id="errorText"></span>
394
+ </div>
395
+
396
+ <div id="successMessage" class="message message-success">
397
+ <span>✅</span>
398
+ <span>Conversion successful!</span>
399
+ </div>
400
+
401
+ <!-- Sample Data -->
402
+ <div class="samples">
403
+ <span style="color: #94a3b8; font-size: 13px; font-weight: 600; margin-right: 8px;">Try samples:</span>
404
+ <button class="sample-btn" onclick="loadSample('simple')">Simple Object</button>
405
+ <button class="sample-btn" onclick="loadSample('array')">Array Data</button>
406
+ <button class="sample-btn" onclick="loadSample('nested')">Nested Structure</button>
407
+ <button class="sample-btn" onclick="loadSample('complex')">Complex Data</button>
408
+ </div>
409
+
410
+ <!-- Editor Section -->
411
+ <div class="editor-section">
412
+ <!-- JSON Input -->
413
+ <div class="editor-panel">
414
+ <div class="panel-header">
415
+ <div class="panel-title">
416
+ <span class="title-icon">{ }</span>
417
+ <span>JSON Input</span>
418
+ </div>
419
+ <div class="panel-actions">
420
+ <button class="icon-btn" onclick="copyJson()" title="Copy">📋</button>
421
+ <button class="icon-btn" onclick="formatJson()" title="Format">✨</button>
422
+ </div>
423
+ </div>
424
+ <div class="panel-content">
425
+ <textarea id="jsonInput" placeholder='Paste your JSON here...'></textarea>
426
+ </div>
427
+ </div>
428
+
429
+ <!-- TOON Output -->
430
+ <div class="editor-panel">
431
+ <div class="panel-header">
432
+ <div class="panel-title">
433
+ <span class="title-icon">⚡</span>
434
+ <span>TOON Output</span>
435
+ </div>
436
+ <div class="panel-actions">
437
+ <button class="icon-btn" onclick="copyToon()" title="Copy">📋</button>
438
+ <button class="icon-btn" onclick="downloadToon()" title="Download">💾</button>
439
+ </div>
440
+ </div>
441
+ <div class="panel-content">
442
+ <textarea id="toonOutput" placeholder="TOON output will appear here..." readonly></textarea>
443
+ </div>
444
+ </div>
445
+ </div>
446
+
447
+ <!-- Stats Section -->
448
+ <div class="stats-section">
449
+ <div class="stats-grid">
450
+ <div class="stat-card">
451
+ <div class="stat-icon">📄</div>
452
+ <div class="stat-value" id="jsonTokens">0</div>
453
+ <div class="stat-label">JSON Tokens</div>
454
+ </div>
455
+
456
+ <div class="stat-card">
457
+ <div class="stat-icon">⚡</div>
458
+ <div class="stat-value" id="toonTokens">0</div>
459
+ <div class="stat-label">TOON Tokens</div>
460
+ </div>
461
+
462
+ <div class="stat-card highlight">
463
+ <div class="stat-icon">💰</div>
464
+ <div class="stat-value" id="tokenSavings">0</div>
465
+ <div class="stat-label">Tokens Saved</div>
466
+ </div>
467
+
468
+ <div class="stat-card highlight">
469
+ <div class="stat-icon">📊</div>
470
+ <div class="stat-value" id="savingsPercent">0%</div>
471
+ <div class="stat-label">Efficiency Gain</div>
472
+ </div>
473
+ </div>
474
+ </div>
475
+ </div>
476
+
477
+ <!-- Footer -->
478
+ <div class="footer">
479
+ <p>Made with ❤️ using json-toon-parser</p>
480
+ <div class="footer-links">
481
+ <a href="https://www.npmjs.com/package/json-toon-parser" class="footer-link" target="_blank">📦 NPM Package</a>
482
+ <a href="https://github.com/bhushan44/json-toon-parser" class="footer-link" target="_blank">⭐ GitHub</a>
483
+ <a href="#" class="footer-link">📖 Documentation</a>
484
+ </div>
485
+ </div>
486
+ </div>
487
+
488
+ <!-- Include TOON library from CDN -->
489
+ <script type="module">
490
+ // Import TOON encoder
491
+ import { encode } from 'https://cdn.jsdelivr.net/npm/@toon-format/toon@1.0.0/+esm';
492
+
493
+ // Make encode available globally
494
+ window.toonEncode = encode;
495
+
496
+ // Simple tokenizer (GPT-3 approximation)
497
+ // This is a simplified version - for exact counts, you'd need the actual GPT-3 encoder
498
+ function countTokens(text) {
499
+ // Approximate token count: ~4 characters per token on average
500
+ // This is a rough estimate - actual GPT-3 tokenization is more complex
501
+ const words = text.split(/\s+/).length;
502
+ const chars = text.length;
503
+ const punctuation = (text.match(/[.,;:!?(){}[\]"']/g) || []).length;
504
+
505
+ // Better approximation
506
+ return Math.ceil((chars / 4) + (punctuation * 0.5));
507
+ }
508
+
509
+ window.countTokens = countTokens;
510
+
511
+ // Sample data
512
+ window.samples = {
513
+ simple: {
514
+ name: "John Doe",
515
+ age: 30,
516
+ email: "john@example.com",
517
+ active: true
518
+ },
519
+ array: {
520
+ users: [
521
+ { id: 1, name: "Alice", role: "admin" },
522
+ { id: 2, name: "Bob", role: "user" },
523
+ { id: 3, name: "Charlie", role: "moderator" }
524
+ ]
525
+ },
526
+ nested: {
527
+ company: {
528
+ name: "Tech Corp",
529
+ employees: [
530
+ {
531
+ id: 1,
532
+ name: "Alice",
533
+ department: {
534
+ id: 10,
535
+ name: "Engineering",
536
+ location: "Building A"
537
+ }
538
+ }
539
+ ],
540
+ founded: 2020
541
+ }
542
+ },
543
+ complex: {
544
+ product: {
545
+ id: "prod_123",
546
+ name: "Awesome Product",
547
+ price: 99.99,
548
+ categories: ["electronics", "gadgets"],
549
+ specs: {
550
+ weight: "500g",
551
+ dimensions: { width: 10, height: 5, depth: 2 },
552
+ colors: ["black", "white", "blue"]
553
+ },
554
+ reviews: [
555
+ { user: "Alice", rating: 5, comment: "Great!" },
556
+ { user: "Bob", rating: 4, comment: "Good value" }
557
+ ],
558
+ inStock: true,
559
+ metadata: {
560
+ created: "2024-01-15",
561
+ updated: "2024-01-20",
562
+ tags: ["featured", "bestseller"]
563
+ }
564
+ }
565
+ }
566
+ };
567
+
568
+ console.log('TOON converter loaded successfully!');
569
+ </script>
570
+
571
+ <script>
572
+ function showError(message) {
573
+ const errorEl = document.getElementById('errorMessage');
574
+ const errorText = document.getElementById('errorText');
575
+ errorText.textContent = message;
576
+ errorEl.classList.add('show');
577
+
578
+ setTimeout(() => {
579
+ errorEl.classList.remove('show');
580
+ }, 5000);
581
+ }
582
+
583
+ function showSuccess() {
584
+ const successEl = document.getElementById('successMessage');
585
+ successEl.classList.add('show');
586
+
587
+ setTimeout(() => {
588
+ successEl.classList.remove('show');
589
+ }, 3000);
590
+ }
591
+
592
+ function convertJson() {
593
+ const jsonInput = document.getElementById('jsonInput').value.trim();
594
+
595
+ if (!jsonInput) {
596
+ showError('Please enter some JSON data');
597
+ return;
598
+ }
599
+
600
+ try {
601
+ // Parse JSON
602
+ const jsonData = JSON.parse(jsonInput);
603
+
604
+ // Convert to TOON
605
+ const toonOutput = window.toonEncode(jsonData);
606
+
607
+ // Display TOON output
608
+ document.getElementById('toonOutput').value = toonOutput;
609
+
610
+ // Calculate tokens
611
+ const jsonString = JSON.stringify(jsonData, null, 2);
612
+ const jsonTokens = window.countTokens(jsonString);
613
+ const toonTokens = window.countTokens(toonOutput);
614
+ const savings = jsonTokens - toonTokens;
615
+ const savingsPercent = ((savings / jsonTokens) * 100).toFixed(1);
616
+
617
+ // Update stats
618
+ document.getElementById('jsonTokens').textContent = jsonTokens;
619
+ document.getElementById('toonTokens').textContent = toonTokens;
620
+ document.getElementById('tokenSavings').textContent = savings;
621
+ document.getElementById('savingsPercent').textContent = savingsPercent + '%';
622
+
623
+ showSuccess();
624
+ } catch (error) {
625
+ showError('Invalid JSON: ' + error.message);
626
+ }
627
+ }
628
+
629
+ function clearAll() {
630
+ document.getElementById('jsonInput').value = '';
631
+ document.getElementById('toonOutput').value = '';
632
+ document.getElementById('jsonTokens').textContent = '0';
633
+ document.getElementById('toonTokens').textContent = '0';
634
+ document.getElementById('tokenSavings').textContent = '0';
635
+ document.getElementById('savingsPercent').textContent = '0%';
636
+ }
637
+
638
+ function formatJson() {
639
+ const jsonInput = document.getElementById('jsonInput').value.trim();
640
+
641
+ if (!jsonInput) return;
642
+
643
+ try {
644
+ const jsonData = JSON.parse(jsonInput);
645
+ document.getElementById('jsonInput').value = JSON.stringify(jsonData, null, 2);
646
+ } catch (error) {
647
+ showError('Cannot format invalid JSON');
648
+ }
649
+ }
650
+
651
+ function copyJson() {
652
+ const jsonInput = document.getElementById('jsonInput');
653
+ jsonInput.select();
654
+ document.execCommand('copy');
655
+ showSuccess();
656
+ }
657
+
658
+ function copyToon() {
659
+ const toonOutput = document.getElementById('toonOutput');
660
+ if (!toonOutput.value) {
661
+ showError('No TOON output to copy');
662
+ return;
663
+ }
664
+ toonOutput.select();
665
+ document.execCommand('copy');
666
+ showSuccess();
667
+ }
668
+
669
+ function downloadToon() {
670
+ const toonOutput = document.getElementById('toonOutput').value;
671
+
672
+ if (!toonOutput) {
673
+ showError('No TOON output to download');
674
+ return;
675
+ }
676
+
677
+ const blob = new Blob([toonOutput], { type: 'text/plain' });
678
+ const url = URL.createObjectURL(blob);
679
+ const a = document.createElement('a');
680
+ a.href = url;
681
+ a.download = `output_${new Date().toISOString().replace(/[:.]/g, '-')}.toon`;
682
+ document.body.appendChild(a);
683
+ a.click();
684
+ document.body.removeChild(a);
685
+ URL.revokeObjectURL(url);
686
+
687
+ showSuccess();
688
+ }
689
+
690
+ function loadSample(type) {
691
+ const sample = window.samples[type];
692
+ if (sample) {
693
+ document.getElementById('jsonInput').value = JSON.stringify(sample, null, 2);
694
+ convertJson();
695
+ }
696
+ }
697
+
698
+ // Auto-convert on Enter (Ctrl+Enter)
699
+ document.getElementById('jsonInput').addEventListener('keydown', function(e) {
700
+ if (e.ctrlKey && e.key === 'Enter') {
701
+ convertJson();
702
+ }
703
+ });
704
+ </script>
705
+ </body>
706
+ </html>