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 +4 -3
- package/bin/latex-stickies.js +10 -0
- package/build/icon.icns +0 -0
- package/build/icon.png +0 -0
- package/package.json +1 -1
- package/src/main.js +22 -1
- package/src/store.js +34 -8
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
|
|
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.
|
|
107
|
-
is
|
|
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
|
|
package/bin/latex-stickies.js
CHANGED
|
@@ -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
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
|
-
|
|
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
|
-
|
|
177
|
-
|
|
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
|
}
|