marksmith 0.4.1 → 0.4.4

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 287d682aee074cbb2c835b88f6891d20e9ed0f2ef53a16984c3f81e474664115
4
- data.tar.gz: 4eff297c350411f5ad3bd61c24e758a359255377750452097cce8a2aef3c8603
3
+ metadata.gz: bac426dee50d826889905eef507ee85bf15741a4f4d7ed85d28f88b77d60c7f7
4
+ data.tar.gz: 11374fff22c72c904840bf9435de06fafd6e9cc23d360f9fb873277757603479
5
5
  SHA512:
6
- metadata.gz: 595a0c5db128e373d5ebf2b0541fed436ede80c48a1a8f9b2fa725c53b3a25d6ebccb915b0712d77a1da031a461197e662ee55d502b6e23f0ea1eecaa5fa4fc6
7
- data.tar.gz: 50bb0a632856c935057c230d4e7eea6f79ae4563186b2d2eb85641995efee4ece418ec5edba860e69968e0351dc4256d21e76d8156e736395c8c460a29f2cd69
6
+ metadata.gz: 6a0f7eca2d748c4b8ac57b33e45a07aace4cdcec9195af7d7ce781a7230b5ad60edaff8cde18cc36aecb6e2aabe07142703681c2678a8c8a0611fe6c8c07cd6e
7
+ data.tar.gz: 5ec63e1912cce26a044f9fdfc902cc4436ef7660cc6c04e67dd07a25c8dd1e0549f94a201b00df8a6d0ed29ce1e95c8cd2fdf1e8a5dbf875a76bde199bc14c3c
@@ -1,5 +1,5 @@
1
1
  /*!
2
- Marksmith 0.4.1
2
+ Marksmith 0.4.4
3
3
  */
