latex-stickies 1.4.2 → 1.4.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 +1 -0
- package/package.json +4 -1
- package/scripts/brand-electron.js +8 -0
- package/src/editor/entry.js +4 -2
- package/src/main.js +40 -3
- package/src/renderer/live-editor.js +51 -8
- package/src/renderer/note.css +5 -1
- package/src/renderer/vendor/codemirror.js +18 -18
package/README.md
CHANGED
|
@@ -169,6 +169,7 @@ node scripts/render-check.js # the first-run note renders correctly
|
|
|
169
169
|
node scripts/ghost-check.js # autocomplete suggests, and Tab accepts
|
|
170
170
|
node scripts/snapshot-check.js # a long note is captured whole
|
|
171
171
|
node scripts/conflict-check.js # the changed-on-disk banner behaves
|
|
172
|
+
node scripts/denied-check.js # a notes folder it may not read is reported
|
|
172
173
|
node scripts/verify-install.js # the install path, end to end (macOS)
|
|
173
174
|
```
|
|
174
175
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "latex-stickies",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.3",
|
|
4
4
|
"description": "Sticky notes for your desktop that render LaTeX and Markdown.",
|
|
5
5
|
"main": "src/main.js",
|
|
6
6
|
"scripts": {
|
|
@@ -25,6 +25,9 @@
|
|
|
25
25
|
],
|
|
26
26
|
"mac": {
|
|
27
27
|
"category": "public.app-category.productivity",
|
|
28
|
+
"extendInfo": {
|
|
29
|
+
"NSDocumentsFolderUsageDescription": "LaTeX Stickies keeps your notes as Markdown files in Documents, so you can read and edit them with any other app."
|
|
30
|
+
},
|
|
28
31
|
"target": [
|
|
29
32
|
"dmg",
|
|
30
33
|
"zip"
|
|
@@ -94,6 +94,10 @@ function isCurrent(version) {
|
|
|
94
94
|
}
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
+
/** Shown in the macOS prompt asking to let the app read the notes folder. */
|
|
98
|
+
const DOCUMENTS_REASON = 'LaTeX Stickies keeps your notes as Markdown files in '
|
|
99
|
+
+ 'Documents, so you can read and edit them with any other app.';
|
|
100
|
+
|
|
97
101
|
function setPlist(plist, key, value) {
|
|
98
102
|
try {
|
|
99
103
|
run('plutil', ['-replace', key, '-string', value, plist]);
|
|
@@ -126,6 +130,10 @@ function build(app, version) {
|
|
|
126
130
|
setPlist(plist, 'CFBundleDisplayName', NAME);
|
|
127
131
|
setPlist(plist, 'CFBundleIdentifier', APP_ID);
|
|
128
132
|
setPlist(plist, 'CFBundleExecutable', NAME);
|
|
133
|
+
// Without this key macOS denies the notes folder without ever asking. The
|
|
134
|
+
// app then starts, reads nothing and shows no window, which looks exactly
|
|
135
|
+
// like a launch that failed.
|
|
136
|
+
setPlist(plist, 'NSDocumentsFolderUsageDescription', DOCUMENTS_REASON);
|
|
129
137
|
|
|
130
138
|
const from = path.join(TARGET, 'Contents', 'MacOS', 'Electron');
|
|
131
139
|
const to = path.join(TARGET, 'Contents', 'MacOS', NAME);
|
package/src/editor/entry.js
CHANGED
|
@@ -20,7 +20,9 @@ import {
|
|
|
20
20
|
defaultKeymap, history, historyKeymap, indentWithTab,
|
|
21
21
|
cursorLineUp, cursorLineDown, selectLineUp, selectLineDown,
|
|
22
22
|
} from '@codemirror/commands';
|
|
23
|
-
import {
|
|
23
|
+
import {
|
|
24
|
+
markdown, markdownLanguage, insertNewlineContinueMarkup,
|
|
25
|
+
} from '@codemirror/lang-markdown';
|
|
24
26
|
import {
|
|
25
27
|
syntaxTree, HighlightStyle, syntaxHighlighting, defaultHighlightStyle,
|
|
26
28
|
LanguageDescription,
|
|
@@ -70,7 +72,7 @@ window.CM = {
|
|
|
70
72
|
EditorView, keymap, Decoration, WidgetType, ViewPlugin,
|
|
71
73
|
defaultKeymap, history, historyKeymap, indentWithTab,
|
|
72
74
|
cursorLineUp, cursorLineDown, selectLineUp, selectLineDown,
|
|
73
|
-
markdown, markdownLanguage,
|
|
75
|
+
markdown, markdownLanguage, insertNewlineContinueMarkup,
|
|
74
76
|
syntaxTree, HighlightStyle, syntaxHighlighting, defaultHighlightStyle,
|
|
75
77
|
tags, codeLanguages,
|
|
76
78
|
search, searchKeymap, highlightSelectionMatches, openSearchPanel,
|
package/src/main.js
CHANGED
|
@@ -527,6 +527,37 @@ function watchNotesFolder() {
|
|
|
527
527
|
});
|
|
528
528
|
}
|
|
529
529
|
|
|
530
|
+
/**
|
|
531
|
+
* macOS can refuse the notes folder outright, and quietly.
|
|
532
|
+
*
|
|
533
|
+
* Documents is protected, and permission is tied to the app's signature -- so
|
|
534
|
+
* a rebuilt copy is a different app to the system and starts again with no
|
|
535
|
+
* access. Reading the folder then fails, nothing is restored and no window
|
|
536
|
+
* opens, which is indistinguishable from a launch that crashed. Say what
|
|
537
|
+
* happened instead, and offer the settings pane that fixes it.
|
|
538
|
+
*/
|
|
539
|
+
function reportNoAccess(err) {
|
|
540
|
+
// Printed as well as shown: the dialog is the answer for the person at the
|
|
541
|
+
// screen, this line is what a harness or a bug report can see.
|
|
542
|
+
console.error(`notes folder refused: ${err.code} ${store.DIR}`);
|
|
543
|
+
const settings = 'x-apple.systempreferences:com.apple.preference.security'
|
|
544
|
+
+ '?Privacy_DocumentsFolder';
|
|
545
|
+
const choice = dialog.showMessageBoxSync({
|
|
546
|
+
type: 'error',
|
|
547
|
+
message: 'LaTeX Stickies cannot read your notes',
|
|
548
|
+
detail: `macOS is not letting it open ${store.DIR}.\n\n`
|
|
549
|
+
+ 'Allow it under Privacy & Security, in Files and Folders, then open '
|
|
550
|
+
+ `the app again.\n\n(${err.code}: ${err.message})`,
|
|
551
|
+
buttons: ['Open Privacy Settings', 'Quit'],
|
|
552
|
+
defaultId: 0,
|
|
553
|
+
});
|
|
554
|
+
if (choice === 0 && process.platform === 'darwin') shell.openExternal(settings);
|
|
555
|
+
app.quit();
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
/** True when the filesystem said no, rather than the folder being absent. */
|
|
559
|
+
const denied = (err) => err && (err.code === 'EPERM' || err.code === 'EACCES');
|
|
560
|
+
|
|
530
561
|
app.whenReady().then(async () => {
|
|
531
562
|
// Run from npm there is no .app bundle of our own to carry the icon, so
|
|
532
563
|
// macOS would show the generic Electron atom in the Dock. Set it explicitly.
|
|
@@ -546,9 +577,15 @@ app.whenReady().then(async () => {
|
|
|
546
577
|
}
|
|
547
578
|
}
|
|
548
579
|
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
580
|
+
try {
|
|
581
|
+
await buildMenu();
|
|
582
|
+
restoreNotes();
|
|
583
|
+
watchNotesFolder();
|
|
584
|
+
} catch (err) {
|
|
585
|
+
if (!denied(err)) throw err;
|
|
586
|
+
reportNoAccess(err);
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
552
589
|
|
|
553
590
|
if (SMOKE_CLOSE_MS) {
|
|
554
591
|
setTimeout(() => {
|
|
@@ -19,7 +19,7 @@ const {
|
|
|
19
19
|
WidgetType, ViewPlugin, keymap, Prec,
|
|
20
20
|
defaultKeymap, history, historyKeymap, indentWithTab,
|
|
21
21
|
cursorLineUp, cursorLineDown, selectLineUp, selectLineDown,
|
|
22
|
-
markdown, markdownLanguage, codeLanguages,
|
|
22
|
+
markdown, markdownLanguage, codeLanguages, insertNewlineContinueMarkup,
|
|
23
23
|
syntaxTree, HighlightStyle, syntaxHighlighting, defaultHighlightStyle, tags,
|
|
24
24
|
search, searchKeymap, highlightSelectionMatches,
|
|
25
25
|
} = window.CM;
|
|
@@ -312,6 +312,8 @@ const HIDE = Decoration.replace({});
|
|
|
312
312
|
*/
|
|
313
313
|
const MARKER = Decoration.mark({ class: 'cm-md-marker' });
|
|
314
314
|
|
|
315
|
+
|
|
316
|
+
|
|
315
317
|
/** Elements whose markers are shown while the caret is inside them. */
|
|
316
318
|
const REVEAL = new Set([
|
|
317
319
|
'Emphasis', 'StrongEmphasis', 'Strikethrough', 'InlineCode', 'FencedCode',
|
|
@@ -616,8 +618,13 @@ const markerReveal = ViewPlugin.fromClass(class {
|
|
|
616
618
|
this.sync();
|
|
617
619
|
}
|
|
618
620
|
|
|
619
|
-
update(
|
|
620
|
-
|
|
621
|
+
update() {
|
|
622
|
+
// Every update, not only the ones that moved the caret. The parser runs
|
|
623
|
+
// behind the keystroke, so the first "#" of a heading arrives as a plain
|
|
624
|
+
// character and becomes a marker on a later update that changed neither
|
|
625
|
+
// the document nor the selection. Skipping those left the reveal one
|
|
626
|
+
// keystroke behind: a heading stayed hidden while it was being typed.
|
|
627
|
+
this.sync();
|
|
621
628
|
}
|
|
622
629
|
|
|
623
630
|
destroy() {
|
|
@@ -630,12 +637,21 @@ const markerReveal = ViewPlugin.fromClass(class {
|
|
|
630
637
|
let from = -1;
|
|
631
638
|
let to = -1;
|
|
632
639
|
if (!snapshotMode) {
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
640
|
+
const tree = syntaxTree(view.state);
|
|
641
|
+
// Both sides of the caret, and the side before it first. Typing "###"
|
|
642
|
+
// leaves the caret at the end of the line, where the node *starting*
|
|
643
|
+
// here is the document, not the heading -- so looking forward only, a
|
|
644
|
+
// heading stayed hidden the whole time you were typing it.
|
|
645
|
+
const enclosing = (side) => {
|
|
646
|
+
for (let node = tree.resolveInner(sel.from, side); node; node = node.parent) {
|
|
647
|
+
if (REVEAL.has(node.name) && sel.to <= node.to) return node;
|
|
638
648
|
}
|
|
649
|
+
return null;
|
|
650
|
+
};
|
|
651
|
+
const node = enclosing(-1) || enclosing(1);
|
|
652
|
+
if (node) {
|
|
653
|
+
from = node.from;
|
|
654
|
+
to = node.to;
|
|
639
655
|
}
|
|
640
656
|
}
|
|
641
657
|
for (const el of view.contentDOM.querySelectorAll('.cm-md-marker')) {
|
|
@@ -669,9 +685,24 @@ const livePreview = StateField.define({
|
|
|
669
685
|
|
|
670
686
|
/* ---------- how markdown reads ---------- */
|
|
671
687
|
|
|
688
|
+
/**
|
|
689
|
+
* Markers are punctuation, not prose, and should not compete with the text
|
|
690
|
+
* they mark up. CodeMirror's default paints them #404740 -- all but the ink
|
|
691
|
+
* colour -- because every marker carries tags.processingInstruction, a child
|
|
692
|
+
* of meta, and defaultHighlightStyle is loaded here for its code colours.
|
|
693
|
+
*
|
|
694
|
+
* A flat grey rather than a faded ink: fading a marker into the paper is what
|
|
695
|
+
* made an H1 and an H2 indistinguishable while one was being typed.
|
|
696
|
+
*/
|
|
697
|
+
const MARKER_INK = '#757575';
|
|
698
|
+
|
|
672
699
|
const markdownStyle = HighlightStyle.define([
|
|
673
700
|
// textDecoration: 'none' is deliberate -- defaultHighlightStyle, loaded for
|
|
674
701
|
// the colours inside code fences, underlines every heading.
|
|
702
|
+
// One entry covers every marker: the hashes, the stars, the backticks, the
|
|
703
|
+
// quote arrow, the list dash, a table's pipes and a strikethrough's tildes
|
|
704
|
+
// all carry this tag.
|
|
705
|
+
{ tag: tags.processingInstruction, color: MARKER_INK },
|
|
675
706
|
{ tag: tags.heading, textDecoration: 'none' },
|
|
676
707
|
{ tag: tags.heading1, fontSize: '1.35em', fontWeight: '600', textDecoration: 'none' },
|
|
677
708
|
{ tag: tags.heading2, fontSize: '1.18em', fontWeight: '600', textDecoration: 'none' },
|
|
@@ -777,6 +808,17 @@ function verticalStep(forward, base, extend) {
|
|
|
777
808
|
};
|
|
778
809
|
}
|
|
779
810
|
|
|
811
|
+
/**
|
|
812
|
+
* Enter carries a list on: a new bullet, the next number, another empty task
|
|
813
|
+
* box, or a second quote line -- and a second Enter on an item with nothing in
|
|
814
|
+
* it ends the list instead of adding to it.
|
|
815
|
+
*
|
|
816
|
+
* Without this, pressing Enter after "- [ ] milk" left a bare line, and typing
|
|
817
|
+
* "[ ] eggs" there looks like a task and is not one: the box only means
|
|
818
|
+
* anything inside a list item.
|
|
819
|
+
*/
|
|
820
|
+
const continueList = [{ key: 'Enter', run: insertNewlineContinueMarkup }];
|
|
821
|
+
|
|
780
822
|
const verticalKeymap = [
|
|
781
823
|
{ key: 'ArrowUp', run: verticalStep(false, cursorLineUp), preventDefault: true },
|
|
782
824
|
{ key: 'ArrowDown', run: verticalStep(true, cursorLineDown), preventDefault: true },
|
|
@@ -804,6 +846,7 @@ function createLiveEditor({ parent, doc, onChange }) {
|
|
|
804
846
|
// Ahead of the defaults, so Mod-i and friends are not swallowed.
|
|
805
847
|
Prec.high(keymap.of(shortcuts)),
|
|
806
848
|
Prec.high(keymap.of(verticalKeymap)),
|
|
849
|
+
Prec.high(keymap.of(continueList)),
|
|
807
850
|
// Search ahead of the defaults: Cmd+F must open the panel rather than
|
|
808
851
|
// fall through to anything else bound to it.
|
|
809
852
|
Prec.high(keymap.of(searchKeymap)),
|
package/src/renderer/note.css
CHANGED
|
@@ -237,9 +237,13 @@ body.editing #tools > * { visibility: visible; }
|
|
|
237
237
|
opacity: 0;
|
|
238
238
|
transition: max-width 0.12s ease, opacity 0.12s ease;
|
|
239
239
|
}
|
|
240
|
+
/* Full strength, not a hint. A revealed marker is there to be edited -- you
|
|
241
|
+
are counting hashes to see what level a heading is, or putting the caret
|
|
242
|
+
between a pair of stars -- and a washed-out one is hard to read against
|
|
243
|
+
the paper. It is the hiding that keeps the note clean, not the fading. */
|
|
240
244
|
.cm-editor .cm-md-marker-open {
|
|
241
245
|
max-width: 4em;
|
|
242
|
-
opacity:
|
|
246
|
+
opacity: 1;
|
|
243
247
|
}
|
|
244
248
|
|
|
245
249
|
/* Sits at the end of a block's opening fence line, beside the language name. */
|