papagaio 0.4.2 → 0.5.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/index.html CHANGED
@@ -4,421 +4,319 @@
4
4
  <meta charset="UTF-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
6
  <title>🦜 papagaio</title>
7
- <style>
8
- :root {
9
- --bg-dark: #282828;
10
- --bg: #3c3836;
11
- --bg-light: #504945;
12
- --fg: #ebdbb2;
13
- --fg-dark: #a89984;
14
- --green: #b8bb26;
15
- --yellow: #fabd2f;
16
- --blue: #83a598;
17
- --red: #fb4934;
18
- --orange: #fe8019;
19
- }
20
-
21
- * { margin: 0; padding: 0; box-sizing: border-box; }
22
-
23
- body {
24
- font-family: -apple-system, BlinkMacSystemFont, sans-serif;
25
- color: var(--fg);
26
- padding: 15px;
27
- min-height: 100vh;
28
- overflow-x: hidden;
29
- position: relative;
30
- }
31
-
32
- /* Animated Reggae Background Stripes */
33
- body::before {
34
- content: '';
35
- position: fixed;
36
- top: 0;
37
- left: 0;
38
- width: 100%;
39
- height: 100%;
40
- background:
41
- linear-gradient(45deg,
42
- #C03F31 0%, #C03F31 25%,
43
- #F9DC5C 25%, #F9DC5C 50%,
44
- #2D936C 50%, #2D936C 75%,
45
- #0A100D 75%, #0A100D 100%);
46
- background-size: 400% 400%;
47
- animation: reggaeBackground 20s ease infinite;
48
- z-index: -1;
49
- }
50
-
51
- @keyframes reggaeBackground {
52
- 0% { background-position: 0% 50%; }
53
- 50% { background-position: 100% 50%; }
54
- 100% { background-position: 0% 50%; }
55
- }
56
-
57
- .container {
58
- max-width: 1100px;
59
- margin: 0 auto;
60
- background: var(--bg);
61
- border-radius: 8px;
62
- overflow: hidden;
63
- height: 95vh;
64
- display: flex;
65
- flex-direction: column;
66
- box-shadow: 0 5px 15px rgba(0,0,0,0.5);
67
- opacity: 1;
68
- }
69
-
70
- .header {
71
- background: var(--bg-light);
72
- padding: 12px 20px;
73
- text-align: center;
74
- position: relative;
75
- border-bottom: 2px solid var(--yellow);
76
- opacity: 1;
77
- }
78
-
79
- .header h1 {
80
- font-size: 22px;
81
- font-weight: bold;
82
- }
83
-
84
- .header::before, .header::after {
85
- content: "🦜";
86
- position: absolute;
87
- top: 75%;
88
- transform: translateY(-75%);
89
- transition: all 0.5s ease;
90
- scale: 2;
91
- }
92
- .header::before { left: 20px; }
93
- .header::after { right: 20px; }
94
-
95
- .animate-parrot::before,
96
- .animate-parrot::after {
97
- animation: parrotSpin 0.25s ease-in-out;
98
- }
99
-
100
- @keyframes parrotSpin {
101
- 0% { transform: translateY(-75%) scale(1) rotate(0deg); }
102
- 10% { transform: translateY(-75%) scale(1.5) rotate(0deg); }
103
- 100% { transform: translateY(-75%) scale(1) rotate(360deg); }
104
- }
105
-
106
- .main-controls {
107
- display: flex;
108
- gap: 10px;
109
- padding: 12px 20px;
110
- background: var(--bg-light);
111
- border-bottom: 1px solid var(--bg);
112
- justify-content: center;
113
- flex-wrap: wrap;
114
- opacity: 1;
115
- }
116
-
117
- button {
118
- padding: 8px 15px;
119
- background: var(--bg);
120
- color: var(--fg);
121
- border: 1px solid var(--bg-light);
122
- border-radius: 5px;
123
- cursor: pointer;
124
- font-weight: 600;
125
- transition: all 0.2s;
126
- }
127
-
128
- button:hover {
129
- background: var(--yellow);
130
- color: var(--bg-dark);
131
- }
132
-
133
- .content {
134
- display: flex;
135
- flex: 1;
136
- gap: 15px;
137
- padding: 15px;
138
- overflow: hidden;
139
- opacity: 1;
140
- }
141
-
142
- .panel {
143
- flex: 1;
144
- display: flex;
145
- flex-direction: column;
146
- background: var(--bg-dark);
147
- border-radius: 5px;
148
- overflow: hidden;
149
- border: 1px solid var(--bg-light);
150
- opacity: 1;
151
- }
152
-
153
- .panel h2 {
154
- padding: 10px 15px;
155
- background: var(--bg-light);
156
- font-size: 14px;
157
- text-align: center;
158
- font-weight: 600;
159
- }
160
-
161
- textarea {
162
- flex: 1;
163
- padding: 15px;
164
- border: none;
165
- background: var(--bg-dark);
166
- color: var(--fg);
167
- resize: none;
168
- font-family: 'Monaco', 'Menlo', monospace;
169
- font-size: 14px;
170
- line-height: 1.5;
171
- opacity: 1;
172
- }
173
-
174
- textarea:focus {
175
- outline: 2px solid var(--blue);
176
- }
177
-
178
- .footer {
179
- padding: 12px 20px;
180
- background: var(--bg-light);
181
- border-top: 1px solid var(--bg);
182
- display: flex;
183
- flex-direction: column;
184
- gap: 10px;
185
- position: relative;
186
- opacity: 1;
187
- }
188
-
189
- .footer::before {
190
- content: "🌴🌿";
191
- position: absolute;
192
- bottom: 15%;
193
- right: 2%;
194
- opacity: 0.7;
195
- }
196
-
197
- .footer-controls {
198
- display: flex;
199
- justify-content: space-between;
200
- align-items: center;
201
- flex-wrap: wrap;
202
- gap: 10px;
203
- }
204
-
205
- .footer-bottom {
206
- display: flex;
207
- justify-content: space-between;
208
- align-items: center;
209
- }
210
-
211
- .control-group {
212
- display: flex;
213
- align-items: center;
214
- gap: 8px;
215
- }
216
-
217
- .control-group label {
218
- color: var(--fg);
219
- font-size: 14px;
220
- white-space: nowrap;
221
- font-weight: 600;
222
- }
223
-
224
- input[type="text"] {
225
- background: var(--bg-dark);
226
- border: 1px solid var(--bg-light);
227
- color: var(--fg);
228
- padding: 6px;
229
- width: 40px;
230
- text-align: center;
231
- border-radius: 3px;
232
- }
233
-
234
- .status {
235
- background: var(--green);
236
- color: var(--bg-dark);
237
- padding: 6px 12px;
238
- border-radius: 4px;
239
- font-size: 14px;
240
- font-weight: bold;
241
- }
242
-
243
- .file-input { display: none; }
244
-
245
- @media (max-width: 768px) {
246
- .content { flex-direction: column; }
247
- .panel { min-height: 250px; }
248
- .footer-controls, .footer-bottom { flex-direction: column; align-items: stretch; gap: 10px; }
249
- .control-group { justify-content: center; }
250
- }
251
- </style>
252
7
  </head>
253
8
  <body>
254
- <div class="container">
255
- <div class="header" id="header">
256
- <h1>papagaio</h1>
257
- easy text preprocessor
258
- </div>
259
-
260
- <div class="main-controls">
261
- <button onclick="animateParrot(); loadFile()">📂 Load</button>
262
- <button onclick="animateParrot(); processCode()">▶️ Process</button>
263
- <button onclick="animateParrot(); downloadResult()">💾 Download</button>
264
- <button onclick="animateParrot(); clearAll()">🗑️ Clear</button>
265
- <input type="file" id="fileInput" class="file-input" onchange="handleFileSelect()">
266
- </div>
267
-
268
- <div class="content">
269
- <div class="panel">
270
- <h2>INPUT</h2>
271
- <textarea id="input" placeholder="Enter your text here..."></textarea>
272
- </div>
273
- <div class="panel">
274
- <h2>OUTPUT</h2>
275
- <textarea id="output" placeholder="Output will appear here..." readonly></textarea>
276
- </div>
277
- </div>
278
-
279
- <div class="footer">
280
- <div class="footer-controls">
281
- <div class="control-group">
282
- <label>Delimiter:</label>
283
- <input id="openQuote" type="text" placeholder="{">
284
- <input id="closeQuote" type="text" placeholder="}">
285
- <button onclick="animateParrot(); applyQuotes()">Apply</button>
286
- <button onclick="animateParrot(); resetQuotes()">Reset</button>
287
- </div>
288
-
289
- <div class="control-group">
290
- <label>Sigil:</label>
291
- <input id="sigilInput" type="text" placeholder="$">
292
- <button onclick="animateParrot(); applySigil()">Apply</button>
293
- <button onclick="animateParrot(); resetSigil()">Reset</button>
294
- </div>
295
- </div>
296
-
297
- <div class="footer-bottom">
298
- <span id="status" class="status">READY</span>
299
- </div>
300
- </div>
9
+
10
+ <h1>🦜 papagaio</h1>
11
+ <p>
12
+ easy yet powerful text preprocessor.
13
+ </p>
14
+ <hr>
15
+ <div>
16
+ <label for="sketchSelect">Current Sketch:</label>
17
+ <select id="sketchSelect" onchange="switchSketch()">
18
+ <option value="">-- Select Sketch --</option>
19
+ </select>
20
+ |
21
+ <button onclick="createNewSketch()">New Sketch</button>
22
+ <button onclick="renameCurrentSketch()">Rename</button>
23
+ <button onclick="deleteCurrentSketch()">Delete</button>
301
24
  </div>
25
+ <hr>
26
+ <div>
27
+ <label for="sketchSelect">Sketches:</label>
28
+ <button onclick="exportSketches()">Export</button>
29
+ <input type="file" id="importFileInput" onchange="importSketches(this.files[0])" accept=".json">
30
+ </div>
31
+
32
+ <hr>
33
+
34
+ <h3>INPUT</h3>
35
+ <textarea id="input" rows="15" cols="80" style="width: 70%; font-family: monospace;"></textarea>
36
+
37
+ <br><br>
38
+
39
+ <button onclick="processCode()">Process</button>
40
+
41
+ <br><br>
42
+
43
+ <h3>OUTPUT</h3>
44
+ <textarea id="output" rows="15" cols="80" style="width: 70%; font-family: monospace;" readonly></textarea>
302
45
 
303
- <script type="module">
304
- import { Papagaio } from './src/papagaio.js';
305
-
306
- let processor = null;
46
+ <script type="module">
47
+ import { Papagaio } from './src/papagaio.js';
307
48
 
308
- function initProcessor() {
309
- processor = new Papagaio();
310
- updateStatus('CORE LOADED');
49
+ class SketchesManager {
50
+ constructor() {
51
+ this.sketches = this.loadSketches();
52
+ this.currentSketchId = this.loadCurrentSketchId();
311
53
  }
312
54
 
313
- window.addEventListener('load', initProcessor);
314
-
315
- function animateParrot() {
316
- const header = document.getElementById('header');
317
- header.classList.add('animate-parrot');
318
- setTimeout(() => header.classList.remove('animate-parrot'), 500);
55
+ loadSketches() {
56
+ try {
57
+ const saved = localStorage.getItem('papagaio_sketches');
58
+ return saved ? JSON.parse(saved) : {};
59
+ } catch {
60
+ return {};
61
+ }
319
62
  }
320
63
 
321
- function updateStatus(message) {
322
- document.getElementById('status').textContent = message;
64
+ saveSketches() {
65
+ try {
66
+ localStorage.setItem('papagaio_sketches', JSON.stringify(this.sketches));
67
+ } catch {}
323
68
  }
324
69
 
325
- function processCode() {
70
+ loadCurrentSketchId() {
326
71
  try {
327
- if (!processor) {
328
- updateStatus('CORE NOT LOADED');
329
- return;
330
- }
331
- updateStatus('PROCESSING...');
332
- const input = document.getElementById('input').value;
333
- const output = processor.process(input);
334
- document.getElementById('output').value = output;
335
- updateStatus('COMPLETED');
336
- } catch (err) {
337
- document.getElementById('output').value = `Error: ${err.message}`;
338
- updateStatus('ERROR');
72
+ return localStorage.getItem('papagaio_current_sketch');
73
+ } catch {
74
+ return null;
339
75
  }
340
76
  }
341
77
 
342
- window.processCode = processCode;
343
- window.animateParrot = animateParrot;
344
- window.loadFile = loadFile;
345
- window.handleFileSelect = handleFileSelect;
346
- window.downloadResult = downloadResult;
347
- window.clearAll = clearAll;
348
- window.applyQuotes = applyQuotes;
349
- window.resetQuotes = resetQuotes;
350
- window.applySigil = applySigil;
351
- window.resetSigil = resetSigil;
352
-
353
- function loadFile() {
354
- document.getElementById('fileInput').click();
78
+ setCurrentSketchId(id) {
79
+ this.currentSketchId = id;
80
+ try {
81
+ localStorage.setItem('papagaio_current_sketch', id);
82
+ } catch {}
355
83
  }
356
84
 
357
- function handleFileSelect() {
358
- const file = document.getElementById('fileInput').files[0];
359
- if (!file) return;
360
- const reader = new FileReader();
361
- reader.onload = e => {
362
- document.getElementById('input').value = e.target.result;
363
- updateStatus('FILE LOADED');
85
+ createSketch(name) {
86
+ const id = Date.now().toString();
87
+ this.sketches[id] = {
88
+ id,
89
+ name,
90
+ input: '',
91
+ createdAt: new Date().toLocaleString()
364
92
  };
365
- reader.readAsText(file);
93
+ this.saveSketches();
94
+ this.setCurrentSketchId(id);
95
+ return id;
96
+ }
97
+
98
+ getSketch(id) {
99
+ return this.sketches[id] || null;
366
100
  }
367
101
 
368
- function downloadResult() {
369
- const output = document.getElementById('output').value;
370
- if (!output) return alert('Nothing to download!');
371
- const blob = new Blob([output], { type: 'text/plain' });
372
- const url = URL.createObjectURL(blob);
373
- const a = document.createElement('a');
374
- a.href = url;
375
- a.download = 'output.txt';
376
- a.click();
377
- URL.revokeObjectURL(url);
378
- updateStatus('DOWNLOAD COMPLETED');
102
+ updateSketch(id, data) {
103
+ if (this.sketches[id]) {
104
+ Object.assign(this.sketches[id], data);
105
+ this.saveSketches();
106
+ }
107
+ }
108
+
109
+ deleteSketch(id) {
110
+ delete this.sketches[id];
111
+ this.saveSketches();
112
+ if (this.currentSketchId === id) {
113
+ const remaining = Object.keys(this.sketches);
114
+ this.currentSketchId = remaining.length > 0 ? remaining[0] : null;
115
+ if (this.currentSketchId) {
116
+ this.setCurrentSketchId(this.currentSketchId);
117
+ }
118
+ }
119
+ }
120
+
121
+ getAllSketches() {
122
+ return Object.values(this.sketches).sort((a, b) =>
123
+ parseInt(b.id) - parseInt(a.id)
124
+ );
125
+ }
126
+
127
+ renameSketch(id, newName) {
128
+ if (this.sketches[id]) {
129
+ this.sketches[id].name = newName;
130
+ this.saveSketches();
131
+ }
379
132
  }
133
+ }
134
+
135
+ let processor = new Papagaio();
136
+ let sketchesManager;
380
137
 
381
- function clearAll() {
382
- document.getElementById('input').value = '';
138
+ window.addEventListener('load', () => {
139
+ sketchesManager = new SketchesManager();
140
+ loadOrCreateDefaultSketch();
141
+ populateSketchSelect();
142
+ });
143
+
144
+ function loadOrCreateDefaultSketch() {
145
+ let sketch = null;
146
+ if (sketchesManager.currentSketchId) {
147
+ sketch = sketchesManager.getSketch(sketchesManager.currentSketchId);
148
+ }
149
+
150
+ if (!sketch) {
151
+ const allSketches = sketchesManager.getAllSketches();
152
+ if (allSketches.length > 0) {
153
+ sketch = allSketches[0];
154
+ sketchesManager.setCurrentSketchId(sketch.id);
155
+ } else {
156
+ const id = sketchesManager.createSketch('Default');
157
+ sketch = sketchesManager.getSketch(id);
158
+ }
159
+ }
160
+
161
+ if (sketch) {
162
+ document.getElementById('input').value = sketch.input;
383
163
  document.getElementById('output').value = '';
384
- updateStatus('CLEARED');
385
164
  }
165
+ }
386
166
 
387
- document.getElementById('input').addEventListener('keydown', e => {
388
- if (e.ctrlKey && e.key === 'Enter') {
389
- animateParrot();
390
- processCode();
167
+ function saveCurrentSketch() {
168
+ if (sketchesManager.currentSketchId) {
169
+ sketchesManager.updateSketch(sketchesManager.currentSketchId, {
170
+ input: document.getElementById('input').value
171
+ });
172
+ }
173
+ }
174
+
175
+ function populateSketchSelect() {
176
+ const select = document.getElementById('sketchSelect');
177
+ select.innerHTML = '<option value="">-- Select Sketch --</option>';
178
+
179
+ const sketches = sketchesManager.getAllSketches();
180
+ sketches.forEach(sketch => {
181
+ const option = document.createElement('option');
182
+ option.value = sketch.id;
183
+ option.textContent = sketch.name;
184
+ if (sketch.id === sketchesManager.currentSketchId) {
185
+ option.selected = true;
391
186
  }
187
+ select.appendChild(option);
392
188
  });
189
+ }
393
190
 
394
- function applyQuotes() {
395
- const o = document.getElementById('openQuote').value;
396
- const c = document.getElementById('closeQuote').value;
397
- if (!o || !c) return alert('Enter both delimiters');
398
- processor.changeDelimiters(o, c);
399
- updateStatus('DELIMITERS APPLIED');
191
+ function switchSketch() {
192
+ const select = document.getElementById('sketchSelect');
193
+ const id = select.value;
194
+
195
+ if (!id) return;
196
+
197
+ saveCurrentSketch();
198
+ sketchesManager.setCurrentSketchId(id);
199
+ const sketch = sketchesManager.getSketch(id);
200
+
201
+ if (sketch) {
202
+ document.getElementById('input').value = sketch.input;
203
+ document.getElementById('output').value = '';
400
204
  }
205
+ }
401
206
 
402
- function resetQuotes() {
403
- processor.changeDelimiters('{', '}');
404
- document.getElementById('openQuote').value = '';
405
- document.getElementById('closeQuote').value = '';
406
- updateStatus('DELIMITERS RESET');
207
+ function createNewSketch() {
208
+ const name = prompt('Enter sketch name:');
209
+ if (!name || !name.trim()) {
210
+ alert('Please enter a valid sketch name');
211
+ return;
407
212
  }
213
+
214
+ const id = sketchesManager.createSketch(name.trim());
215
+ populateSketchSelect();
216
+
217
+ const sketch = sketchesManager.getSketch(id);
218
+ if (sketch) {
219
+ document.getElementById('input').value = sketch.input;
220
+ document.getElementById('output').value = '';
221
+ }
222
+ }
408
223
 
409
- function applySigil() {
410
- const s = document.getElementById('sigilInput').value;
411
- if (!s) return alert('Enter a sigil');
412
- processor.changeSigil(s);
413
- updateStatus('SIGIL APPLIED');
224
+ function renameCurrentSketch() {
225
+ if (!sketchesManager.currentSketchId) {
226
+ alert('No sketch selected');
227
+ return;
228
+ }
229
+
230
+ const sketch = sketchesManager.getSketch(sketchesManager.currentSketchId);
231
+ const newName = prompt('Enter new name:', sketch.name);
232
+
233
+ if (!newName || !newName.trim()) {
234
+ alert('Please enter a valid name');
235
+ return;
414
236
  }
237
+
238
+ sketchesManager.renameSketch(sketchesManager.currentSketchId, newName.trim());
239
+ populateSketchSelect();
240
+ }
415
241
 
416
- function resetSigil() {
417
- processor.changeSigil('$');
418
- document.getElementById('sigilInput').value = '';
419
- updateStatus('SIGIL RESET');
242
+ function deleteCurrentSketch() {
243
+ if (!sketchesManager.currentSketchId) {
244
+ alert('No sketch selected');
245
+ return;
420
246
  }
421
- </script>
247
+
248
+ const sketch = sketchesManager.getSketch(sketchesManager.currentSketchId);
249
+
250
+ if (!confirm(`Are you sure you want to delete "${sketch.name}"? This action cannot be undone.`)) {
251
+ return;
252
+ }
253
+
254
+ sketchesManager.deleteSketch(sketchesManager.currentSketchId);
255
+ loadOrCreateDefaultSketch();
256
+ populateSketchSelect();
257
+ }
258
+
259
+ function processCode() {
260
+ try {
261
+ const input = document.getElementById('input').value;
262
+ const output = processor.process(input);
263
+ document.getElementById('output').value = output;
264
+ saveCurrentSketch();
265
+ } catch (err) {
266
+ document.getElementById('output').value = `Error: ${err.message}`;
267
+ }
268
+ }
269
+
270
+ function exportSketches() {
271
+ const data = {
272
+ papagaio_sketches: localStorage.getItem('papagaio_sketches'),
273
+ papagaio_current_sketch: localStorage.getItem('papagaio_current_sketch')
274
+ };
275
+
276
+ const blob = new Blob([JSON.stringify(data, null, 2)], {
277
+ type: "application/json"
278
+ });
279
+
280
+ const a = document.createElement("a");
281
+ a.href = URL.createObjectURL(blob);
282
+ a.download = `papagaio_sketches_${Date.now()}.json`;
283
+ a.click();
284
+ }
285
+
286
+ function importSketches(file) {
287
+ if (!file) return;
288
+
289
+ const reader = new FileReader();
290
+
291
+ reader.onload = () => {
292
+ try {
293
+ const data = JSON.parse(reader.result);
294
+ if (data.papagaio_sketches) {
295
+ localStorage.setItem('papagaio_sketches', data.papagaio_sketches);
296
+ }
297
+ if (data.papagaio_current_sketch) {
298
+ localStorage.setItem('papagaio_current_sketch', data.papagaio_current_sketch);
299
+ }
300
+ location.reload();
301
+ } catch (err) {
302
+ alert('Error importing file: ' + err.message);
303
+ }
304
+ };
305
+
306
+ reader.readAsText(file);
307
+ }
308
+
309
+ setInterval(() => {
310
+ saveCurrentSketch();
311
+ }, 1000);
422
312
 
313
+ window.processCode = processCode;
314
+ window.switchSketch = switchSketch;
315
+ window.createNewSketch = createNewSketch;
316
+ window.renameCurrentSketch = renameCurrentSketch;
317
+ window.deleteCurrentSketch = deleteCurrentSketch;
318
+ window.exportSketches = exportSketches;
319
+ window.importSketches = importSketches;
320
+ </script>
423
321
  </body>
424
322
  </html>