4
4
  var ListContinuationController = (function () {
5
5
  'use strict';
@@ -1,5 +1,5 @@
1
1
  /*!
2
- Marksmith 0.4.1
2
+ Marksmith 0.4.4
3
3
  */
4
4
  var ListContinuationController = (function (stimulus) {
5
5
  'use strict';
@@ -1,5 +1,5 @@
1
1
  /*!
2
- Marksmith 0.4.1
2
+ Marksmith 0.4.4
3
3
  */
4
4
  var MarksmithController = (function () {
5
5
  'use strict';
@@ -2417,15 +2417,15 @@ var MarksmithController = (function () {
2417
2417
  }
2418
2418
  el.addEventListener('paste', unsetSkipFormattedFlag);
2419
2419
  }
2420
- function uninstall$5(el) {
2420
+ function uninstall$6(el) {
2421
2421
  el.removeEventListener('keydown', setSkipFormattingFlag);
2422
2422
  el.removeEventListener('paste', unsetSkipFormattedFlag);
2423
2423
  }
2424
2424
 
2425
- function install$4(el) {
2425
+ function install$5(el) {
2426
2426
  el.addEventListener('paste', onPaste$4);
2427
2427
  }
2428
- function uninstall$4(el) {
2428
+ function uninstall$5(el) {
2429
2429
  el.removeEventListener('paste', onPaste$4);
2430
2430
  }
2431
2431
  function onPaste$4(event) {
@@ -2539,12 +2539,12 @@ var MarksmithController = (function () {
2539
2539
  return ((_a = link.textContent) === null || _a === undefined ? undefined : _a.slice(0, 1)) === '@' && link.getAttribute('data-hovercard-type') === 'team';
2540
2540
  }
2541
2541
 
2542
- function install$3(el) {
2542
+ function install$4(el) {
2543
2543
  el.addEventListener('dragover', onDragover$1);
2544
2544
  el.addEventListener('drop', onDrop$1);
2545
2545
  el.addEventListener('paste', onPaste$3);
2546
2546
  }
2547
- function uninstall$3(el) {
2547
+ function uninstall$4(el) {
2548
2548
  el.removeEventListener('dragover', onDragover$1);
2549
2549
  el.removeEventListener('drop', onDrop$1);
2550
2550
  el.removeEventListener('paste', onPaste$3);
@@ -2607,12 +2607,12 @@ var MarksmithController = (function () {
2607
2607
  }
2608
2608
 
2609
2609
  const pasteLinkAsPlainTextOverSelectedTextMap = new WeakMap();
2610
- function install$2(el, optionConfig) {
2610
+ function install$3(el, optionConfig) {
2611
2611
  var _a;
2612
2612
  pasteLinkAsPlainTextOverSelectedTextMap.set(el, ((_a = optionConfig === null || optionConfig === undefined ? undefined : optionConfig.defaultPlainTextPaste) === null || _a === undefined ? undefined : _a.urlLinks) === true);
2613
2613
  el.addEventListener('paste', onPaste$2);
2614
2614
  }
2615
- function uninstall$2(el) {
2615
+ function uninstall$3(el) {
2616
2616
  el.removeEventListener('paste', onPaste$2);
2617
2617
  }
2618
2618
  function onPaste$2(event) {
@@ -2676,12 +2676,12 @@ var MarksmithController = (function () {
2676
2676
  return url.endsWith('/') ? url.slice(0, url.length - 1) : url;
2677
2677
  }
2678
2678
 
2679
- function install$1(el) {
2679
+ function install$2(el) {
2680
2680
  el.addEventListener('dragover', onDragover);
2681
2681
  el.addEventListener('drop', onDrop);
2682
2682
  el.addEventListener('paste', onPaste$1);
2683
2683
  }
2684
- function uninstall$1(el) {
2684
+ function uninstall$2(el) {
2685
2685
  el.removeEventListener('dragover', onDragover);
2686
2686
  el.removeEventListener('drop', onDrop);
2687
2687
  el.removeEventListener('paste', onPaste$1);
@@ -2772,10 +2772,10 @@ var MarksmithController = (function () {
2772
2772
  return [start, formattedTable, end].join('').replace(/<meta.*?>/, '');
2773
2773
  }
2774
2774
 
2775
- function install(el) {
2775
+ function install$1(el) {
2776
2776
  el.addEventListener('paste', onPaste);
2777
2777
  }
2778
- function uninstall(el) {
2778
+ function uninstall$1(el) {
2779
2779
  el.removeEventListener('paste', onPaste);
2780
2780
  }
2781
2781
  function onPaste(event) {
@@ -2800,19 +2800,442 @@ var MarksmithController = (function () {
2800
2800
  }
2801
2801
 
2802
2802
  function subscribe(el, optionConfig) {
2803
- installAround(el, [install$1, install$3, install$2, install, install$4], optionConfig);
2803
+ installAround(el, [install$2, install$4, install$3, install$1, install$5], optionConfig);
2804
2804
  return {
2805
2805
  unsubscribe: () => {
2806
+ uninstall$6(el);
2807
+ uninstall$2(el);
2806
2808
  uninstall$5(el);
2807
- uninstall$1(el);
2808
2809
  uninstall$4(el);
2809
2810
  uninstall$3(el);
2810
- uninstall$2(el);
2811
- uninstall(el);
2811
+ uninstall$1(el);
2812
2812
  },
2813
2813
  };
2814
2814
  }
2815
2815
 
2816
+ class Leaf {
2817
+ constructor(trie) {
2818
+ this.children = [];
2819
+ this.parent = trie;
2820
+ }
2821
+ delete(value) {
2822
+ const index = this.children.indexOf(value);
2823
+ if (index === -1)
2824
+ return false;
2825
+ this.children = this.children.slice(0, index).concat(this.children.slice(index + 1));
2826
+ if (this.children.length === 0) {
2827
+ this.parent.delete(this);
2828
+ }
2829
+ return true;
2830
+ }
2831
+ add(value) {
2832
+ this.children.push(value);
2833
+ return this;
2834
+ }
2835
+ }
2836
+ class RadixTrie {
2837
+ constructor(trie) {
2838
+ this.parent = null;
2839
+ this.children = {};
2840
+ this.parent = trie || null;
2841
+ }
2842
+ get(edge) {
2843
+ return this.children[edge];
2844
+ }
2845
+ insert(edges) {
2846
+ let currentNode = this;
2847
+ for (let i = 0; i < edges.length; i += 1) {
2848
+ const edge = edges[i];
2849
+ let nextNode = currentNode.get(edge);
2850
+ if (i === edges.length - 1) {
2851
+ if (nextNode instanceof RadixTrie) {
2852
+ currentNode.delete(nextNode);
2853
+ nextNode = null;
2854
+ }
2855
+ if (!nextNode) {
2856
+ nextNode = new Leaf(currentNode);
2857
+ currentNode.children[edge] = nextNode;
2858
+ }
2859
+ return nextNode;
2860
+ }
2861
+ else {
2862
+ if (nextNode instanceof Leaf)
2863
+ nextNode = null;
2864
+ if (!nextNode) {
2865
+ nextNode = new RadixTrie(currentNode);
2866
+ currentNode.children[edge] = nextNode;
2867
+ }
2868
+ }
2869
+ currentNode = nextNode;
2870
+ }
2871
+ return currentNode;
2872
+ }
2873
+ delete(node) {
2874
+ for (const edge in this.children) {
2875
+ const currentNode = this.children[edge];
2876
+ if (currentNode === node) {
2877
+ const success = delete this.children[edge];
2878
+ if (Object.keys(this.children).length === 0 && this.parent) {
2879
+ this.parent.delete(this);
2880
+ }
2881
+ return success;
2882
+ }
2883
+ }
2884
+ return false;
2885
+ }
2886
+ }
2887
+
2888
+ const macosSymbolLayerKeys = {
2889
+ ['¡']: '1',
2890
+ ['™']: '2',
2891
+ ['£']: '3',
2892
+ ['¢']: '4',
2893
+ ['∞']: '5',
2894
+ ['§']: '6',
2895
+ ['¶']: '7',
2896
+ ['•']: '8',
2897
+ ['ª']: '9',
2898
+ ['º']: '0',
2899
+ ['–']: '-',
2900
+ ['≠']: '=',
2901
+ ['⁄']: '!',
2902
+ ['€']: '@',
2903
+ ['‹']: '#',
2904
+ ['›']: '$',
2905
+ ['fi']: '%',
2906
+ ['fl']: '^',
2907
+ ['‡']: '&',
2908
+ ['°']: '*',
2909
+ ['·']: '(',
2910
+ ['‚']: ')',
2911
+ ['—']: '_',
2912
+ ['±']: '+',
2913
+ ['œ']: 'q',
2914
+ ['∑']: 'w',
2915
+ ['®']: 'r',
2916
+ ['†']: 't',
2917
+ ['¥']: 'y',
2918
+ ['ø']: 'o',
2919
+ ['π']: 'p',
2920
+ ['“']: '[',
2921
+ ['‘']: ']',
2922
+ ['«']: '\\',
2923
+ ['Œ']: 'Q',
2924
+ ['„']: 'W',
2925
+ ['´']: 'E',
2926
+ ['‰']: 'R',
2927
+ ['ˇ']: 'T',
2928
+ ['Á']: 'Y',
2929
+ ['¨']: 'U',
2930
+ ['ˆ']: 'I',
2931
+ ['Ø']: 'O',
2932
+ ['∏']: 'P',
2933
+ ['”']: '{',
2934
+ ['’']: '}',
2935
+ ['»']: '|',
2936
+ ['å']: 'a',
2937
+ ['ß']: 's',
2938
+ ['∂']: 'd',
2939
+ ['ƒ']: 'f',
2940
+ ['©']: 'g',
2941
+ ['˙']: 'h',
2942
+ ['∆']: 'j',
2943
+ ['˚']: 'k',
2944
+ ['¬']: 'l',
2945
+ ['…']: ';',
2946
+ ['æ']: "'",
2947
+ ['Å']: 'A',
2948
+ ['Í']: 'S',
2949
+ ['Î']: 'D',
2950
+ ['Ï']: 'F',
2951
+ ['˝']: 'G',
2952
+ ['Ó']: 'H',
2953
+ ['Ô']: 'J',
2954
+ ['']: 'K',
2955
+ ['Ò']: 'L',
2956
+ ['Ú']: ':',
2957
+ ['Æ']: '"',
2958
+ ['Ω']: 'z',
2959
+ ['≈']: 'x',
2960
+ ['ç']: 'c',
2961
+ ['√']: 'v',
2962
+ ['∫']: 'b',
2963
+ ['µ']: 'm',
2964
+ ['≤']: ',',
2965
+ ['≥']: '.',
2966
+ ['÷']: '/',
2967
+ ['¸']: 'Z',
2968
+ ['˛']: 'X',
2969
+ ['Ç']: 'C',
2970
+ ['◊']: 'V',
2971
+ ['ı']: 'B',
2972
+ ['˜']: 'N',
2973
+ ['Â']: 'M',
2974
+ ['¯']: '<',
2975
+ ['˘']: '>',
2976
+ ['¿']: '?'
2977
+ };
2978
+
2979
+ const macosUppercaseLayerKeys = {
2980
+ ['`']: '~',
2981
+ ['1']: '!',
2982
+ ['2']: '@',
2983
+ ['3']: '#',
2984
+ ['4']: '$',
2985
+ ['5']: '%',
2986
+ ['6']: '^',
2987
+ ['7']: '&',
2988
+ ['8']: '*',
2989
+ ['9']: '(',
2990
+ ['0']: ')',
2991
+ ['-']: '_',
2992
+ ['=']: '+',
2993
+ ['[']: '{',
2994
+ [']']: '}',
2995
+ ['\\']: '|',
2996
+ [';']: ':',
2997
+ ["'"]: '"',
2998
+ [',']: '<',
2999
+ ['.']: '>',
3000
+ ['/']: '?',
3001
+ ['q']: 'Q',
3002
+ ['w']: 'W',
3003
+ ['e']: 'E',
3004
+ ['r']: 'R',
3005
+ ['t']: 'T',
3006
+ ['y']: 'Y',
3007
+ ['u']: 'U',
3008
+ ['i']: 'I',
3009
+ ['o']: 'O',
3010
+ ['p']: 'P',
3011
+ ['a']: 'A',
3012
+ ['s']: 'S',
3013
+ ['d']: 'D',
3014
+ ['f']: 'F',
3015
+ ['g']: 'G',
3016
+ ['h']: 'H',
3017
+ ['j']: 'J',
3018
+ ['k']: 'K',
3019
+ ['l']: 'L',
3020
+ ['z']: 'Z',
3021
+ ['x']: 'X',
3022
+ ['c']: 'C',
3023
+ ['v']: 'V',
3024
+ ['b']: 'B',
3025
+ ['n']: 'N',
3026
+ ['m']: 'M'
3027
+ };
3028
+
3029
+ const syntheticKeyNames = {
3030
+ ' ': 'Space',
3031
+ '+': 'Plus'
3032
+ };
3033
+ function eventToHotkeyString(event, platform = navigator.platform) {
3034
+ var _a, _b, _c;
3035
+ const { ctrlKey, altKey, metaKey, shiftKey, key } = event;
3036
+ const hotkeyString = [];
3037
+ const modifiers = [ctrlKey, altKey, metaKey, shiftKey];
3038
+ for (const [i, mod] of modifiers.entries()) {
3039
+ if (mod)
3040
+ hotkeyString.push(modifierKeyNames[i]);
3041
+ }
3042
+ if (!modifierKeyNames.includes(key)) {
3043
+ const altNormalizedKey = hotkeyString.includes('Alt') && matchApplePlatform.test(platform) ? (_a = macosSymbolLayerKeys[key]) !== null && _a !== undefined ? _a : key : key;
3044
+ const shiftNormalizedKey = hotkeyString.includes('Shift') && matchApplePlatform.test(platform)
3045
+ ? (_b = macosUppercaseLayerKeys[altNormalizedKey]) !== null && _b !== undefined ? _b : altNormalizedKey
3046
+ : altNormalizedKey;
3047
+ const syntheticKey = (_c = syntheticKeyNames[shiftNormalizedKey]) !== null && _c !== undefined ? _c : shiftNormalizedKey;
3048
+ hotkeyString.push(syntheticKey);
3049
+ }
3050
+ return hotkeyString.join('+');
3051
+ }
3052
+ const modifierKeyNames = ['Control', 'Alt', 'Meta', 'Shift'];
3053
+ function normalizeHotkey(hotkey, platform) {
3054
+ let result;
3055
+ result = localizeMod(hotkey);
3056
+ result = sortModifiers(result);
3057
+ return result;
3058
+ }
3059
+ const matchApplePlatform = /Mac|iPod|iPhone|iPad/i;
3060
+ function localizeMod(hotkey, platform) {
3061
+ var _a;
3062
+ const ssrSafeWindow = typeof window === 'undefined' ? undefined : window;
3063
+ const safePlatform = (_a = ssrSafeWindow === null || ssrSafeWindow === undefined ? undefined : ssrSafeWindow.navigator.platform) !== null && _a !== undefined ? _a : '';
3064
+ const localModifier = matchApplePlatform.test(safePlatform) ? 'Meta' : 'Control';
3065
+ return hotkey.replace('Mod', localModifier);
3066
+ }
3067
+ function sortModifiers(hotkey) {
3068
+ const key = hotkey.split('+').pop();
3069
+ const modifiers = [];
3070
+ for (const modifier of ['Control', 'Alt', 'Meta', 'Shift']) {
3071
+ if (hotkey.includes(modifier)) {
3072
+ modifiers.push(modifier);
3073
+ }
3074
+ }
3075
+ if (key)
3076
+ modifiers.push(key);
3077
+ return modifiers.join('+');
3078
+ }
3079
+
3080
+ const SEQUENCE_DELIMITER = ' ';
3081
+ class SequenceTracker {
3082
+ constructor({ onReset } = {}) {
3083
+ this._path = [];
3084
+ this.timer = null;
3085
+ this.onReset = onReset;
3086
+ }
3087
+ get path() {
3088
+ return this._path;
3089
+ }
3090
+ get sequence() {
3091
+ return this._path.join(SEQUENCE_DELIMITER);
3092
+ }
3093
+ registerKeypress(event) {
3094
+ this._path = [...this._path, eventToHotkeyString(event)];
3095
+ this.startTimer();
3096
+ }
3097
+ reset() {
3098
+ var _a;
3099
+ this.killTimer();
3100
+ this._path = [];
3101
+ (_a = this.onReset) === null || _a === undefined ? undefined : _a.call(this);
3102
+ }
3103
+ killTimer() {
3104
+ if (this.timer != null) {
3105
+ window.clearTimeout(this.timer);
3106
+ }
3107
+ this.timer = null;
3108
+ }
3109
+ startTimer() {
3110
+ this.killTimer();
3111
+ this.timer = window.setTimeout(() => this.reset(), SequenceTracker.CHORD_TIMEOUT);
3112
+ }
3113
+ }
3114
+ SequenceTracker.CHORD_TIMEOUT = 1500;
3115
+
3116
+ function isFormField(element) {
3117
+ if (!(element instanceof HTMLElement)) {
3118
+ return false;
3119
+ }
3120
+ const name = element.nodeName.toLowerCase();
3121
+ const type = (element.getAttribute('type') || '').toLowerCase();
3122
+ return (name === 'select' ||
3123
+ name === 'textarea' ||
3124
+ (name === 'input' &&
3125
+ type !== 'submit' &&
3126
+ type !== 'reset' &&
3127
+ type !== 'checkbox' &&
3128
+ type !== 'radio' &&
3129
+ type !== 'file') ||
3130
+ element.isContentEditable);
3131
+ }
3132
+ function fireDeterminedAction(el, path) {
3133
+ const delegateEvent = new CustomEvent('hotkey-fire', { cancelable: true, detail: { path } });
3134
+ const cancelled = !el.dispatchEvent(delegateEvent);
3135
+ if (cancelled)
3136
+ return;
3137
+ if (isFormField(el)) {
3138
+ el.focus();
3139
+ }
3140
+ else {
3141
+ el.click();
3142
+ }
3143
+ }
3144
+ function expandHotkeyToEdges(hotkey) {
3145
+ const output = [];
3146
+ let acc = [''];
3147
+ let commaIsSeparator = false;
3148
+ for (let i = 0; i < hotkey.length; i++) {
3149
+ if (commaIsSeparator && hotkey[i] === ',') {
3150
+ output.push(acc);
3151
+ acc = [''];
3152
+ commaIsSeparator = false;
3153
+ continue;
3154
+ }
3155
+ if (hotkey[i] === SEQUENCE_DELIMITER) {
3156
+ acc.push('');
3157
+ commaIsSeparator = false;
3158
+ continue;
3159
+ }
3160
+ else if (hotkey[i] === '+') {
3161
+ commaIsSeparator = false;
3162
+ }
3163
+ else {
3164
+ commaIsSeparator = true;
3165
+ }
3166
+ acc[acc.length - 1] += hotkey[i];
3167
+ }
3168
+ output.push(acc);
3169
+ return output.map(h => h.map(k => normalizeHotkey(k)).filter(k => k !== '')).filter(h => h.length > 0);
3170
+ }
3171
+
3172
+ const hotkeyRadixTrie = new RadixTrie();
3173
+ const elementsLeaves = new WeakMap();
3174
+ let currentTriePosition = hotkeyRadixTrie;
3175
+ const sequenceTracker = new SequenceTracker({
3176
+ onReset() {
3177
+ currentTriePosition = hotkeyRadixTrie;
3178
+ }
3179
+ });
3180
+ function keyDownHandler(event) {
3181
+ if (event.defaultPrevented)
3182
+ return;
3183
+ if (!(event.target instanceof Node))
3184
+ return;
3185
+ if (isFormField(event.target)) {
3186
+ const target = event.target;
3187
+ if (!target.id)
3188
+ return;
3189
+ if (!target.ownerDocument.querySelector(`[data-hotkey-scope="${target.id}"]`))
3190
+ return;
3191
+ }
3192
+ const newTriePosition = currentTriePosition.get(eventToHotkeyString(event));
3193
+ if (!newTriePosition) {
3194
+ sequenceTracker.reset();
3195
+ return;
3196
+ }
3197
+ sequenceTracker.registerKeypress(event);
3198
+ currentTriePosition = newTriePosition;
3199
+ if (newTriePosition instanceof Leaf) {
3200
+ const target = event.target;
3201
+ let shouldFire = false;
3202
+ let elementToFire;
3203
+ const formField = isFormField(target);
3204
+ for (let i = newTriePosition.children.length - 1; i >= 0; i -= 1) {
3205
+ elementToFire = newTriePosition.children[i];
3206
+ const scope = elementToFire.getAttribute('data-hotkey-scope');
3207
+ if ((!formField && !scope) || (formField && target.id === scope)) {
3208
+ shouldFire = true;
3209
+ break;
3210
+ }
3211
+ }
3212
+ if (elementToFire && shouldFire) {
3213
+ fireDeterminedAction(elementToFire, sequenceTracker.path);
3214
+ event.preventDefault();
3215
+ }
3216
+ sequenceTracker.reset();
3217
+ }
3218
+ }
3219
+ function install(element, hotkey) {
3220
+ if (Object.keys(hotkeyRadixTrie.children).length === 0) {
3221
+ document.addEventListener('keydown', keyDownHandler);
3222
+ }
3223
+ const hotkeys = expandHotkeyToEdges(element.getAttribute('data-hotkey') || '');
3224
+ const leaves = hotkeys.map(h => hotkeyRadixTrie.insert(h).add(element));
3225
+ elementsLeaves.set(element, leaves);
3226
+ }
3227
+ function uninstall(element) {
3228
+ const leaves = elementsLeaves.get(element);
3229
+ if (leaves && leaves.length) {
3230
+ for (const leaf of leaves) {
3231
+ leaf && leaf.delete(element);
3232
+ }
3233
+ }
3234
+ if (Object.keys(hotkeyRadixTrie.children).length === 0) {
3235
+ document.removeEventListener('keydown', keyDownHandler);
3236
+ }
3237
+ }
3238
+
2816
3239
  /* eslint-disable camelcase */
2817
3240
 
2818
3241
  // upload code from Jeremy Smith's blog post
@@ -2839,7 +3262,19 @@ var MarksmithController = (function () {
2839
3262
  }
2840
3263
 
2841
3264
  connect() {
2842
- subscribe(this.fieldContainerTarget, { defaultPlainTextPaste: { urlLinks: true } });
3265
+ subscribe(this.fieldElementTarget);
3266
+
3267
+ // Install all the hotkeys on the page
3268
+ for (const el of document.querySelectorAll('[data-hotkey]')) {
3269
+ install(el);
3270
+ }
3271
+ }
3272
+
3273
+ disconnect() {
3274
+ // Uninstall all the hotkeys on the page
3275
+ for (const el of document.querySelectorAll('[data-hotkey]')) {
3276
+ uninstall(el);
3277
+ }
2843
3278
  }
2844
3279
 
2845
3280
  switchToWrite(event) {
@@ -1,5 +1,5 @@
1
1
  /*!
2
- Marksmith 0.4.1
2
+ Marksmith 0.4.4
3
3
  */
4
4
  var MarksmithController = (function (stimulus) {
5
5
  'use strict';
@@ -1927,15 +1927,15 @@ var MarksmithController = (function (stimulus) {
1927
1927
  }
1928
1928
  el.addEventListener('paste', unsetSkipFormattedFlag);
1929
1929
  }
1930
- function uninstall$5(el) {
1930
+ function uninstall$6(el) {
1931
1931
  el.removeEventListener('keydown', setSkipFormattingFlag);
1932
1932
  el.removeEventListener('paste', unsetSkipFormattedFlag);
1933
1933
  }
1934
1934
 
1935
- function install$4(el) {
1935
+ function install$5(el) {
1936
1936
  el.addEventListener('paste', onPaste$4);
1937
1937
  }
1938
- function uninstall$4(el) {
1938
+ function uninstall$5(el) {
1939
1939
  el.removeEventListener('paste', onPaste$4);
1940
1940
  }
1941
1941
  function onPaste$4(event) {
@@ -2049,12 +2049,12 @@ var MarksmithController = (function (stimulus) {
2049
2049
  return ((_a = link.textContent) === null || _a === undefined ? undefined : _a.slice(0, 1)) === '@' && link.getAttribute('data-hovercard-type') === 'team';
2050
2050
  }
2051
2051
 
2052
- function install$3(el) {
2052
+ function install$4(el) {
2053
2053
  el.addEventListener('dragover', onDragover$1);
2054
2054
  el.addEventListener('drop', onDrop$1);
2055
2055
  el.addEventListener('paste', onPaste$3);
2056
2056
  }
2057
- function uninstall$3(el) {
2057
+ function uninstall$4(el) {
2058
2058
  el.removeEventListener('dragover', onDragover$1);
2059
2059
  el.removeEventListener('drop', onDrop$1);
2060
2060
  el.removeEventListener('paste', onPaste$3);
@@ -2117,12 +2117,12 @@ var MarksmithController = (function (stimulus) {
2117
2117
  }
2118
2118
 
2119
2119
  const pasteLinkAsPlainTextOverSelectedTextMap = new WeakMap();
2120
- function install$2(el, optionConfig) {
2120
+ function install$3(el, optionConfig) {
2121
2121
  var _a;
2122
2122
  pasteLinkAsPlainTextOverSelectedTextMap.set(el, ((_a = optionConfig === null || optionConfig === undefined ? undefined : optionConfig.defaultPlainTextPaste) === null || _a === undefined ? undefined : _a.urlLinks) === true);
2123
2123
  el.addEventListener('paste', onPaste$2);
2124
2124
  }
2125
- function uninstall$2(el) {
2125
+ function uninstall$3(el) {
2126
2126
  el.removeEventListener('paste', onPaste$2);
2127
2127
  }
2128
2128
  function onPaste$2(event) {
@@ -2186,12 +2186,12 @@ var MarksmithController = (function (stimulus) {
2186
2186
  return url.endsWith('/') ? url.slice(0, url.length - 1) : url;
2187
2187
  }
2188
2188
 
2189
- function install$1(el) {
2189
+ function install$2(el) {
2190
2190
  el.addEventListener('dragover', onDragover);
2191
2191
  el.addEventListener('drop', onDrop);
2192
2192
  el.addEventListener('paste', onPaste$1);
2193
2193
  }
2194
- function uninstall$1(el) {
2194
+ function uninstall$2(el) {
2195
2195
  el.removeEventListener('dragover', onDragover);
2196
2196
  el.removeEventListener('drop', onDrop);
2197
2197
  el.removeEventListener('paste', onPaste$1);
@@ -2282,10 +2282,10 @@ var MarksmithController = (function (stimulus) {
2282
2282
  return [start, formattedTable, end].join('').replace(/<meta.*?>/, '');
2283
2283
  }
2284
2284
 
2285
- function install(el) {
2285
+ function install$1(el) {
2286
2286
  el.addEventListener('paste', onPaste);
2287
2287
  }
2288
- function uninstall(el) {
2288
+ function uninstall$1(el) {
2289
2289
  el.removeEventListener('paste', onPaste);
2290
2290
  }
2291
2291
  function onPaste(event) {
@@ -2310,19 +2310,442 @@ var MarksmithController = (function (stimulus) {
2310
2310
  }
2311
2311
 
2312
2312
  function subscribe(el, optionConfig) {
2313
- installAround(el, [install$1, install$3, install$2, install, install$4], optionConfig);
2313
+ installAround(el, [install$2, install$4, install$3, install$1, install$5], optionConfig);
2314
2314
  return {
2315
2315
  unsubscribe: () => {
2316
+ uninstall$6(el);
2317
+ uninstall$2(el);
2316
2318
  uninstall$5(el);
2317
- uninstall$1(el);
2318
2319
  uninstall$4(el);
2319
2320
  uninstall$3(el);
2320
- uninstall$2(el);
2321
- uninstall(el);
2321
+ uninstall$1(el);
2322
2322
  },
2323
2323
  };
2324
2324
  }
2325
2325
 
2326
+ class Leaf {
2327
+ constructor(trie) {
2328
+ this.children = [];
2329
+ this.parent = trie;
2330
+ }
2331
+ delete(value) {
2332
+ const index = this.children.indexOf(value);
2333
+ if (index === -1)
2334
+ return false;
2335
+ this.children = this.children.slice(0, index).concat(this.children.slice(index + 1));
2336
+ if (this.children.length === 0) {
2337
+ this.parent.delete(this);
2338
+ }
2339
+ return true;
2340
+ }
2341
+ add(value) {
2342
+ this.children.push(value);
2343
+ return this;
2344
+ }
2345
+ }
2346
+ class RadixTrie {
2347
+ constructor(trie) {
2348
+ this.parent = null;
2349
+ this.children = {};
2350
+ this.parent = trie || null;
2351
+ }
2352
+ get(edge) {
2353
+ return this.children[edge];
2354
+ }
2355
+ insert(edges) {
2356
+ let currentNode = this;
2357
+ for (let i = 0; i < edges.length; i += 1) {
2358
+ const edge = edges[i];
2359
+ let nextNode = currentNode.get(edge);
2360
+ if (i === edges.length - 1) {
2361
+ if (nextNode instanceof RadixTrie) {
2362
+ currentNode.delete(nextNode);
2363
+ nextNode = null;
2364
+ }
2365
+ if (!nextNode) {
2366
+ nextNode = new Leaf(currentNode);
2367
+ currentNode.children[edge] = nextNode;
2368
+ }
2369
+ return nextNode;
2370
+ }
2371
+ else {
2372
+ if (nextNode instanceof Leaf)
2373
+ nextNode = null;
2374
+ if (!nextNode) {
2375
+ nextNode = new RadixTrie(currentNode);
2376
+ currentNode.children[edge] = nextNode;
2377
+ }
2378
+ }
2379
+ currentNode = nextNode;
2380
+ }
2381
+ return currentNode;
2382
+ }
2383
+ delete(node) {
2384
+ for (const edge in this.children) {
2385
+ const currentNode = this.children[edge];
2386
+ if (currentNode === node) {
2387
+ const success = delete this.children[edge];
2388
+ if (Object.keys(this.children).length === 0 && this.parent) {
2389
+ this.parent.delete(this);
2390
+ }
2391
+ return success;
2392
+ }
2393
+ }
2394
+ return false;
2395
+ }
2396
+ }
2397
+
2398
+ const macosSymbolLayerKeys = {
2399
+ ['¡']: '1',
2400
+ ['™']: '2',
2401
+ ['£']: '3',
2402
+ ['¢']: '4',
2403
+ ['∞']: '5',
2404
+ ['§']: '6',
2405
+ ['¶']: '7',
2406
+ ['•']: '8',
2407
+ ['ª']: '9',
2408
+ ['º']: '0',
2409
+ ['–']: '-',
2410
+ ['≠']: '=',
2411
+ ['⁄']: '!',
2412
+ ['€']: '@',
2413
+ ['‹']: '#',
2414
+ ['›']: '$',
2415
+ ['fi']: '%',
2416
+ ['fl']: '^',
2417
+ ['‡']: '&',
2418
+ ['°']: '*',
2419
+ ['·']: '(',
2420
+ ['‚']: ')',
2421
+ ['—']: '_',
2422
+ ['±']: '+',
2423
+ ['œ']: 'q',
2424
+ ['∑']: 'w',
2425
+ ['®']: 'r',
2426
+ ['†']: 't',
2427
+ ['¥']: 'y',
2428
+ ['ø']: 'o',
2429
+ ['π']: 'p',
2430
+ ['“']: '[',
2431
+ ['‘']: ']',
2432
+ ['«']: '\\',
2433
+ ['Œ']: 'Q',
2434
+ ['„']: 'W',
2435
+ ['´']: 'E',
2436
+ ['‰']: 'R',
2437
+ ['ˇ']: 'T',
2438
+ ['Á']: 'Y',
2439
+ ['¨']: 'U',
2440
+ ['ˆ']: 'I',
2441
+ ['Ø']: 'O',
2442
+ ['∏']: 'P',
2443
+ ['”']: '{',
2444
+ ['’']: '}',
2445
+ ['»']: '|',
2446
+ ['å']: 'a',
2447
+ ['ß']: 's',
2448
+ ['∂']: 'd',
2449
+ ['ƒ']: 'f',
2450
+ ['©']: 'g',
2451
+ ['˙']: 'h',
2452
+ ['∆']: 'j',
2453
+ ['˚']: 'k',
2454
+ ['¬']: 'l',
2455
+ ['…']: ';',
2456
+ ['æ']: "'",
2457
+ ['Å']: 'A',
2458
+ ['Í']: 'S',
2459
+ ['Î']: 'D',
2460
+ ['Ï']: 'F',
2461
+ ['˝']: 'G',
2462
+ ['Ó']: 'H',
2463
+ ['Ô']: 'J',
2464
+ ['']: 'K',
2465
+ ['Ò']: 'L',
2466
+ ['Ú']: ':',
2467
+ ['Æ']: '"',
2468
+ ['Ω']: 'z',
2469
+ ['≈']: 'x',
2470
+ ['ç']: 'c',
2471
+ ['√']: 'v',
2472
+ ['∫']: 'b',
2473
+ ['µ']: 'm',
2474
+ ['≤']: ',',
2475
+ ['≥']: '.',
2476
+ ['÷']: '/',
2477
+ ['¸']: 'Z',
2478
+ ['˛']: 'X',
2479
+ ['Ç']: 'C',
2480
+ ['◊']: 'V',
2481
+ ['ı']: 'B',
2482
+ ['˜']: 'N',
2483
+ ['Â']: 'M',
2484
+ ['¯']: '<',
2485
+ ['˘']: '>',
2486
+ ['¿']: '?'
2487
+ };
2488
+
2489
+ const macosUppercaseLayerKeys = {
2490
+ ['`']: '~',
2491
+ ['1']: '!',
2492
+ ['2']: '@',
2493
+ ['3']: '#',
2494
+ ['4']: '$',
2495
+ ['5']: '%',
2496
+ ['6']: '^',
2497
+ ['7']: '&',
2498
+ ['8']: '*',
2499
+ ['9']: '(',
2500
+ ['0']: ')',
2501
+ ['-']: '_',
2502
+ ['=']: '+',
2503
+ ['[']: '{',
2504
+ [']']: '}',
2505
+ ['\\']: '|',
2506
+ [';']: ':',
2507
+ ["'"]: '"',
2508
+ [',']: '<',
2509
+ ['.']: '>',
2510
+ ['/']: '?',
2511
+ ['q']: 'Q',
2512
+ ['w']: 'W',
2513
+ ['e']: 'E',
2514
+ ['r']: 'R',
2515
+ ['t']: 'T',
2516
+ ['y']: 'Y',
2517
+ ['u']: 'U',
2518
+ ['i']: 'I',
2519
+ ['o']: 'O',
2520
+ ['p']: 'P',
2521
+ ['a']: 'A',
2522
+ ['s']: 'S',
2523
+ ['d']: 'D',
2524
+ ['f']: 'F',
2525
+ ['g']: 'G',
2526
+ ['h']: 'H',
2527
+ ['j']: 'J',
2528
+ ['k']: 'K',
2529
+ ['l']: 'L',
2530
+ ['z']: 'Z',
2531
+ ['x']: 'X',
2532
+ ['c']: 'C',
2533
+ ['v']: 'V',
2534
+ ['b']: 'B',
2535
+ ['n']: 'N',
2536
+ ['m']: 'M'
2537
+ };
2538
+
2539
+ const syntheticKeyNames = {
2540
+ ' ': 'Space',
2541
+ '+': 'Plus'
2542
+ };
2543
+ function eventToHotkeyString(event, platform = navigator.platform) {
2544
+ var _a, _b, _c;
2545
+ const { ctrlKey, altKey, metaKey, shiftKey, key } = event;
2546
+ const hotkeyString = [];
2547
+ const modifiers = [ctrlKey, altKey, metaKey, shiftKey];
2548
+ for (const [i, mod] of modifiers.entries()) {
2549
+ if (mod)
2550
+ hotkeyString.push(modifierKeyNames[i]);
2551
+ }
2552
+ if (!modifierKeyNames.includes(key)) {
2553
+ const altNormalizedKey = hotkeyString.includes('Alt') && matchApplePlatform.test(platform) ? (_a = macosSymbolLayerKeys[key]) !== null && _a !== undefined ? _a : key : key;
2554
+ const shiftNormalizedKey = hotkeyString.includes('Shift') && matchApplePlatform.test(platform)
2555
+ ? (_b = macosUppercaseLayerKeys[altNormalizedKey]) !== null && _b !== undefined ? _b : altNormalizedKey
2556
+ : altNormalizedKey;
2557
+ const syntheticKey = (_c = syntheticKeyNames[shiftNormalizedKey]) !== null && _c !== undefined ? _c : shiftNormalizedKey;
2558
+ hotkeyString.push(syntheticKey);
2559
+ }
2560
+ return hotkeyString.join('+');
2561
+ }
2562
+ const modifierKeyNames = ['Control', 'Alt', 'Meta', 'Shift'];
2563
+ function normalizeHotkey(hotkey, platform) {
2564
+ let result;
2565
+ result = localizeMod(hotkey);
2566
+ result = sortModifiers(result);
2567
+ return result;
2568
+ }
2569
+ const matchApplePlatform = /Mac|iPod|iPhone|iPad/i;
2570
+ function localizeMod(hotkey, platform) {
2571
+ var _a;
2572
+ const ssrSafeWindow = typeof window === 'undefined' ? undefined : window;
2573
+ const safePlatform = (_a = ssrSafeWindow === null || ssrSafeWindow === undefined ? undefined : ssrSafeWindow.navigator.platform) !== null && _a !== undefined ? _a : '';
2574
+ const localModifier = matchApplePlatform.test(safePlatform) ? 'Meta' : 'Control';
2575
+ return hotkey.replace('Mod', localModifier);
2576
+ }
2577
+ function sortModifiers(hotkey) {
2578
+ const key = hotkey.split('+').pop();
2579
+ const modifiers = [];
2580
+ for (const modifier of ['Control', 'Alt', 'Meta', 'Shift']) {
2581
+ if (hotkey.includes(modifier)) {
2582
+ modifiers.push(modifier);
2583
+ }
2584
+ }
2585
+ if (key)
2586
+ modifiers.push(key);
2587
+ return modifiers.join('+');
2588
+ }
2589
+
2590
+ const SEQUENCE_DELIMITER = ' ';
2591
+ class SequenceTracker {
2592
+ constructor({ onReset } = {}) {
2593
+ this._path = [];
2594
+ this.timer = null;
2595
+ this.onReset = onReset;
2596
+ }
2597
+ get path() {
2598
+ return this._path;
2599
+ }
2600
+ get sequence() {
2601
+ return this._path.join(SEQUENCE_DELIMITER);
2602
+ }
2603
+ registerKeypress(event) {
2604
+ this._path = [...this._path, eventToHotkeyString(event)];
2605
+ this.startTimer();
2606
+ }
2607
+ reset() {
2608
+ var _a;
2609
+ this.killTimer();
2610
+ this._path = [];
2611
+ (_a = this.onReset) === null || _a === undefined ? undefined : _a.call(this);
2612
+ }
2613
+ killTimer() {
2614
+ if (this.timer != null) {
2615
+ window.clearTimeout(this.timer);
2616
+ }
2617
+ this.timer = null;
2618
+ }
2619
+ startTimer() {
2620
+ this.killTimer();
2621
+ this.timer = window.setTimeout(() => this.reset(), SequenceTracker.CHORD_TIMEOUT);
2622
+ }
2623
+ }
2624
+ SequenceTracker.CHORD_TIMEOUT = 1500;
2625
+
2626
+ function isFormField(element) {
2627
+ if (!(element instanceof HTMLElement)) {
2628
+ return false;
2629
+ }
2630
+ const name = element.nodeName.toLowerCase();
2631
+ const type = (element.getAttribute('type') || '').toLowerCase();
2632
+ return (name === 'select' ||
2633
+ name === 'textarea' ||
2634
+ (name === 'input' &&
2635
+ type !== 'submit' &&
2636
+ type !== 'reset' &&
2637
+ type !== 'checkbox' &&
2638
+ type !== 'radio' &&
2639
+ type !== 'file') ||
2640
+ element.isContentEditable);
2641
+ }
2642
+ function fireDeterminedAction(el, path) {
2643
+ const delegateEvent = new CustomEvent('hotkey-fire', { cancelable: true, detail: { path } });
2644
+ const cancelled = !el.dispatchEvent(delegateEvent);
2645
+ if (cancelled)
2646
+ return;
2647
+ if (isFormField(el)) {
2648
+ el.focus();
2649
+ }
2650
+ else {
2651
+ el.click();
2652
+ }
2653
+ }
2654
+ function expandHotkeyToEdges(hotkey) {
2655
+ const output = [];
2656
+ let acc = [''];
2657
+ let commaIsSeparator = false;
2658
+ for (let i = 0; i < hotkey.length; i++) {
2659
+ if (commaIsSeparator && hotkey[i] === ',') {
2660
+ output.push(acc);
2661
+ acc = [''];
2662
+ commaIsSeparator = false;
2663
+ continue;
2664
+ }
2665
+ if (hotkey[i] === SEQUENCE_DELIMITER) {
2666
+ acc.push('');
2667
+ commaIsSeparator = false;
2668
+ continue;
2669
+ }
2670
+ else if (hotkey[i] === '+') {
2671
+ commaIsSeparator = false;
2672
+ }
2673
+ else {
2674
+ commaIsSeparator = true;
2675
+ }
2676
+ acc[acc.length - 1] += hotkey[i];
2677
+ }
2678
+ output.push(acc);
2679
+ return output.map(h => h.map(k => normalizeHotkey(k)).filter(k => k !== '')).filter(h => h.length > 0);
2680
+ }
2681
+
2682
+ const hotkeyRadixTrie = new RadixTrie();
2683
+ const elementsLeaves = new WeakMap();
2684
+ let currentTriePosition = hotkeyRadixTrie;
2685
+ const sequenceTracker = new SequenceTracker({
2686
+ onReset() {
2687
+ currentTriePosition = hotkeyRadixTrie;
2688
+ }
2689
+ });
2690
+ function keyDownHandler(event) {
2691
+ if (event.defaultPrevented)
2692
+ return;
2693
+ if (!(event.target instanceof Node))
2694
+ return;
2695
+ if (isFormField(event.target)) {
2696
+ const target = event.target;
2697
+ if (!target.id)
2698
+ return;
2699
+ if (!target.ownerDocument.querySelector(`[data-hotkey-scope="${target.id}"]`))
2700
+ return;
2701
+ }
2702
+ const newTriePosition = currentTriePosition.get(eventToHotkeyString(event));
2703
+ if (!newTriePosition) {
2704
+ sequenceTracker.reset();
2705
+ return;
2706
+ }
2707
+ sequenceTracker.registerKeypress(event);
2708
+ currentTriePosition = newTriePosition;
2709
+ if (newTriePosition instanceof Leaf) {
2710
+ const target = event.target;
2711
+ let shouldFire = false;
2712
+ let elementToFire;
2713
+ const formField = isFormField(target);
2714
+ for (let i = newTriePosition.children.length - 1; i >= 0; i -= 1) {
2715
+ elementToFire = newTriePosition.children[i];
2716
+ const scope = elementToFire.getAttribute('data-hotkey-scope');
2717
+ if ((!formField && !scope) || (formField && target.id === scope)) {
2718
+ shouldFire = true;
2719
+ break;
2720
+ }
2721
+ }
2722
+ if (elementToFire && shouldFire) {
2723
+ fireDeterminedAction(elementToFire, sequenceTracker.path);
2724
+ event.preventDefault();
2725
+ }
2726
+ sequenceTracker.reset();
2727
+ }
2728
+ }
2729
+ function install(element, hotkey) {
2730
+ if (Object.keys(hotkeyRadixTrie.children).length === 0) {
2731
+ document.addEventListener('keydown', keyDownHandler);
2732
+ }
2733
+ const hotkeys = expandHotkeyToEdges(element.getAttribute('data-hotkey') || '');
2734
+ const leaves = hotkeys.map(h => hotkeyRadixTrie.insert(h).add(element));
2735
+ elementsLeaves.set(element, leaves);
2736
+ }
2737
+ function uninstall(element) {
2738
+ const leaves = elementsLeaves.get(element);
2739
+ if (leaves && leaves.length) {
2740
+ for (const leaf of leaves) {
2741
+ leaf && leaf.delete(element);
2742
+ }
2743
+ }
2744
+ if (Object.keys(hotkeyRadixTrie.children).length === 0) {
2745
+ document.removeEventListener('keydown', keyDownHandler);
2746
+ }
2747
+ }
2748
+
2326
2749
  /* eslint-disable camelcase */
2327
2750
 
2328
2751
  // upload code from Jeremy Smith's blog post
@@ -2349,7 +2772,19 @@ var MarksmithController = (function (stimulus) {
2349
2772
  }
2350
2773
 
2351
2774
  connect() {
2352
- subscribe(this.fieldContainerTarget, { defaultPlainTextPaste: { urlLinks: true } });
2775
+ subscribe(this.fieldElementTarget);
2776
+
2777
+ // Install all the hotkeys on the page
2778
+ for (const el of document.querySelectorAll('[data-hotkey]')) {
2779
+ install(el);
2780
+ }
2781
+ }
2782
+
2783
+ disconnect() {
2784
+ // Uninstall all the hotkeys on the page
2785
+ for (const el of document.querySelectorAll('[data-hotkey]')) {
2786
+ uninstall(el);
2787
+ }
2353
2788
  }
2354
2789
 
2355
2790
  switchToWrite(event) {
@@ -4,6 +4,7 @@ import { Controller } from '@hotwired/stimulus'
4
4
  import { DirectUpload } from '@rails/activestorage'
5
5
  import { post } from '@rails/request.js'
6
6
  import { subscribe } from '@github/paste-markdown'
7
+ import {install, uninstall} from '@github/hotkey'
7
8
 
8
9
  // upload code from Jeremy Smith's blog post
9
10
  // https://hybrd.co/posts/github-issue-style-file-uploader-using-stimulus-and-active-storage
@@ -29,7 +30,19 @@ export default class extends Controller {
29
30
  }
30
31
 
31
32
  connect() {
32
- subscribe(this.fieldContainerTarget, { defaultPlainTextPaste: { urlLinks: true } })
33
+ subscribe(this.fieldElementTarget)
34
+
35
+ // Install all the hotkeys on the page
36
+ for (const el of document.querySelectorAll('[data-hotkey]')) {
37
+ install(el)
38
+ }
39
+ }
40
+
41
+ disconnect() {
42
+ // Uninstall all the hotkeys on the page
43
+ for (const el of document.querySelectorAll('[data-hotkey]')) {
44
+ uninstall(el)
45
+ }
33
46
  }
34
47
 
35
48
  switchToWrite(event) {
@@ -26,8 +26,14 @@ module Marksmith
26
26
  )
27
27
  end
28
28
 
29
- def marksmith_toolbar_button(name, **kwargs)
30
- content_tag "md-#{name}", marksmith_toolbar_svg(name), title: t("marksmith.#{name.to_s.gsub("-", "_")}").humanize, class: marksmith_button_classes
29
+ def marksmith_toolbar_button(name, hotkey_scope: nil, hotkey: nil, **kwargs)
30
+ content_tag "md-#{name}", marksmith_toolbar_svg(name),
31
+ title: t("marksmith.#{name.to_s.gsub("-", "_")}").humanize,
32
+ class: marksmith_button_classes,
33
+ data: {
34
+ hotkey_scope:,
35
+ hotkey:
36
+ }
31
37
  end
32
38
 
33
39
  def marksmith_tab_classes
@@ -101,4 +101,12 @@ class Marksmith::Editor
101
101
  def value
102
102
  form&.object&.send(name) || @value || nil
103
103
  end
104
+
105
+ def textarea_id
106
+ "#{id}-textarea"
107
+ end
108
+
109
+ def preview_pane_id
110
+ "#{id}-preview-pane"
111
+ end
104
112
  end
@@ -1,10 +1,10 @@
1
- <%= tag.markdown_toolbar for: name,
1
+ <%= tag.markdown_toolbar for: textarea_id,
2
2
  class: class_names("ms:flex ms:flex-wrap ms:px-2 ms:py-1", "ms:pointer-events-none": disabled),
3
3
  data: { marksmith_target: "toolbar" } do
4
4
  %>
5
- <%= marksmith_toolbar_button "bold" %>
6
- <%= marksmith_toolbar_button "header" %>
7
- <%= marksmith_toolbar_button "italic" %>
5
+ <%= marksmith_toolbar_button "bold", hotkey: "Meta+b", hotkey_scope: textarea_id %>
6
+ <%= marksmith_toolbar_button "header", hotkey: "Meta+h", hotkey_scope: textarea_id %>
7
+ <%= marksmith_toolbar_button "italic", hotkey: "Meta+i", hotkey_scope: textarea_id %>
8
8
  <%= marksmith_toolbar_button "quote" %>
9
9
  <%= marksmith_toolbar_button "code" %>
10
10
  <%= marksmith_toolbar_button "link" %>
@@ -15,9 +15,9 @@
15
15
  marksmith_extra_preview_params_value: editor.extra_preview_params.as_json,
16
16
  **editor.controller_data_attributes,
17
17
  } do %>
18
- <%= render partial: "marksmith/shared/toolbar", locals: { name: editor.name, disabled: editor.disabled } %>
18
+ <%= render partial: "marksmith/shared/toolbar", locals: { textarea_id: editor.textarea_id, disabled: editor.disabled} %>
19
19
  <div class="ms:border-t ms:w-full ms:border-neutral-500 ms:flex ms:flex-1">
20
20
  <%= render partial: "marksmith/shared/editor_pane", locals: { editor: } %>
21
- <%= render partial: "marksmith/shared/preview_pane", locals: { name: editor.name } %>
21
+ <%= render partial: "marksmith/shared/preview_pane", locals: { editor: } %>
22
22
  </div>
23
23
  <% end %>
@@ -1,6 +1,6 @@
1
1
  <%= content_tag :div, class: "ms:flex ms:flex-1 ms:flex-col ms:size-full", data: { marksmith_target: "fieldContainer" } do %>
2
2
  <%= text_area_tag editor.field_name, editor.value,
3
- id: editor.name,
3
+ id: editor.textarea_id,
4
4
  class: class_names(
5
5
  "ms:flex ms:flex-1 ms:border-none ms:resize-none ms:focus:outline-none ms:font-mono ms:focus:ring-0 ms:leading-normal ms:p-2 ms:text-sm ms:field-sizing-content ms:min-h-60",
6
6
  "ms:dark:bg-neutral-800 ms:dark:text-neutral-200",
@@ -1,6 +1,6 @@
1
1
  <%= content_tag :div,
2
2
  class: "ms:hidden ms:markdown-preview ms:size-full ms:flex-1 ms:flex ms:size-full ms:p-2 ms:overflow-auto ms:bg-white ms:dark:bg-neutral-800 ms:rounded-b-md",
3
- id: "markdown-preview-#{name}",
3
+ id: editor.preview_pane_id,
4
4
  data: {
5
5
  marksmith_target: "previewPane",
6
6
  } do %>
@@ -4,5 +4,5 @@
4
4
  ) do %>
5
5
  <%= render partial: "marksmith/shared/tabs" %>
6
6
 
7
- <%= render partial: "marksmith/shared/action_bar", locals: { name:, disabled: } %>
7
+ <%= render partial: "marksmith/shared/action_bar", locals: { textarea_id:, disabled: } %>
8
8
  <% end %>
@@ -1,3 +1,3 @@
1
1
  module Marksmith
2
- VERSION = "0.4.1"
2
+ VERSION = "0.4.4"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: marksmith
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: 0.4.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Adrian Marin