open-notepad 1.0.6 → 1.0.8

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.
Files changed (2) hide show
  1. package/bin/note.js +143 -81
  2. package/package.json +1 -1
package/bin/note.js CHANGED
@@ -141,101 +141,155 @@ async function apiFetch(config, endpoint, options = {}) {
141
141
 
142
142
  // Commands
143
143
  async function handleLogin(apiUrlArg) {
144
- clearScreen();
145
- log(`${colors.bgBlue}${colors.white}${colors.bold} Configure Notepad CLI ${colors.reset}\n`);
146
-
147
144
  const current = await loadConfig();
148
145
  const apiUrl = apiUrlArg || current.apiUrl || 'https://notepad.web.id';
149
146
 
150
- // Generate a random session code
151
- const sessionCode = 'cli_' + Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
147
+ // Present choices
148
+ const loginOptions = [
149
+ '🌐 Sign in with Google (Browser Redirect)',
150
+ '🔑 Enter API Key (Manual)'
151
+ ];
152
152
 
153
- // Try browser-based login
154
- info('Registering CLI session...');
155
- try {
156
- const regRes = await fetch(`${apiUrl.replace(/\/$/, '')}/api/cli/session`, {
157
- method: 'POST',
158
- headers: { 'Content-Type': 'application/json' },
159
- body: JSON.stringify({ code: sessionCode })
160
- });
153
+ const titleCallback = () => {
154
+ log(`${colors.bgBlue}${colors.white}${colors.bold} Configure Notepad CLI ${colors.reset}`);
155
+ log(`${colors.dim}Select how you want to authenticate with ${colors.reset}${colors.cyan}${apiUrl}${colors.reset}\n`);
156
+ };
161
157
 
162
- if (!regRes.ok) {
163
- throw new Error(`Server returned ${regRes.status}`);
164
- }
158
+ const choice = await selectMenuOption(loginOptions, titleCallback);
165
159
 
166
- // Open browser
167
- const authPageUrl = `${apiUrl.replace(/\/$/, '')}/auth-cli-20260628dontuseforyounote?session=${sessionCode}`;
168
- info(`Opening browser for authentication...`);
169
- log(`\n${colors.dim}If browser does not open automatically, visit:${colors.reset}`);
170
- log(`${colors.bold}${colors.cyan}${authPageUrl}${colors.reset}\n`);
171
-
172
- // Platform-specific browser open
173
- const openCmd = process.platform === 'win32' ? 'start'
174
- : process.platform === 'darwin' ? 'open'
175
- : 'xdg-open';
176
- const child = spawn(openCmd, [authPageUrl], { shell: true, stdio: 'ignore', detached: true });
177
- child.unref();
178
-
179
- // Poll for completion
180
- info('Waiting for browser authorization...');
181
- const maxAttempts = 120; // 2 minutes (polling every 1.5s)
182
- let attempts = 0;
160
+ if (choice === -1) {
161
+ return;
162
+ }
163
+
164
+ if (choice === 0) {
165
+ // Google Login (Browser flow)
166
+ clearScreen();
167
+ log(`${colors.bgBlue}${colors.white}${colors.bold} Google Authentication ${colors.reset}\n`);
183
168
 
184
- while (attempts < maxAttempts) {
185
- await new Promise(r => setTimeout(r, 1500));
186
- attempts++;
169
+ // Generate a random session code
170
+ const sessionCode = 'cli_' + Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
187
171
 
188
- try {
189
- const pollRes = await fetch(`${apiUrl.replace(/\/$/, '')}/api/cli/session/${sessionCode}`);
190
-
191
- if (pollRes.status === 404) {
192
- // Session expired or not found
193
- if (attempts > 10) {
194
- error('Session expired. Please try again.');
195
- return;
196
- }
197
- continue;
172
+ info('Registering CLI session...');
173
+ try {
174
+ const regRes = await fetch(`${apiUrl.replace(/\/$/, '')}/api/cli/session`, {
175
+ method: 'POST',
176
+ headers: { 'Content-Type': 'application/json' },
177
+ body: JSON.stringify({ code: sessionCode })
178
+ });
179
+
180
+ if (!regRes.ok) {
181
+ throw new Error(`Server returned ${regRes.status}`);
182
+ }
183
+
184
+ // Open browser
185
+ const authPageUrl = `${apiUrl.replace(/\/$/, '')}/auth-cli-20260628dontuseforyounote?session=${sessionCode}`;
186
+ info(`Opening browser for authentication...`);
187
+ log(`\n${colors.dim}If browser does not open automatically, visit:${colors.reset}`);
188
+ log(`${colors.bold}${colors.cyan}${authPageUrl}${colors.reset}\n`);
189
+
190
+ // Platform-specific browser open
191
+ const openCmd = process.platform === 'win32' ? 'start'
192
+ : process.platform === 'darwin' ? 'open'
193
+ : 'xdg-open';
194
+ const child = spawn(openCmd, [authPageUrl], { shell: true, stdio: 'ignore', detached: true });
195
+ child.unref();
196
+
197
+ // Poll for completion
198
+ info('Waiting for browser authorization...');
199
+ log(`${colors.dim}Press Esc at any time to cancel and return to menu.${colors.reset}\n`);
200
+ const maxAttempts = 120; // 2 minutes (polling every 1.5s)
201
+ let attempts = 0;
202
+ let cancelled = false;
203
+
204
+ // Listen to Esc keypress during polling
205
+ const hasTTY = process.stdin.isTTY && typeof process.stdin.setRawMode === 'function';
206
+ const onPollKeypress = (str, key) => {
207
+ if (key && (key.name === 'escape' || (key.ctrl && key.name === 'c'))) {
208
+ cancelled = true;
198
209
  }
210
+ };
199
211
 
200
- if (pollRes.ok) {
201
- const data = await pollRes.json();
212
+ if (hasTTY) {
213
+ readline.emitKeypressEvents(process.stdin);
214
+ try {
215
+ process.stdin.setRawMode(true);
216
+ } catch (e) {}
217
+ process.stdin.resume();
218
+ process.stdin.on('keypress', onPollKeypress);
219
+ }
220
+
221
+ while (attempts < maxAttempts && !cancelled) {
222
+ await new Promise(r => setTimeout(r, 1500));
223
+ if (cancelled) break;
224
+ attempts++;
225
+
226
+ try {
227
+ const pollRes = await fetch(`${apiUrl.replace(/\/$/, '')}/api/cli/session/${sessionCode}`);
202
228
 
203
- if (data.status === 'completed' && data.room_id && data.api_key) {
204
- clearScreen();
205
- log(`${colors.bgBlue}${colors.white}${colors.bold} CLI Connected Successfully! ${colors.reset}\n`);
206
- await saveConfig({
207
- apiUrl,
208
- roomId: data.room_id,
209
- apiKey: data.api_key
210
- });
211
- success(`Linked to room: ${colors.bold}${data.room_id}.notepad.web.id${colors.reset}`);
212
- info('You can now use all CLI commands. Try: note list');
213
- return;
229
+ if (pollRes.status === 404) {
230
+ if (attempts > 10) {
231
+ error('Session expired. Please try again.');
232
+ break;
233
+ }
234
+ continue;
235
+ }
236
+
237
+ if (pollRes.ok) {
238
+ const data = await pollRes.json();
239
+
240
+ if (data.status === 'completed' && data.room_id && data.api_key) {
241
+ clearScreen();
242
+ log(`${colors.bgBlue}${colors.white}${colors.bold} CLI Connected Successfully! ${colors.reset}\n`);
243
+ await saveConfig({
244
+ apiUrl,
245
+ roomId: data.room_id,
246
+ apiKey: data.api_key
247
+ });
248
+ success(`Linked to room: ${colors.bold}${data.room_id}.notepad.web.id${colors.reset}`);
249
+ info('You can now use all CLI commands. Try: note list');
250
+ cancelled = false; // Reset to indicate success
251
+ break;
252
+ }
214
253
  }
254
+ } catch (pollErr) {
255
+ // Silently continue polling
256
+ }
257
+
258
+ if (attempts % 4 === 0) {
259
+ process.stdout.write(`\r${colors.dim} Polling... (${Math.round(attempts * 1.5)}s elapsed, waiting for browser auth)${colors.reset} `);
215
260
  }
216
- } catch (pollErr) {
217
- // Silently continue polling
218
261
  }
219
262
 
220
- // Progress indicator
221
- if (attempts % 4 === 0) {
222
- process.stdout.write(`\r${colors.dim} Polling... (${attempts}s elapsed, waiting for browser auth)${colors.reset} `);
263
+ if (hasTTY) {
264
+ try {
265
+ process.stdin.off('keypress', onPollKeypress);
266
+ process.stdin.setRawMode(false);
267
+ } catch (e) {}
223
268
  }
224
- }
225
269
 
226
- process.stdout.write('\n');
227
- warning('Browser authorization timed out.');
228
- info('Falling back to manual configuration...\n');
229
- } catch (e) {
230
- warning(`Browser login unavailable: ${e.message}`);
231
- info('Using manual configuration instead...\n');
232
- }
270
+ if (cancelled) {
271
+ process.stdout.write('\n');
272
+ warning('Browser authorization cancelled by user.');
273
+ return;
274
+ }
233
275
 
234
- // Fallback: manual configuration
235
- const roomId = await ask('Enter Subdomain/Room ID (e.g. "saya")', current.roomId);
236
- const apiKey = await ask('Enter Room API Token/Key', current.apiKey);
276
+ if (attempts >= maxAttempts) {
277
+ process.stdout.write('\n');
278
+ warning('Browser authorization timed out.');
279
+ }
280
+ } catch (e) {
281
+ error(`Browser login unavailable: ${e.message}`);
282
+ }
283
+ } else {
284
+ // Manual API Key configuration
285
+ clearScreen();
286
+ log(`${colors.bgBlue}${colors.white}${colors.bold} Manual API Key Config ${colors.reset}\n`);
287
+
288
+ const roomId = await ask('Enter Subdomain/Room ID (e.g. "saya")', current.roomId);
289
+ const apiKey = await ask('Enter Room API Token/Key', current.apiKey);
237
290
 
238
- await saveConfig({ apiUrl, roomId, apiKey });
291
+ await saveConfig({ apiUrl, roomId, apiKey });
292
+ }
239
293
  }
240
294
 
241
295
  async function handleList() {
@@ -633,7 +687,7 @@ function selectMenuOption(options, titleCallback) {
633
687
  log(` ${colors.dim}${opt}${colors.reset}`);
634
688
  }
635
689
  });
636
- log(`\n${colors.dim}Use Up/Down arrows to navigate, Enter to select.${colors.reset}`);
690
+ log(`\n${colors.dim}Use Up/Down arrows to navigate, Enter to select, Esc to go back.${colors.reset}`);
637
691
  };
638
692
 
639
693
  readline.emitKeypressEvents(process.stdin);
@@ -665,6 +719,12 @@ function selectMenuOption(options, titleCallback) {
665
719
  } catch (e) {}
666
720
  process.stdin.off('keypress', onKeypress);
667
721
  resolve(activeIndex);
722
+ } else if (key && key.name === 'escape') {
723
+ try {
724
+ process.stdin.setRawMode(false);
725
+ } catch (e) {}
726
+ process.stdin.off('keypress', onKeypress);
727
+ resolve(-1);
668
728
  }
669
729
  };
670
730
 
@@ -694,6 +754,12 @@ async function startMainMenu() {
694
754
 
695
755
  const choice = await selectMenuOption(options, titleCallback);
696
756
 
757
+ if (choice === -1 || choice === 6) {
758
+ clearScreen();
759
+ log('Goodbye!');
760
+ process.exit(0);
761
+ }
762
+
697
763
  switch (choice) {
698
764
  case 0:
699
765
  await handleList();
@@ -719,10 +785,6 @@ async function startMainMenu() {
719
785
  await handleLogin();
720
786
  await ask('Press Enter to return to menu');
721
787
  break;
722
- case 6:
723
- clearScreen();
724
- log('Goodbye!');
725
- process.exit(0);
726
788
  }
727
789
  }
728
790
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-notepad",
3
- "version": "1.0.6",
3
+ "version": "1.0.8",
4
4
  "description": "CLI tool for notepad.web.id to access, edit, create, and list room notes interactively.",
5
5
  "type": "module",
6
6
  "bin": {