latex-stickies 1.3.1 → 1.3.3

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
@@ -88,7 +88,7 @@ Use `Ctrl` in place of `Cmd` on Windows and Linux.
88
88
  | | |
89
89
  |---|---|
90
90
  | `Cmd+N` | New note |
91
- | `Cmd+W` | Close note (it reopens next launch) |
91
+ | `Cmd+W` | Close note (it stays closed until you reopen it) |
92
92
  | `Cmd+Shift+Backspace` | Delete note, permanently |
93
93
  | `Cmd+B` / `Cmd+I` / `Cmd+E` | Bold / italic / code |
94
94
  | `Cmd+K` | Link |
@@ -103,8 +103,9 @@ On Windows and Linux the note windows have no menu bar, so the **☰** button
103
103
  in a note's toolbar opens the same menu. Every command is there.
104
104
 
105
105
  The **All Notes** menu lists every note you have. A tick means it is on screen;
106
- click to open a note, or click a ticked one to close it. So a note you closed
107
- is one click away rather than lost until the next launch.
106
+ click to open a note, or click a ticked one to close it. Closing a note is
107
+ remembered, so the desk you left is the desk you come back to -- the app
108
+ reopens what was on screen rather than every note you have ever written.
108
109
 
109
110
  ## Where notes live
110
111
 
@@ -21,6 +21,16 @@ try {
21
21
  process.exit(1);
22
22
  }
23
23
 
24
+ // Name the Electron shell before launching it, not only at install time.
25
+ // postinstall scripts are increasingly blocked -- npm warns about them by
26
+ // default now, and plenty of people and companies set ignore-scripts -- and a
27
+ // user who installs with them off gets "Electron" in their Dock. Doing it here
28
+ // as well means the name is right by the time macOS reads the bundle, whatever
29
+ // happened during the install. It returns immediately once done.
30
+ try {
31
+ require('../scripts/brand-electron.js');
32
+ } catch (_) { /* cosmetic: never block a launch over the name */ }
33
+
24
34
  const appDir = path.join(__dirname, '..');
