papagaio 0.7.3 → 0.7.5

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
@@ -9,7 +9,7 @@ Minimal yet powerful text preprocessor.
9
9
 
10
10
  ## Installation
11
11
  ```javascript
12
- import { Papagaio } from './src/papagaio.js';
12
+ import { Papagaio } from './papagaio.js';
13
13
  const papagaio = new Papagaio();
14
14
  const result = papagaio.process(input);
15
15
  ```
package/bin/cli.mjs CHANGED
@@ -7,147 +7,131 @@ const isNode = typeof process !== 'undefined' && process.versions && process.ver
7
7
  // MAIN FUNCTION
8
8
  // ============================================================================
9
9
  async function main() {
10
- // ============================================================================
11
- // IMPORTS - Branch por runtime
12
- // ============================================================================
13
- let Papagaio, std, os, fs, pkg;
14
-
15
- if (isQuickJS) {
16
- // QuickJS imports
17
- const stdModule = await import("std");
18
- const osModule = await import("os");
19
- std = stdModule;
20
- os = osModule;
21
- const { Papagaio: P } = await import("../src/papagaio.js");
22
- Papagaio = P;
23
- } else {
24
- // Node.js imports
25
- const fsModule = await import("fs");
26
- fs = fsModule.default;
27
-
28
- // Load package.json usando fs ao invés de require
29
- const pkgPath = new URL("../package.json", import.meta.url);
30
- const pkgContent = fs.readFileSync(pkgPath, "utf8");
31
- pkg = JSON.parse(pkgContent);
32
-
33
- const { Papagaio: P } = await import("../src/papagaio.js");
34
- Papagaio = P;
35
- }
36
-
37
- // ============================================================================
38
- // ABSTRAÇÃO DE CONSOLE/STD
39
- // ============================================================================
40
- const output = {
41
- log: isQuickJS ? (msg) => std.out.puts(msg + "\n") : console.log,
42
- error: isQuickJS ? (msg) => std.err.puts(msg + "\n") : console.error,
43
- exit: isQuickJS ? std.exit : process.exit
44
- };
45
-
46
- // ============================================================================
47
- // PARSE ARGUMENTS
48
- // ============================================================================
49
- const args = isQuickJS ? scriptArgs.slice(1) : process.argv.slice(2);
50
- const VERSION = isQuickJS ? "0.6.0" : pkg.version;
51
-
52
- // Help & Version
53
- if (args.includes("-v") || args.includes("--version")) {
54
- output.log(VERSION);
55
- output.exit(0);
56
- }
57
-
58
- if (args.includes("-h") || args.includes("--help")) {
59
- output.log(`Usage: papagaio [options] <file1> [file2] [...]
10
+ // ============================================================================
11
+ // IMPORTS - Branch por runtime
12
+ // ============================================================================
13
+ let Papagaio, std, os, fs, pkg;
14
+
15
+ if (isQuickJS) {
16
+ // QuickJS imports
17
+ const stdModule = await import("std");
18
+ const osModule = await import("os");
19
+ std = stdModule;
20
+ os = osModule;
21
+ const { Papagaio: P } = await import("../papagaio.js");
22
+ Papagaio = P;
23
+ } else {
24
+ // Node.js imports
25
+ const fsModule = await import("fs");
26
+ fs = fsModule.default;
27
+
28
+ // Load package.json usando fs ao invés de require
29
+ const pkgPath = new URL("../package.json", import.meta.url);
30
+ const pkgContent = fs.readFileSync(pkgPath, "utf8");
31
+ pkg = JSON.parse(pkgContent);
32
+
33
+ const { Papagaio: P } = await import("../papagaio.js");
34
+ Papagaio = P;
35
+ }
36
+
37
+ // ============================================================================
38
+ // ABSTRAÇÃO DE CONSOLE/STD
39
+ // ============================================================================
40
+ const output = {
41
+ log: isQuickJS ? (msg) => std.out.puts(msg + "\n") : console.log,
42
+ error: isQuickJS ? (msg) => std.err.puts(msg + "\n") : console.error,
43
+ exit: isQuickJS ? std.exit : process.exit
44
+ };
45
+
46
+ // ============================================================================
47
+ // PARSE ARGUMENTS
48
+ // ============================================================================
49
+ const args = isQuickJS ? scriptArgs.slice(1) : process.argv.slice(2);
50
+ const VERSION = isQuickJS ? "0.6.0" : pkg.version;
51
+
52
+ // Help & Version
53
+ if (args.includes("-v") || args.includes("--version")) {
54
+ output.log(VERSION);
55
+ output.exit(0);
56
+ }
57
+
58
+ if (args.includes("-h") || args.includes("--help")) {
59
+ output.log(`Usage: papagaio [options] <file1> [file2] [...]
60
60
 
61
61
  Options:
62
62
  -h, --help Show this help message
63
63
  -v, --version Show version number
64
- --sigil <symbol> Set sigil symbol
65
- --open <symbol> Set open symbol
66
- --close <symbol> Set close symbol
67
64
 
68
65
  Examples:
69
66
  papagaio input.txt
70
67
  papagaio file1.txt file2.txt file3.txt
71
- papagaio *.txt
72
- papagaio --sigil @ --open [ --close ] input.txt`);
73
- output.exit(0);
74
- }
75
-
76
- // Parse options
77
- const sigilIndex = args.findIndex(arg => arg === "--sigil");
78
- const openIndex = args.findIndex(arg => arg === "--open");
79
- const closeIndex = args.findIndex(arg => arg === "--close");
80
-
81
- const sigil = sigilIndex !== -1 ? args[sigilIndex + 1] : undefined;
82
- const open = openIndex !== -1 ? args[openIndex + 1] : undefined;
83
- const close = closeIndex !== -1 ? args[closeIndex + 1] : undefined;
84
-
85
- // Get input files
86
- const files = args.filter((arg, i) => {
87
- if (arg.startsWith("-")) return false;
88
- if (i > 0 && (args[i - 1] === "--sigil" || args[i - 1] === "--open" || args[i - 1] === "--close")) return false;
89
- return true;
90
- });
91
-
92
- if (files.length === 0) {
93
- output.error("Error: no input file specified.\nUse --help for usage.");
94
- output.exit(1);
95
- }
96
-
97
- // ============================================================================
98
- // FILE READING ABSTRACTION
99
- // ============================================================================
100
- function readFile(filepath) {
101
- if (isQuickJS) {
102
- const f = std.open(filepath, "r");
103
- if (!f) {
104
- throw new Error(`cannot open file '${filepath}'`);
105
- }
106
- const content = f.readAsString();
107
- f.close();
108
- return content;
109
- } else {
110
- if (!fs.existsSync(filepath)) {
111
- throw new Error(`file not found: ${filepath}`);
112
- }
113
- return fs.readFileSync(filepath, "utf8");
68
+ papagaio *.txt`);
69
+ output.exit(0);
70
+ }
71
+
72
+ // Get input files
73
+ const files = args.filter((arg, i) => {
74
+ if (arg.startsWith("-")) return false;
75
+ return true;
76
+ });
77
+
78
+ if (files.length === 0) {
79
+ output.error("Error: no input file specified.\nUse --help for usage.");
80
+ output.exit(1);
81
+ }
82
+
83
+ // ============================================================================
84
+ // FILE READING ABSTRACTION
85
+ // ============================================================================
86
+ function readFile(filepath) {
87
+ if (isQuickJS) {
88
+ const f = std.open(filepath, "r");
89
+ if (!f) {
90
+ throw new Error(`cannot open file '${filepath}'`);
91
+ }
92
+ const content = f.readAsString();
93
+ f.close();
94
+ return content;
95
+ } else {
96
+ if (!fs.existsSync(filepath)) {
97
+ throw new Error(`file not found: ${filepath}`);
98
+ }
99
+ return fs.readFileSync(filepath, "utf8");
100
+ }
114
101
  }
115
- }
116
-
117
- // ============================================================================
118
- // READ AND CONCATENATE FILES
119
- // ============================================================================
120
- let concatenatedSrc = "";
121
- let hasErrors = false;
122
-
123
- for (const file of files) {
124
- try {
125
- const src = readFile(file);
126
- concatenatedSrc += src;
127
- } catch (error) {
128
- output.error(`Error reading ${file}: ${error.message || error}`);
129
- hasErrors = true;
102
+
103
+ // ============================================================================
104
+ // READ AND CONCATENATE FILES
105
+ // ============================================================================
106
+ let concatenatedSrc = "";
107
+ let hasErrors = false;
108
+
109
+ for (const file of files) {
110
+ try {
111
+ const src = readFile(file);
112
+ concatenatedSrc += src;
113
+ } catch (error) {
114
+ output.error(`Error reading ${file}: ${error.message || error}`);
115
+ hasErrors = true;
116
+ }
130
117
  }
131
- }
132
-
133
- if (hasErrors) {
134
- output.exit(1);
135
- }
136
-
137
- // ============================================================================
138
- // PROCESS CONCATENATED INPUT
139
- // ============================================================================
140
- const p = new Papagaio(sigil, open, close);
141
- const out = p.process(concatenatedSrc);
142
- output.log(out);
118
+
119
+ if (hasErrors) {
120
+ output.exit(1);
121
+ }
122
+
123
+ // PROCESS CONCATENATED INPUT
124
+ const p = new Papagaio();
125
+ const out = p.process(concatenatedSrc);
126
+ output.log(out);
143
127
  }
144
128
 
145
- // Executa main
129
+ // main
146
130
  main().catch(err => {
147
- const output = isQuickJS
148
- ? (msg) => std.err.puts(msg + "\n")
149
- : console.error;
150
- output("Fatal error: " + (err.message || err));
151
- const exit = isQuickJS ? std.exit : process.exit;
152
- exit(1);
131
+ const output = isQuickJS
132
+ ? (msg) => std.err.puts(msg + "\n")
133
+ : console.error;
134
+ output("Fatal error: " + (err.message || err));
135
+ const exit = isQuickJS ? std.exit : process.exit;
136
+ exit(1);
153
137
  });
package/index.html CHANGED
@@ -2,263 +2,192 @@
2
2
  <html lang="en">
3
3
  <head>
4
4
  <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
6
6
  <title>🦜 papagaio</title>
7
- </head>
8
- <body>
9
-
10
- <h1>🦜 papagaio </h1>
11
- <p>
12
- easy yet powerful text preprocessor.
13
- </p>
14
- <hr>
15
- <div>
16
- <strong>
17
- <label for="sketchSelect">Current Sketch:</label>
18
- </strong>
19
-
20
- <select id="sketchSelect" onchange="switchSketch()">
21
- <option value="">-- Select Sketch --</option>
22
- </select>
23
- |
24
- <button onclick="createNewSketch()">New Sketch</button>
25
- <button onclick="renameCurrentSketch()">Rename</button>
26
- <button onclick="deleteCurrentSketch()">Delete</button>
27
- </div>
28
- <hr>
29
- <div>
30
- <strong>
31
- <label for="sketchSelect">Sketches:</label>
32
- </strong>
33
- <button onclick="exportSketches()">Export</button>
34
- <input type="file" id="importFileInput" onchange="importSketches(this.files[0])" accept=".json">
35
- </div>
36
-
37
- <hr>
38
-
39
- <h3>INPUT</h3>
40
- <textarea id="input" rows="15" cols="80" style="width: 70%; font-family: monospace;"></textarea>
41
-
42
- <br><br>
43
-
44
- <button onclick="processCode()">Process</button>
45
-
46
- <br><br>
47
-
48
- <h3>OUTPUT</h3>
49
- <textarea id="output" rows="15" cols="80" style="width: 70%; font-family: monospace;" readonly></textarea>
50
-
51
- <script type="module">
52
- import { Papagaio } from './src/papagaio.js';
53
-
54
- class SketchesManager {
55
- constructor() {
56
- this.sketches = this.loadSketches();
57
- this.currentSketchId = this.loadCurrentSketchId();
58
- }
7
+ <style>
8
+ * {
9
+ margin: 0;
10
+ padding: 0;
11
+ box-sizing: border-box;
12
+ -webkit-tap-highlight-color: transparent;
13
+ }
59
14
 
60
- loadSketches() {
61
- try {
62
- const saved = localStorage.getItem('papagaio_sketches');
63
- return saved ? JSON.parse(saved) : {};
64
- } catch {
65
- return {};
66
- }
67
- }
15
+ body {
16
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
17
+ background: #fff;
18
+ color: #000;
19
+ overflow: hidden;
20
+ }
68
21
 
69
- saveSketches() {
70
- try {
71
- localStorage.setItem('papagaio_sketches', JSON.stringify(this.sketches));
72
- } catch {}
73
- }
22
+ .app {
23
+ max-width: 100%;
24
+ height: 100vh;
25
+ display: flex;
26
+ flex-direction: column;
27
+ }
74
28
 
75
- loadCurrentSketchId() {
76
- try {
77
- return localStorage.getItem('papagaio_current_sketch');
78
- } catch {
79
- return null;
80
- }
81
- }
29
+ .header {
30
+ background: #fff;
31
+ padding: 24px 20px;
32
+ text-align: center;
33
+ border-bottom: 1px solid #ddd;
34
+ }
82
35
 
83
- setCurrentSketchId(id) {
84
- this.currentSketchId = id;
85
- try {
86
- localStorage.setItem('papagaio_current_sketch', id);
87
- } catch {}
88
- }
36
+ .header h1 {
37
+ font-size: 2rem;
38
+ color: #000;
39
+ margin-bottom: 4px;
40
+ }
89
41
 
90
- createSketch(name) {
91
- const id = Date.now().toString();
92
- this.sketches[id] = {
93
- id,
94
- name,
95
- input: '',
96
- createdAt: new Date().toLocaleString()
97
- };
98
- this.saveSketches();
99
- this.setCurrentSketchId(id);
100
- return id;
101
- }
42
+ .header p {
43
+ font-size: 0.9rem;
44
+ color: #666;
45
+ }
102
46
 
103
- getSketch(id) {
104
- return this.sketches[id] || null;
105
- }
47
+ .tabs {
48
+ display: flex;
49
+ background: #fff;
50
+ border-bottom: 1px solid #ddd;
51
+ }
106
52
 
107
- updateSketch(id, data) {
108
- if (this.sketches[id]) {
109
- Object.assign(this.sketches[id], data);
110
- this.saveSketches();
111
- }
112
- }
53
+ .tab {
54
+ flex: 1;
55
+ padding: 18px;
56
+ text-align: center;
57
+ font-size: 1rem;
58
+ font-weight: 500;
59
+ background: #f5f5f5;
60
+ border: none;
61
+ cursor: pointer;
62
+ transition: all 0.2s;
63
+ color: #666;
64
+ border-right: 1px solid #ddd;
65
+ }
113
66
 
114
- deleteSketch(id) {
115
- delete this.sketches[id];
116
- this.saveSketches();
117
- if (this.currentSketchId === id) {
118
- const remaining = Object.keys(this.sketches);
119
- this.currentSketchId = remaining.length > 0 ? remaining[0] : null;
120
- if (this.currentSketchId) {
121
- this.setCurrentSketchId(this.currentSketchId);
122
- }
123
- }
124
- }
67
+ .tab:last-child {
68
+ border-right: none;
69
+ }
125
70
 
126
- getAllSketches() {
127
- return Object.values(this.sketches).sort((a, b) =>
128
- parseInt(b.id) - parseInt(a.id)
129
- );
130
- }
71
+ .tab.active {
72
+ background: #fff;
73
+ color: #000;
74
+ border-bottom: 2px solid #000;
75
+ }
131
76
 
132
- renameSketch(id, newName) {
133
- if (this.sketches[id]) {
134
- this.sketches[id].name = newName;
135
- this.saveSketches();
136
- }
137
- }
77
+ .content {
78
+ flex: 1;
79
+ display: flex;
80
+ flex-direction: column;
81
+ overflow: hidden;
138
82
  }
139
83
 
140
- let processor = new Papagaio();
141
- let sketchesManager;
84
+ .editor-container {
85
+ display: none;
86
+ flex-direction: column;
87
+ flex: 1;
88
+ }
142
89
 
143
- window.addEventListener('load', () => {
144
- sketchesManager = new SketchesManager();
145
- loadOrCreateDefaultSketch();
146
- populateSketchSelect();
147
- });
90
+ .editor-container.active {
91
+ display: flex;
92
+ }
148
93
 
149
- function loadOrCreateDefaultSketch() {
150
- let sketch = null;
151
- if (sketchesManager.currentSketchId) {
152
- sketch = sketchesManager.getSketch(sketchesManager.currentSketchId);
153
- }
154
-
155
- if (!sketch) {
156
- const allSketches = sketchesManager.getAllSketches();
157
- if (allSketches.length > 0) {
158
- sketch = allSketches[0];
159
- sketchesManager.setCurrentSketchId(sketch.id);
160
- } else {
161
- const id = sketchesManager.createSketch('Default');
162
- sketch = sketchesManager.getSketch(id);
163
- }
164
- }
165
-
166
- if (sketch) {
167
- document.getElementById('input').value = sketch.input;
168
- document.getElementById('output').value = '';
169
- }
94
+ textarea {
95
+ flex: 1;
96
+ padding: 20px;
97
+ border: none;
98
+ font-family:
99
+ ui-monospace,
100
+ SFMono-Regular,
101
+ Menlo,
102
+ Consolas,
103
+ "Liberation Mono",
104
+ monospace;
105
+ font-size: 15px;
106
+ line-height: 1.55;
107
+ font-weight: 500;
108
+ letter-spacing: 0.2px;
109
+ resize: none;
110
+ background: #fff;
111
+ color: #000;
112
+ white-space: pre;
113
+ overflow-x: auto;
114
+ overflow-y: auto;
115
+ word-break: normal;
170
116
  }
171
117
 
172
- function saveCurrentSketch() {
173
- if (sketchesManager.currentSketchId) {
174
- sketchesManager.updateSketch(sketchesManager.currentSketchId, {
175
- input: document.getElementById('input').value
176
- });
177
- }
118
+ textarea:focus {
119
+ outline: none;
178
120
  }
179
121
 
180
- function populateSketchSelect() {
181
- const select = document.getElementById('sketchSelect');
182
- select.innerHTML = '<option value="">-- Select Sketch --</option>';
183
-
184
- const sketches = sketchesManager.getAllSketches();
185
- sketches.forEach(sketch => {
186
- const option = document.createElement('option');
187
- option.value = sketch.id;
188
- option.textContent = sketch.name;
189
- if (sketch.id === sketchesManager.currentSketchId) {
190
- option.selected = true;
191
- }
192
- select.appendChild(option);
193
- });
122
+ #output {
123
+ background: #fafafa;
194
124
  }
195
125
 
196
- function switchSketch() {
197
- const select = document.getElementById('sketchSelect');
198
- const id = select.value;
199
-
200
- if (!id) return;
201
-
202
- saveCurrentSketch();
203
- sketchesManager.setCurrentSketchId(id);
204
- const sketch = sketchesManager.getSketch(id);
205
-
206
- if (sketch) {
207
- document.getElementById('input').value = sketch.input;
208
- document.getElementById('output').value = '';
126
+ @media (min-width: 768px) {
127
+ .app {
128
+ max-width: 600px;
129
+ margin: 0 auto;
130
+ border-left: 1px solid #ddd;
131
+ border-right: 1px solid #ddd;
209
132
  }
210
133
  }
134
+ </style>
135
+ </head>
136
+ <body>
137
+ <div class="app">
138
+ <div class="header">
139
+ <h1>🦜 papagaio</h1>
140
+ <p>easy yet powerful text preprocessor</p>
141
+ </div>
142
+
143
+ <div class="tabs">
144
+ <button class="tab active" onclick="switchTab('input')">INPUT</button>
145
+ <button class="tab" onclick="switchTab('output')">OUTPUT</button>
146
+ </div>
147
+
148
+ <div class="content">
149
+ <div class="editor-container active" id="inputContainer">
150
+ <textarea id="input"></textarea>
151
+ </div>
152
+ <div class="editor-container" id="outputContainer">
153
+ <textarea id="output" readonly></textarea>
154
+ </div>
155
+ </div>
156
+ </div>
211
157
 
212
- function createNewSketch() {
213
- const name = prompt('Enter sketch name:');
214
- if (!name || !name.trim()) {
215
- alert('Please enter a valid sketch name');
216
- return;
217
- }
218
-
219
- const id = sketchesManager.createSketch(name.trim());
220
- populateSketchSelect();
221
-
222
- const sketch = sketchesManager.getSketch(id);
223
- if (sketch) {
224
- document.getElementById('input').value = sketch.input;
225
- document.getElementById('output').value = '';
226
- }
158
+ <script type="module">
159
+ import Papagaio from './papagaio.js';
160
+
161
+ const processor = new Papagaio();
162
+
163
+ function loadSaved() {
164
+ try {
165
+ const saved = localStorage.getItem('papagaio_input');
166
+ if (saved) {
167
+ document.getElementById('input').value = saved;
168
+ }
169
+ } catch {}
227
170
  }
228
171
 
229
- function renameCurrentSketch() {
230
- if (!sketchesManager.currentSketchId) {
231
- alert('No sketch selected');
232
- return;
233
- }
234
-
235
- const sketch = sketchesManager.getSketch(sketchesManager.currentSketchId);
236
- const newName = prompt('Enter new name:', sketch.name);
237
-
238
- if (!newName || !newName.trim()) {
239
- alert('Please enter a valid name');
240
- return;
241
- }
242
-
243
- sketchesManager.renameSketch(sketchesManager.currentSketchId, newName.trim());
244
- populateSketchSelect();
172
+ function saveInput() {
173
+ try {
174
+ const input = document.getElementById('input').value;
175
+ localStorage.setItem('papagaio_input', input);
176
+ } catch {}
245
177
  }
246
178
 
247
- function deleteCurrentSketch() {
248
- if (!sketchesManager.currentSketchId) {
249
- alert('No sketch selected');
250
- return;
251
- }
252
-
253
- const sketch = sketchesManager.getSketch(sketchesManager.currentSketchId);
179
+ function switchTab(tab) {
180
+ document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
181
+ document.querySelectorAll('.editor-container').forEach(c => c.classList.remove('active'));
254
182
 
255
- if (!confirm(`Are you sure you want to delete "${sketch.name}"? This action cannot be undone.`)) {
256
- return;
183
+ if (tab === 'input') {
184
+ document.querySelectorAll('.tab')[0].classList.add('active');
185
+ document.getElementById('inputContainer').classList.add('active');
186
+ } else {
187
+ document.querySelectorAll('.tab')[1].classList.add('active');
188
+ document.getElementById('outputContainer').classList.add('active');
189
+ processCode();
257
190
  }
258
-
259
- sketchesManager.deleteSketch(sketchesManager.currentSketchId);
260
- loadOrCreateDefaultSketch();
261
- populateSketchSelect();
262
191
  }
263
192
 
264
193
  function processCode() {
@@ -266,66 +195,15 @@
266
195
  const input = document.getElementById('input').value;
267
196
  const output = processor.process(input);
268
197
  document.getElementById('output').value = output;
269
- saveCurrentSketch();
198
+ saveInput();
270
199
  } catch (err) {
271
200
  document.getElementById('output').value = `Error: ${err.message}`;
272
201
  }
273
202
  }
274
203
 
275
- function exportSketches() {
276
- const data = {
277
- papagaio_sketches: localStorage.getItem('papagaio_sketches'),
278
- papagaio_current_sketch: localStorage.getItem('papagaio_current_sketch'),
279
- papagaio_config: localStorage.getItem('papagaio_config')
280
- };
281
-
282
- const blob = new Blob([JSON.stringify(data, null, 2)], {
283
- type: "application/json"
284
- });
285
-
286
- const a = document.createElement("a");
287
- a.href = URL.createObjectURL(blob);
288
- a.download = `papagaio_sketches_${Date.now()}.json`;
289
- a.click();
290
- }
291
-
292
- function importSketches(file) {
293
- if (!file) return;
294
-
295
- const reader = new FileReader();
296
-
297
- reader.onload = () => {
298
- try {
299
- const data = JSON.parse(reader.result);
300
- if (data.papagaio_sketches) {
301
- localStorage.setItem('papagaio_sketches', data.papagaio_sketches);
302
- }
303
- if (data.papagaio_current_sketch) {
304
- localStorage.setItem('papagaio_current_sketch', data.papagaio_current_sketch);
305
- }
306
- if (data.papagaio_config) {
307
- localStorage.setItem('papagaio_config', data.papagaio_config);
308
- }
309
- location.reload();
310
- } catch (err) {
311
- alert('Error importing file: ' + err.message);
312
- }
313
- };
314
-
315
- reader.readAsText(file);
316
- }
317
-
318
- setInterval(() => {
319
- saveCurrentSketch();
320
- }, 1000);
321
-
322
- window.processCode = processCode;
323
- window.switchSketch = switchSketch;
324
- window.createNewSketch = createNewSketch;
325
- window.renameCurrentSketch = renameCurrentSketch;
326
- window.deleteCurrentSketch = deleteCurrentSketch;
327
- window.exportSketches = exportSketches;
328
- window.importSketches = importSketches;
204
+ window.addEventListener('load', loadSaved);
205
+ setInterval(saveInput, 2000);
206
+ window.switchTab = switchTab;
329
207
  </script>
330
208
  </body>
331
209
  </html>
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "papagaio",
3
- "version": "0.7.3",
3
+ "version": "0.7.5",
4
4
  "description": "easy yet powerful preprocessor",
5
- "main": "src/papagaio.js",
5
+ "main": "papagaio.js",
6
6
  "type": "module",
7
7
  "scripts": {
8
8
  "test": "node tests/test.js"
@@ -1,5 +1,5 @@
1
1
  // papagaio - https://github.com/jardimdanificado/papagaio
2
- import { capture } from './louro.js';
2
+ import 'https://unpkg.com/louro@latest/louro.js';
3
3
 
4
4
  function extractBlock(p, src, i, od = p.symbols.open, cd = p.symbols.close) {
5
5
  if (od.length > 1 || cd.length > 1) {
@@ -147,7 +147,7 @@ function applyPats(p, src, pats) {
147
147
  if (regexMatches.length > 0) src = n;
148
148
  } else {
149
149
  // Usa louro para padrões normais
150
- const result = capture(src, pat.m, p.symbols);
150
+ const result = src.capture(pat.m, p.symbols);
151
151
 
152
152
  if (result.count > 0) {
153
153
  src = result.replace((match) => {
@@ -174,7 +174,7 @@ function applyPats(p, src, pats) {
174
174
 
175
175
  function esc(s) { return s.replace(/[.*+?^${}()|[\]\\""']/g, '\\$&'); }
176
176
 
177
- export class Papagaio {
177
+ export default class Papagaio {
178
178
  constructor(sigil = '$', open = '{', close = '}', pattern = 'pattern', evalKw = 'eval', blockKw = 'recursive', regexKw = 'regex', blockseqKw = 'sequential') {
179
179
  this.symbols = { pattern, open, close, sigil, eval: evalKw, block: blockKw, regex: regexKw, blockseq: blockseqKw };
180
180
  this.content = "";
package/tests/test.js CHANGED
@@ -1,4 +1,4 @@
1
- import { Papagaio } from '../src/papagaio.js';
1
+ import { Papagaio } from '../papagaio.js';
2
2
  import fs from 'fs';
3
3
  import path from 'path';
4
4
  import { fileURLToPath } from 'url';
package/mobile.html DELETED
@@ -1,209 +0,0 @@
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, user-scalable=no">
6
- <title>🦜 papagaio</title>
7
- <style>
8
- * {
9
- margin: 0;
10
- padding: 0;
11
- box-sizing: border-box;
12
- -webkit-tap-highlight-color: transparent;
13
- }
14
-
15
- body {
16
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
17
- background: #fff;
18
- color: #000;
19
- overflow: hidden;
20
- }
21
-
22
- .app {
23
- max-width: 100%;
24
- height: 100vh;
25
- display: flex;
26
- flex-direction: column;
27
- }
28
-
29
- .header {
30
- background: #fff;
31
- padding: 24px 20px;
32
- text-align: center;
33
- border-bottom: 1px solid #ddd;
34
- }
35
-
36
- .header h1 {
37
- font-size: 2rem;
38
- color: #000;
39
- margin-bottom: 4px;
40
- }
41
-
42
- .header p {
43
- font-size: 0.9rem;
44
- color: #666;
45
- }
46
-
47
- .tabs {
48
- display: flex;
49
- background: #fff;
50
- border-bottom: 1px solid #ddd;
51
- }
52
-
53
- .tab {
54
- flex: 1;
55
- padding: 18px;
56
- text-align: center;
57
- font-size: 1rem;
58
- font-weight: 500;
59
- background: #f5f5f5;
60
- border: none;
61
- cursor: pointer;
62
- transition: all 0.2s;
63
- color: #666;
64
- border-right: 1px solid #ddd;
65
- }
66
-
67
- .tab:last-child {
68
- border-right: none;
69
- }
70
-
71
- .tab.active {
72
- background: #fff;
73
- color: #000;
74
- border-bottom: 2px solid #000;
75
- }
76
-
77
- .content {
78
- flex: 1;
79
- display: flex;
80
- flex-direction: column;
81
- overflow: hidden;
82
- }
83
-
84
- .editor-container {
85
- display: none;
86
- flex-direction: column;
87
- flex: 1;
88
- }
89
-
90
- .editor-container.active {
91
- display: flex;
92
- }
93
-
94
- textarea {
95
- flex: 1;
96
- padding: 20px;
97
- border: none;
98
- font-family:
99
- ui-monospace,
100
- SFMono-Regular,
101
- Menlo,
102
- Consolas,
103
- "Liberation Mono",
104
- monospace;
105
- font-size: 15px;
106
- line-height: 1.55;
107
- font-weight: 500;
108
- letter-spacing: 0.2px;
109
- resize: none;
110
- background: #fff;
111
- color: #000;
112
- white-space: pre;
113
- overflow-x: auto;
114
- overflow-y: auto;
115
- word-break: normal;
116
- }
117
-
118
- textarea:focus {
119
- outline: none;
120
- }
121
-
122
- #output {
123
- background: #fafafa;
124
- }
125
-
126
- @media (min-width: 768px) {
127
- .app {
128
- max-width: 600px;
129
- margin: 0 auto;
130
- border-left: 1px solid #ddd;
131
- border-right: 1px solid #ddd;
132
- }
133
- }
134
- </style>
135
- </head>
136
- <body>
137
- <div class="app">
138
- <div class="header">
139
- <h1>🦜 papagaio</h1>
140
- <p>easy yet powerful text preprocessor</p>
141
- </div>
142
-
143
- <div class="tabs">
144
- <button class="tab active" onclick="switchTab('input')">INPUT</button>
145
- <button class="tab" onclick="switchTab('output')">OUTPUT</button>
146
- </div>
147
-
148
- <div class="content">
149
- <div class="editor-container active" id="inputContainer">
150
- <textarea id="input"></textarea>
151
- </div>
152
- <div class="editor-container" id="outputContainer">
153
- <textarea id="output" readonly></textarea>
154
- </div>
155
- </div>
156
- </div>
157
-
158
- <script type="module">
159
- import { Papagaio } from './src/papagaio.js';
160
-
161
- const processor = new Papagaio();
162
-
163
- function loadSaved() {
164
- try {
165
- const saved = localStorage.getItem('papagaio_input');
166
- if (saved) {
167
- document.getElementById('input').value = saved;
168
- }
169
- } catch {}
170
- }
171
-
172
- function saveInput() {
173
- try {
174
- const input = document.getElementById('input').value;
175
- localStorage.setItem('papagaio_input', input);
176
- } catch {}
177
- }
178
-
179
- function switchTab(tab) {
180
- document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
181
- document.querySelectorAll('.editor-container').forEach(c => c.classList.remove('active'));
182
-
183
- if (tab === 'input') {
184
- document.querySelectorAll('.tab')[0].classList.add('active');
185
- document.getElementById('inputContainer').classList.add('active');
186
- } else {
187
- document.querySelectorAll('.tab')[1].classList.add('active');
188
- document.getElementById('outputContainer').classList.add('active');
189
- processCode();
190
- }
191
- }
192
-
193
- function processCode() {
194
- try {
195
- const input = document.getElementById('input').value;
196
- const output = processor.process(input);
197
- document.getElementById('output').value = output;
198
- saveInput();
199
- } catch (err) {
200
- document.getElementById('output').value = `Error: ${err.message}`;
201
- }
202
- }
203
-
204
- window.addEventListener('load', loadSaved);
205
- setInterval(saveInput, 2000);
206
- window.switchTab = switchTab;
207
- </script>
208
- </body>
209
- </html>
package/src/louro.js DELETED
@@ -1,235 +0,0 @@
1
- // louro - https://github.com/jardimdanificado/papagaio
2
- function parsePattern(symbols, pat) {
3
- const t = [], S = symbols.sigil, O = symbols.open, C = symbols.close;
4
- let i = 0;
5
- while (i < pat.length) {
6
- if (pat[i] === S) {
7
- let j = i + S.length;
8
- const isDouble = pat[j] === S;
9
- if (isDouble) j++;
10
- if (pat[j] === O) {
11
- const [od, e1] = extractBlock(symbols, pat, j);
12
- if (pat[e1] === O) {
13
- const [cd, e2] = extractBlock(symbols, pat, e1);
14
- let v = '', k = e2;
15
- while (k < pat.length && /[A-Za-z0-9_]/.test(pat[k])) v += pat[k++];
16
- if (v) {
17
- let optional = pat[k] === '?';
18
- if (optional) k++;
19
-
20
- t.push({
21
- type: isDouble ? 'blockseq' : 'block',
22
- varName: v,
23
- open: unescapeDelim(od.trim()) || O,
24
- close: unescapeDelim(cd.trim()) || C,
25
- optional
26
- });
27
- i = k; continue;
28
- }
29
- }
30
- }
31
- j = i + S.length;
32
- let v = '';
33
- while (j < pat.length && /[A-Za-z0-9_]/.test(pat[j])) v += pat[j++];
34
- if (v) {
35
- const optional = pat[j] === '?';
36
- if (optional) j++;
37
- t.push({ type: 'var', varName: v, optional });
38
- i = j; continue;
39
- }
40
- t.push({ type: 'lit', value: S }); i += S.length; continue;
41
- }
42
- if (/\s/.test(pat[i])) {
43
- while (i < pat.length && /\s/.test(pat[i])) i++;
44
- t.push({ type: 'ws' }); continue;
45
- }
46
- let lit = '';
47
- while (i < pat.length && pat[i] !== S && !/\s/.test(pat[i])) lit += pat[i++];
48
- if (lit) t.push({ type: 'lit', value: lit });
49
- }
50
- return t;
51
- }
52
-
53
- function matchPattern(symbols, src, tok, pos = 0) {
54
- let cap = {};
55
- const startPos = pos;
56
-
57
- for (let ti = 0; ti < tok.length; ti++) {
58
- const t = tok[ti];
59
- if (t.type === 'ws') { while (pos < src.length && /\s/.test(src[pos])) pos++; continue; }
60
- if (t.type === 'lit') { if (!src.startsWith(t.value, pos)) return null; pos += t.value.length; continue; }
61
- if (t.type === 'var') {
62
- while (pos < src.length && /\s/.test(src[pos])) pos++;
63
- const nx = findNext(tok, ti);
64
- let v = '';
65
- if (nx && (nx.type === 'block' || nx.type === 'lit')) {
66
- const stop = nx.type === 'block' ? nx.open : nx.value;
67
- while (pos < src.length && !src.startsWith(stop, pos) && src[pos] !== '\n') v += src[pos++];
68
- v = v.trimEnd();
69
- } else {
70
- while (pos < src.length && !/\s/.test(src[pos])) v += src[pos++];
71
- }
72
- if (!v && !t.optional) return null;
73
- cap[t.varName] = v;
74
- continue;
75
- }
76
- if (t.type === 'blockseq') {
77
- let blocks = [];
78
- while (pos < src.length && src.startsWith(t.open, pos)) {
79
- const [c, e] = extractBlock(symbols, src, pos, t.open, t.close);
80
- blocks.push(c);
81
- pos = e;
82
- while (pos < src.length && /\s/.test(src[pos])) pos++;
83
- }
84
- if (!blocks.length && !t.optional) return null;
85
- cap[t.varName] = blocks;
86
- continue;
87
- }
88
- if (t.type === 'block') {
89
- if (!src.startsWith(t.open, pos)) {
90
- if (t.optional) {
91
- cap[t.varName] = '';
92
- continue;
93
- }
94
- return null;
95
- }
96
-
97
- const [c, e] = extractBlock(symbols, src, pos, t.open, t.close);
98
- cap[t.varName] = c; pos = e; continue;
99
- }
100
- }
101
- return { captures: cap, startPos, endPos: pos, matched: src.slice(startPos, pos) };
102
- }
103
-
104
- function findNext(t, i) {
105
- for (let k = i + 1; k < t.length; k++)
106
- if (t[k].type !== 'ws') return t[k];
107
- return null;
108
- }
109
-
110
- function extractBlock(symbols, src, i, od = symbols.open, cd = symbols.close) {
111
- if (od.length > 1 || cd.length > 1) {
112
- if (src.substring(i, i + od.length) === od) {
113
- i += od.length; const s = i; let d = 0;
114
- while (i < src.length) {
115
- if (src.substring(i, i + od.length) === od) { d++; i += od.length; }
116
- else if (src.substring(i, i + cd.length) === cd) {
117
- if (!d) return [src.substring(s, i), i + cd.length];
118
- d--; i += cd.length;
119
- } else i++;
120
- }
121
- return [src.substring(s), src.length];
122
- }
123
- }
124
- if (src[i] === od) {
125
- i++; const s = i;
126
- if (od === cd) { while (i < src.length && src[i] !== cd) i++; return [src.substring(s, i), i + 1]; }
127
- let d = 1;
128
- while (i < src.length && d > 0) { if (src[i] === od) d++; else if (src[i] === cd) d--; if (d > 0) i++; }
129
- return [src.substring(s, i), i + 1];
130
- }
131
- return ['', i];
132
- }
133
-
134
- function esc(s) { return s.replace(/[.*+?^${}()|[\]\\""']/g, '\\$&'); }
135
- function unescapeDelim(s) {
136
- let r = '';
137
- for (let i = 0; i < s.length; i++) {
138
- if (s[i] === '\\' && i + 1 < s.length && (s[i + 1] === '"' || s[i + 1] === "'" || s[i + 1] === '\\')) { r += s[i + 1]; i++; }
139
- else r += s[i];
140
- }
141
- return r;
142
- }
143
-
144
- export function capture(content, pattern, symbols = { sigil: '$', open: '{', close: '}' }) {
145
- const tokens = parsePattern(symbols, pattern);
146
- const matches = [];
147
-
148
- let pos = 0;
149
- while (pos < content.length) {
150
- const m = matchPattern(symbols, content, tokens, pos);
151
- if (m) {
152
- matches.push({
153
- matched: m.matched,
154
- captures: m.captures,
155
- start: m.startPos,
156
- end: m.endPos,
157
- index: matches.length
158
- });
159
- pos = m.endPos;
160
- } else {
161
- pos++;
162
- }
163
- }
164
-
165
- return {
166
- content,
167
- pattern,
168
- matches,
169
- count: matches.length,
170
-
171
- replace(replacement) {
172
- if (matches.length === 0) return { content, matches: [], count: 0 };
173
-
174
- let result = '';
175
- let lastPos = 0;
176
-
177
- for (const match of matches) {
178
- result += content.slice(lastPos, match.start);
179
-
180
- let rep = typeof replacement === 'function'
181
- ? replacement(match)
182
- : replacement;
183
-
184
- for (const [key, value] of Object.entries(match.captures)) {
185
- const varPattern = new RegExp(esc(symbols.sigil + key) + '(?![A-Za-z0-9_])', 'g');
186
- rep = rep.replace(varPattern, value);
187
- }
188
-
189
- result += rep;
190
- lastPos = match.end;
191
- }
192
-
193
- result += content.slice(lastPos);
194
-
195
- return result;
196
- },
197
-
198
- filter(predicate) {
199
- const filtered = matches.filter(predicate);
200
- return {
201
- content,
202
- pattern,
203
- matches: filtered,
204
- count: filtered.length,
205
- replace: this.replace.bind({ ...this, matches: filtered }),
206
- filter: this.filter,
207
- only: this.only
208
- };
209
- },
210
-
211
- only(n) {
212
- const len = this.matches.length;
213
- let idx = n >= 0 ? n : len + n;
214
- if (idx < 0 || idx >= len) {
215
- return {
216
- ...this,
217
- matches: []
218
- };
219
- }
220
- return {
221
- ...this,
222
- matches: [this.matches[idx]]
223
- };
224
- }
225
- };
226
- }
227
-
228
- Object.defineProperty(String.prototype, "capture", {
229
- value: function (pattern, symbols) {
230
- return capture(this.toString(), pattern, symbols);
231
- },
232
- writable: true,
233
- configurable: true,
234
- enumerable: false
235
- });