25
35
  const child = spawn(electron, [appDir, ...process.argv.slice(2)], {
26
36
  detached: true,
package/build/icon.icns CHANGED
Binary file
package/build/icon.png CHANGED
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "latex-stickies",
3
- "version": "1.3.1",
3
+ "version": "1.3.3",
4
4
  "description": "Sticky notes for your desktop that render LaTeX and Markdown.",
5
5
  "main": "src/main.js",
6
6
  "scripts": {
package/src/main.js CHANGED
@@ -128,6 +128,8 @@ function openNote(note) {
128
128
  return existing;
129
129
  }
130
130
 
131
+ store.upsert({ id: note.id, open: true });
132
+
131
133
  const win = new BrowserWindow({
132
134
  ...onScreenBounds(note.bounds),
133
135
  minWidth: 220,
@@ -158,6 +160,11 @@ function openNote(note) {
158
160
  windows.delete(note.id);
159
161
  smokeLog(`windows=${windows.size}`);
160
162
 
163
+ // Closing a note is a decision that should survive a restart. Quitting is
164
+ // not: an app that shuts down closes every window, and treating that as
165
+ // "the user closed them all" would greet them with an empty desk.
166
+ if (!quitting) store.upsert({ id: note.id, open: false });
167
+
161
168
  // macOS apps outlive their windows; the Dock icon is the way back.
162
169
  // Elsewhere there is no way back, so an app with no windows is just an
163
170
  // invisible process -- and the next launch hands off to it and appears to
@@ -205,13 +212,24 @@ function createNote(seed = {}) {
205
212
  return note;
206
213
  }
207
214
 
215
+ /**
216
+ * Opens the notes that were on screen when the app was last used.
217
+ *
218
+ * Not every note: someone with thirty notes and three on screen wants three
219
+ * windows back, and reopening all of them made closing one pointless. A note
220
+ * closed on purpose stays closed until it is opened again from All Notes.
221
+ */
208
222
  function restoreNotes() {
209
223
  const notes = store.all();
210
224
  if (notes.length === 0) {
211
225
  createNote({ body: WELCOME });
212
226
  return;
213
227
  }
214
- notes.forEach(openNote);
228
+
229
+ const open = notes.filter((n) => n.open !== false);
230
+ // Never nothing. A launch that puts no window on screen looks like a failed
231
+ // launch, and on Windows it leaves an invisible process with no way back.
232
+ (open.length ? open : [notes[notes.length - 1]]).forEach(openNote);
215
233
  }
216
234
 
217
235
  const { WELCOME } = require('./welcome');
@@ -442,6 +460,8 @@ app.on('second-instance', surfaceNotes);
442
460
  * quietly overwriting what was changed on disk.
443
461
  */
444
462
  let stopWatchingNotes = null;
463
+ /** Set while the app is shutting down, so closing windows is not a choice. */
464
+ let quitting = false;
445
465
 
446
466
  /**
447
467
  * Everything that must happen before the process goes away.
@@ -452,6 +472,7 @@ let stopWatchingNotes = null;
452
472
  * process -- alive after the last window has gone.
453
473
  */
454
474
  function prepareToExit() {
475
+ quitting = true;
455
476
  if (stopWatchingNotes) {
456
477
  stopWatchingNotes();
457
478
  stopWatchingNotes = null;
package/src/store.js CHANGED
@@ -25,7 +25,7 @@ const INDEX = path.join(DIR, '.stickies.json');
25
25
  /** The old single-file store, migrated from once and then left alone. */
26
26
  const LEGACY = path.join(app.getPath('userData'), 'notes.json');
27
27
 
28
- const DEFAULTS = { color: 'yellow', fontSize: 15, alwaysOnTop: false };
28
+ const DEFAULTS = { color: 'yellow', fontSize: 15, alwaysOnTop: false, open: true };
29
29
 
30
30
  let notes = null; // [{ id, file, body, color, fontSize, alwaysOnTop, bounds }]
31
31
  let timer = null;
@@ -94,6 +94,9 @@ function load() {
94
94
  ...saved,
95
95
  // The file is the source of truth for content; the index only decorates.
96
96
  id: saved.id || file,
97
+ // A file that appeared in the folder from outside has no index entry,
98
+ // and should show itself rather than stay invisible.
99
+ open: saved.open !== false,
97
100
  file,
98
101
  body,
99
102
  };
@@ -173,26 +176,34 @@ function writeIndex(meta) {
173
176
  }
174
177
  }
175
178
 
176
- function flush() {
177
- timer = null;
179
+ /** The sidecar index alone: filenames to ids, colours, bounds. */
180
+ function flushIndex() {
178
181
  const meta = {};
179
182
  for (const note of all()) {
180
- try {
181
- writeFileAtomic(path.join(DIR, note.file), note.body || '');
182
- } catch (err) {
183
- console.error(`failed to save ${note.file}`, err);
184
- }
185
183
  meta[note.file] = {
186
184
  id: note.id,
187
185
  color: note.color,
188
186
  fontSize: note.fontSize,
189
187
  alwaysOnTop: note.alwaysOnTop,
190
188
  bounds: note.bounds,
189
+ open: note.open !== false,
191
190
  };
192
191
  }
193
192
  writeIndex(meta);
194
193
  }
195
194
 
195
+ function flush() {
196
+ timer = null;
197
+ for (const note of all()) {
198
+ try {
199
+ writeFileAtomic(path.join(DIR, note.file), note.body || '');
200
+ } catch (err) {
201
+ console.error(`failed to save ${note.file}`, err);
202
+ }
203
+ }
204
+ flushIndex();
205
+ }
206
+
196
207
  // Writes are frequent (every keystroke, every window drag), so coalesce them.
197
208
  function save() {
198
209
  if (timer) return;
@@ -234,8 +245,23 @@ function upsert(note) {
234
245
  const next = slugFor(list[i].body, taken, path.extname(before.file) || '.md');
235
246
  try {
236
247
  const from = path.join(DIR, before.file);
248
+ // Both names count as our own writing, or the rename looks like an
249
+ // outside edit and the watcher reloads over the top of it.
250
+ justWrote.set(before.file, Date.now());
251
+ justWrote.set(next, Date.now());
237
252
  if (fs.existsSync(from)) fs.renameSync(from, path.join(DIR, next));
238
253
  list[i].file = next;
254
+ // Write this note's body now too. The rename is immediate but the body
255
+ // is on the 400ms timer, so a reload in between would read the new file
256
+ // and find the old text -- after which the filename stops following the
257
+ // title, because it no longer looks like a name we chose.
258
+ writeFileAtomic(path.join(DIR, next), list[i].body || '');
259
+ // The index must follow the rename at once, not on the 400ms timer.
260
+ // A note's id lives in the index, keyed by filename; anything that
261
+ // reloads in the gap finds a file the index has never heard of, gives
262
+ // it a fresh id, and orphans the window still holding the old one --
263
+ // whose next keystroke then writes a second copy of the same note.
264
+ flushIndex();
239
265
  } catch (err) {
240
266
  console.error(`could not rename ${before.file} to ${next}`, err);
241
267
  }