ascii-tabs 0.10.3 → 0.11.2
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 +30 -4
- package/ascii-tabs.js +64 -16
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -24,6 +24,14 @@ import { parse, format } from 'ascii-tabs';
|
|
|
24
24
|
|
|
25
25
|
No bundler? Load it from a CDN instead, see [Usage](#usage). Full documentation and a live, editable demo: **https://libasoles.github.io/ascii-tabs/**
|
|
26
26
|
|
|
27
|
+
## Used in
|
|
28
|
+
|
|
29
|
+
`ascii-tabs` runs in production on [acordesdeguitarra.com.ar](https://acordesdeguitarra.com.ar/en):
|
|
30
|
+
|
|
31
|
+
- [Tabs for arpeggios](https://acordesdeguitarra.com.ar/en/articles/tabs-for-arpeggios): an article with arpeggio tabs.
|
|
32
|
+
- [Create guitar tabs](https://acordesdeguitarra.com.ar/en/create-guitar-tabs): write your own tabs online.
|
|
33
|
+
- [Carcassi preludes](https://acordesdeguitarra.com.ar/en/articles/carcassi-preludes): integrated with sheet music (partituras).
|
|
34
|
+
|
|
27
35
|
## See it in action
|
|
28
36
|
|
|
29
37
|
### Edit and organize tabs
|
|
@@ -54,12 +62,12 @@ Design notes: [spec](docs/spec-v0.1.md), [glossary](GLOSSARY.md), [ADRs](docs/ad
|
|
|
54
62
|
## Usage
|
|
55
63
|
|
|
56
64
|
```html
|
|
57
|
-
<script type="module" src="https://cdn.jsdelivr.net/gh/libasoles/ascii-tabs@0.
|
|
65
|
+
<script type="module" src="https://cdn.jsdelivr.net/gh/libasoles/ascii-tabs@0.11.2/ascii-tabs.js"></script>
|
|
58
66
|
|
|
59
67
|
<ascii-tabs></ascii-tabs>
|
|
60
68
|
```
|
|
61
69
|
|
|
62
|
-
A bare `<ascii-tabs>` is editable: a hint, one Sheet per Tab with copy and delete buttons, and an add button. Click a string and type the fret number (0–24); move with the arrow keys, Enter and Tab. Press Cmd+Right Arrow or Alt+Right Arrow to copy all six strings of the current column into the next column, replacing its contents, and move the cursor there. Press Cmd+0 or Alt+0 to fill only the empty cells of the current column with zeros, keeping existing frets and the cursor in place.
|
|
70
|
+
A bare `<ascii-tabs>` is editable: a hint, one Sheet per Tab with copy and delete buttons, and an add button. Click a string and type the fret number (0–24, or up to `max-fret`); move with the arrow keys, Enter and Tab. Press Cmd+Right Arrow or Alt+Right Arrow to copy all six strings of the current column into the next column, replacing its contents, and move the cursor there. Press Shift+Up Arrow or Shift+Down Arrow to re-finger the current note (or the whole selection) on the next String up or down that keeps its pitch, for example a 7 on String 5 becomes a 2 on String 4; nothing happens when no such position exists. Press Cmd+0 or Alt+0 to fill only the empty cells of the current column with zeros, keeping existing frets and the cursor in place.
|
|
63
71
|
|
|
64
72
|
Drag from an empty cell or a blank area of a Sheet to select notes inside a rectangle. Drag any selected note to move the group, keeping its relative columns and strings; occupied destination cells are replaced. Cmd+Right Arrow or Alt+Right Arrow duplicates the selected group immediately after its last column and selects the duplicate. Cmd/Ctrl+C or the Sheet's copy button copies the selection as ASCII; Cmd/Ctrl+V pastes ASCII at the current column, preserving string positions. Delete or Backspace removes the selected notes, and Escape clears the selection.
|
|
65
73
|
|
|
@@ -401,7 +409,7 @@ E|-----3-|</pre>
|
|
|
401
409
|
The copy button copies the same plain ASCII. `parse` and `format` are exported as pure functions:
|
|
402
410
|
|
|
403
411
|
```js
|
|
404
|
-
import { parse, format } from 'https://cdn.jsdelivr.net/gh/libasoles/ascii-tabs@0.
|
|
412
|
+
import { parse, format } from 'https://cdn.jsdelivr.net/gh/libasoles/ascii-tabs@0.11.2/ascii-tabs.js';
|
|
405
413
|
|
|
406
414
|
const tab = parse('1 -3--5-\n2 ------\n3 ------\n4 ------\n5 ------\n6 ------'); // one Tab: Column[]
|
|
407
415
|
format(tab, { spacing: 2 }); // "1 -3--5-\n2 ------\n..."
|
|
@@ -565,14 +573,32 @@ Changing or removing the attribute updates the view immediately.
|
|
|
565
573
|
|
|
566
574
|
## String labels
|
|
567
575
|
|
|
576
|
+
`max-fret` (property `maxFret`) sets the highest Fret the editor accepts when typing or re-fingering with Shift+Up/Down Arrow. It defaults to `24`; use `max-fret="19"` for a classical guitar. Values outside 1–24 fall back to 24. It does not rewrite notes already in `value`.
|
|
577
|
+
|
|
578
|
+
```html
|
|
579
|
+
<ascii-tabs max-fret="19"></ascii-tabs>
|
|
580
|
+
```
|
|
581
|
+
|
|
568
582
|
Strings are labelled `1`–`6` by default, on screen and in copied text. `labels="notes"` names them using `tuning` (standard tuning by default: `e B G D A E`, String 1 to 6). `labels="none"` hides the labels and uses their space for Columns. This also applies to copied text. The `labels` property accepts `"numbers"`, `"notes"`, or `"none"`; other values fall back to numbers. Parsing accepts all three styles whatever the attribute says.
|
|
569
583
|
|
|
570
584
|
```html
|
|
571
585
|
<ascii-tabs></ascii-tabs> <!-- 1 -3--5- -->
|
|
572
|
-
<ascii-tabs labels="notes"></ascii-tabs> <!-- e
|
|
586
|
+
<ascii-tabs labels="notes"></ascii-tabs> <!-- e -3--5- -->
|
|
573
587
|
<ascii-tabs labels="none"></ascii-tabs> <!-- -3--5- -->
|
|
574
588
|
```
|
|
575
589
|
|
|
590
|
+
String labels have no bars by default. Add the boolean `string-bars` attribute
|
|
591
|
+
(or set `element.stringBars = true`) to draw a `|` at the start of each String,
|
|
592
|
+
independently of the labels. The bars also appear in copied ASCII. Remove the
|
|
593
|
+
attribute or set the property to `false` to hide them.
|
|
594
|
+
|
|
595
|
+
```html
|
|
596
|
+
<ascii-tabs labels="notes" string-bars></ascii-tabs> <!-- e |-3--5- -->
|
|
597
|
+
<ascii-tabs labels="none" string-bars></ascii-tabs> <!-- |-3--5- -->
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
The standalone formatter accepts the same option: `format(tab, { stringBars: true })`.
|
|
601
|
+
|
|
576
602
|
## Tool position
|
|
577
603
|
|
|
578
604
|
A Sheet's tools (copy, delete) stand in a column to the right of the Staves by default, and the Staves leave room for them. `tools="top"` lines them up in a row above the first String instead. Also available as the `tools` property.
|
package/ascii-tabs.js
CHANGED
|
@@ -26,11 +26,12 @@ const labelStyle = value => (value === 'notes' || value === 'none' ? value : 'nu
|
|
|
26
26
|
const stringLabels = (labels, tuning) => {
|
|
27
27
|
if (labelStyle(labels) === 'none') return Array(STRING_COUNT).fill('');
|
|
28
28
|
if (labelStyle(labels) === 'numbers') return ['1 ', '2 ', '3 ', '4 ', '5 ', '6 '];
|
|
29
|
-
const names = tuning.map((name, s) => `${s === 0 ? name.toLowerCase() : name}
|
|
29
|
+
const names = tuning.map((name, s) => `${s === 0 ? name.toLowerCase() : name} `);
|
|
30
30
|
const width = Math.max(...names.map(name => name.length));
|
|
31
31
|
return names.map(name => name.padEnd(width, ' '));
|
|
32
32
|
};
|
|
33
33
|
const labelWidth = (labels, tuning) => stringLabels(labels, tuning)[0].length;
|
|
34
|
+
const prefixWidth = (labels, tuning, stringBars) => labelWidth(labels, tuning) + (stringBars ? 1 : 0);
|
|
34
35
|
const notationStyle = value => (value === 'letters' || value === 'solfege' ? value : 'frets');
|
|
35
36
|
const NOTE_NAMES = {
|
|
36
37
|
letters: ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'],
|
|
@@ -176,7 +177,7 @@ function layout(tab, spacing, capacity, notation, tuning, sizes = []) {
|
|
|
176
177
|
}
|
|
177
178
|
|
|
178
179
|
// One Tab as plain ASCII, every Staff cut right after its last Column
|
|
179
|
-
function format(tab, { spacing = DEFAULT_SPACING, labels = 'numbers', notation = 'frets', tuning = STANDARD_TUNING, width = Infinity, annotations = [], annotationFit = 'overflow' } = {}) {
|
|
180
|
+
function format(tab, { spacing = DEFAULT_SPACING, labels = 'numbers', stringBars = false, notation = 'frets', tuning = STANDARD_TUNING, width = Infinity, annotations = [], annotationFit = 'overflow' } = {}) {
|
|
180
181
|
tuning = normalizeTuning(tuning);
|
|
181
182
|
annotations = normalizeAnnotations([annotations], [tab])[0] ?? [];
|
|
182
183
|
const names = stringLabels(labels, tuning);
|
|
@@ -184,14 +185,14 @@ function format(tab, { spacing = DEFAULT_SPACING, labels = 'numbers', notation =
|
|
|
184
185
|
if (!columns.length) return '';
|
|
185
186
|
const lead = '-'.repeat(leadingDashes(spacing));
|
|
186
187
|
const trail = '-'.repeat(spacing - lead.length);
|
|
187
|
-
const capacity = width -
|
|
188
|
+
const capacity = width - prefixWidth(labels, tuning, stringBars);
|
|
188
189
|
const sizes = annotationSizes(columns, annotations, normalizeAnnotationFit(annotationFit), { spacing, notation, tuning, capacity });
|
|
189
190
|
return layout(columns, spacing, capacity, notation, tuning, sizes)
|
|
190
191
|
.map(([start, end]) => {
|
|
191
192
|
const offsets = columnOffsets(columns, start, end, spacing, notation, tuning, sizes);
|
|
192
|
-
const lines = annotationLines(annotations, start, end, offsets,
|
|
193
|
+
const lines = annotationLines(annotations, start, end, offsets, prefixWidth(labels, tuning, stringBars));
|
|
193
194
|
for (let s = 0; s < STRING_COUNT; s++) {
|
|
194
|
-
let line = names[s];
|
|
195
|
+
let line = names[s] + (stringBars ? '|' : '');
|
|
195
196
|
for (let c = start; c < end; c++) {
|
|
196
197
|
const digits = sizes[c] ?? columnDigits(columns[c], notation, tuning);
|
|
197
198
|
const fret = fretText(columns[c][s], s, notation, tuning);
|
|
@@ -229,7 +230,7 @@ function parse(ascii) {
|
|
|
229
230
|
continue;
|
|
230
231
|
}
|
|
231
232
|
const label = LABEL_PATTERN.exec(line);
|
|
232
|
-
if (!label && !/^\s
|
|
233
|
+
if (!label && !/^\s*[-|]/.test(line)) return fail(`unexpected line "${line.trim()}"`);
|
|
233
234
|
const body = label ? line.slice(label[0].length) : line;
|
|
234
235
|
if (!/^[-|\d\s]*$/.test(body.replace(/\(\d*\)/g, ''))) return fail(`unexpected characters in "${line.trim()}"`);
|
|
235
236
|
group.push(body);
|
|
@@ -453,6 +454,7 @@ ascii-tabs .ascii-tabs-text-editor:focus { border-bottom-color: var(--ascii-tabs
|
|
|
453
454
|
|
|
454
455
|
ascii-tabs .ascii-tabs-line { display: block; height: 1.5em; line-height: 1.5em; }
|
|
455
456
|
ascii-tabs .ascii-tabs-label { color: var(--ascii-tabs-dash); }
|
|
457
|
+
ascii-tabs .ascii-tabs-bar { display: inline-block; flex: 0 0 1ch; }
|
|
456
458
|
ascii-tabs .ascii-tabs-string { color: var(--ascii-tabs-string); }
|
|
457
459
|
ascii-tabs .ascii-tabs-cell {
|
|
458
460
|
display: inline-block; height: 1.5em;
|
|
@@ -785,7 +787,7 @@ class AsciiTabs extends Base {
|
|
|
785
787
|
}
|
|
786
788
|
}
|
|
787
789
|
|
|
788
|
-
static observedAttributes = ['readonly', 'spacing', 'labels', 'notation', 'tuning', 'lang', 'tools', 'annotations-editable', 'titles-editable', 'annotation-fit'];
|
|
790
|
+
static observedAttributes = ['readonly', 'spacing', 'labels', 'string-bars', 'notation', 'tuning', 'lang', 'tools', 'annotations-editable', 'titles-editable', 'annotation-fit', 'max-fret'];
|
|
789
791
|
|
|
790
792
|
// Strings: the `lang` preset (English by default) with `messages` merged over it
|
|
791
793
|
get #text() {
|
|
@@ -830,6 +832,14 @@ class AsciiTabs extends Base {
|
|
|
830
832
|
this.setAttribute('labels', labelStyle(value));
|
|
831
833
|
}
|
|
832
834
|
|
|
835
|
+
get stringBars() {
|
|
836
|
+
return this.hasAttribute('string-bars');
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
set stringBars(on) {
|
|
840
|
+
this.toggleAttribute('string-bars', Boolean(on));
|
|
841
|
+
}
|
|
842
|
+
|
|
833
843
|
get notation() {
|
|
834
844
|
return notationStyle(this.getAttribute('notation'));
|
|
835
845
|
}
|
|
@@ -846,6 +856,16 @@ class AsciiTabs extends Base {
|
|
|
846
856
|
this.setAttribute('tuning', normalizeTuning(value).join(' '));
|
|
847
857
|
}
|
|
848
858
|
|
|
859
|
+
// Highest Fret the editor accepts: 24 by default, lower for instruments with fewer frets
|
|
860
|
+
get maxFret() {
|
|
861
|
+
const n = Math.floor(Number(this.getAttribute('max-fret')));
|
|
862
|
+
return Number.isFinite(n) && n >= 1 && n <= MAX_FRET ? n : MAX_FRET;
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
set maxFret(value) {
|
|
866
|
+
this.setAttribute('max-fret', value);
|
|
867
|
+
}
|
|
868
|
+
|
|
849
869
|
get tools() {
|
|
850
870
|
return toolsPosition(this.getAttribute('tools'));
|
|
851
871
|
}
|
|
@@ -881,7 +901,7 @@ class AsciiTabs extends Base {
|
|
|
881
901
|
|
|
882
902
|
attributeChangedCallback(name) {
|
|
883
903
|
if (name === 'spacing') return this.#applySpacing();
|
|
884
|
-
if (name === 'labels' || name === 'tuning') {
|
|
904
|
+
if (name === 'labels' || name === 'string-bars' || name === 'tuning') {
|
|
885
905
|
this.#measure();
|
|
886
906
|
return this.#render();
|
|
887
907
|
}
|
|
@@ -911,7 +931,7 @@ class AsciiTabs extends Base {
|
|
|
911
931
|
return;
|
|
912
932
|
}
|
|
913
933
|
// Properties set before the element was upgraded shadow the accessors
|
|
914
|
-
for (const name of ['value', 'columnLayout', 'annotations', 'tabTitles', 'annotationsEditable', 'titlesEditable', 'annotationFit', 'readonly', 'spacing', 'labels', 'notation', 'tuning', 'tools', 'variant', 'theme', 'messages']) {
|
|
934
|
+
for (const name of ['value', 'columnLayout', 'annotations', 'tabTitles', 'annotationsEditable', 'titlesEditable', 'annotationFit', 'readonly', 'spacing', 'labels', 'stringBars', 'notation', 'tuning', 'maxFret', 'tools', 'variant', 'theme', 'messages']) {
|
|
915
935
|
if (Object.hasOwn(this, name)) {
|
|
916
936
|
const own = this[name];
|
|
917
937
|
delete this[name];
|
|
@@ -1110,7 +1130,7 @@ class AsciiTabs extends Base {
|
|
|
1110
1130
|
if (!charWidth) return;
|
|
1111
1131
|
this.#scheduleOverflow();
|
|
1112
1132
|
const chars = Math.floor(width / charWidth);
|
|
1113
|
-
const capacity = Math.max(chars -
|
|
1133
|
+
const capacity = Math.max(chars - prefixWidth(this.labels, this.tuning, this.stringBars), 4 * (this.#spacing + 1));
|
|
1114
1134
|
const signature = `${width}:${charWidth}`;
|
|
1115
1135
|
const annotationResized = this.#annotationMeasure !== signature;
|
|
1116
1136
|
this.#annotationMeasure = signature;
|
|
@@ -1188,6 +1208,7 @@ class AsciiTabs extends Base {
|
|
|
1188
1208
|
for (let s = 0; s < STRING_COUNT; s++) {
|
|
1189
1209
|
html += '<span class="ascii-tabs-line">';
|
|
1190
1210
|
if (labels !== 'none') html += `<span class="ascii-tabs-label" style="flex-basis:${gutter}ch">${names[s]}</span>`;
|
|
1211
|
+
if (this.stringBars) html += '<span class="ascii-tabs-bar ascii-tabs-string" aria-hidden="true">|</span>';
|
|
1191
1212
|
if (geometry) html += `<span class="ascii-tabs-area" data-t="${t}" data-s="${s}" style="width:${geometry.width}px">`;
|
|
1192
1213
|
for (let c = start; c < end; c++) {
|
|
1193
1214
|
const isCur = this.#cur && this.#cur.t === t && this.#cur.c === c && this.#cur.s === s;
|
|
@@ -1239,7 +1260,7 @@ class AsciiTabs extends Base {
|
|
|
1239
1260
|
const annotations = entries.filter(a => a.column >= start && a.column < end && (a.html ?? a.text));
|
|
1240
1261
|
if (!annotations.length && !(this.annotationsEditable && !this.readonly)) return;
|
|
1241
1262
|
const band = this.#el('div', 'ascii-tabs-annotations');
|
|
1242
|
-
if (geometry) band.style.width = `calc(${geometry.width}px + ${
|
|
1263
|
+
if (geometry) band.style.width = `calc(${geometry.width}px + ${prefixWidth(this.labels, this.tuning, this.stringBars)}ch)`;
|
|
1243
1264
|
staff.prepend(band);
|
|
1244
1265
|
const shown = shownAnnotations(entries);
|
|
1245
1266
|
for (const entry of annotations) {
|
|
@@ -1653,6 +1674,33 @@ class AsciiTabs extends Base {
|
|
|
1653
1674
|
this.#goTo(t, c, s + d);
|
|
1654
1675
|
}
|
|
1655
1676
|
|
|
1677
|
+
// Shift+Up/Down: re-finger the focused or selected notes on the nearest String that keeps their
|
|
1678
|
+
// pitch and puts every one of them within 0..maxFret. Strings are assumed to descend in pitch.
|
|
1679
|
+
#refinger(d) {
|
|
1680
|
+
const notes = (this.#selection.length ? this.#selection : [this.#cur]).filter(p => this.#tabs[p.t][p.c]?.[p.s] != null);
|
|
1681
|
+
if (!notes.length) return;
|
|
1682
|
+
const tuning = this.tuning;
|
|
1683
|
+
const depth = [0];
|
|
1684
|
+
for (let s = 1; s < STRING_COUNT; s++) depth[s] = depth[s - 1] + ((tuningPitch(tuning[s - 1]) - tuningPitch(tuning[s]) + 11) % 12 + 1);
|
|
1685
|
+
for (let step = 1; step < STRING_COUNT; step++) {
|
|
1686
|
+
const moved = notes.map(p => {
|
|
1687
|
+
const to = p.s + d * step;
|
|
1688
|
+
const note = this.#tabs[p.t][p.c][p.s];
|
|
1689
|
+
return { ...p, to, fret: fretValue(note) + depth[to] - depth[p.s], note };
|
|
1690
|
+
});
|
|
1691
|
+
if (moved.some(m => m.to < 0 || m.to >= STRING_COUNT || !isFret(m.fret) || m.fret > this.maxFret)) {
|
|
1692
|
+
if (moved.some(m => m.to < 0 || m.to >= STRING_COUNT)) return;
|
|
1693
|
+
continue;
|
|
1694
|
+
}
|
|
1695
|
+
for (const m of moved) this.#setFret(m.t, m.c, m.s, null);
|
|
1696
|
+
for (const m of moved) this.#setFret(m.t, m.c, m.to, typeof m.note === 'object' ? { ...m.note, fret: m.fret } : m.fret);
|
|
1697
|
+
const { t, c, s } = this.#cur;
|
|
1698
|
+
if (this.#selection.length) this.#selection = moved.map(m => ({ t: m.t, c: m.c, s: m.to }));
|
|
1699
|
+
this.#goTo(t, c, s + d * step, this.#selection.length > 0);
|
|
1700
|
+
return this.#emitChange(this.#selection.length ? null : this.#cur);
|
|
1701
|
+
}
|
|
1702
|
+
}
|
|
1703
|
+
|
|
1656
1704
|
// Typing writes to the value straight away, so `change` follows every keystroke
|
|
1657
1705
|
#edit(draft, keepParentheses = true) {
|
|
1658
1706
|
this.#selection = [];
|
|
@@ -1670,7 +1718,7 @@ class AsciiTabs extends Base {
|
|
|
1670
1718
|
#typeDigit(d) {
|
|
1671
1719
|
if (this.#draft === null || this.#draft === '') return this.#edit(d);
|
|
1672
1720
|
const next = this.#draft + d;
|
|
1673
|
-
if (/^(0|[1-9][0-9]?)$/.test(next) && Number(next) <=
|
|
1721
|
+
if (/^(0|[1-9][0-9]?)$/.test(next) && Number(next) <= this.maxFret) this.#edit(next);
|
|
1674
1722
|
}
|
|
1675
1723
|
|
|
1676
1724
|
#parenthesize() {
|
|
@@ -1751,10 +1799,10 @@ class AsciiTabs extends Base {
|
|
|
1751
1799
|
return this.#moveHorizontal(-1);
|
|
1752
1800
|
case 'ArrowUp':
|
|
1753
1801
|
e.preventDefault();
|
|
1754
|
-
return this.#moveVertical(-1);
|
|
1802
|
+
return e.shiftKey ? this.#refinger(-1) : this.#moveVertical(-1);
|
|
1755
1803
|
case 'ArrowDown':
|
|
1756
1804
|
e.preventDefault();
|
|
1757
|
-
return this.#moveVertical(1);
|
|
1805
|
+
return e.shiftKey ? this.#refinger(1) : this.#moveVertical(1);
|
|
1758
1806
|
case 'Backspace': {
|
|
1759
1807
|
e.preventDefault();
|
|
1760
1808
|
const { t, c, s } = this.#cur;
|
|
@@ -1786,7 +1834,7 @@ class AsciiTabs extends Base {
|
|
|
1786
1834
|
const last = Math.max(...this.#selection.map(p => p.c));
|
|
1787
1835
|
const columns = Array.from({ length: last - first + 1 }, emptyColumn);
|
|
1788
1836
|
for (const { t, c, s } of this.#selection) columns[c - first][s] = this.#tabs[t][c][s];
|
|
1789
|
-
return format(columns, { spacing: this.#spacing, labels: this.labels, notation: this.notation, tuning: this.tuning });
|
|
1837
|
+
return format(columns, { spacing: this.#spacing, labels: this.labels, stringBars: this.stringBars, notation: this.notation, tuning: this.tuning });
|
|
1790
1838
|
}
|
|
1791
1839
|
|
|
1792
1840
|
#onCopy = e => {
|
|
@@ -1975,7 +2023,7 @@ class AsciiTabs extends Base {
|
|
|
1975
2023
|
async copy(index = 0) {
|
|
1976
2024
|
const tab = this.#tabs[index];
|
|
1977
2025
|
if (!tab) return;
|
|
1978
|
-
await copyText(this.#selection.length && this.#selection[0].t === index ? this.#selectionText() : format(tab, { spacing: this.#spacing, labels: this.labels, notation: this.notation, tuning: this.tuning, width: this.#capacity +
|
|
2026
|
+
await copyText(this.#selection.length && this.#selection[0].t === index ? this.#selectionText() : format(tab, { spacing: this.#spacing, labels: this.labels, stringBars: this.stringBars, notation: this.notation, tuning: this.tuning, width: this.#capacity + prefixWidth(this.labels, this.tuning, this.stringBars), annotations: this.#annotations[index], annotationFit: this.#annotationFit() }));
|
|
1979
2027
|
const button = this.#sheetEls[index]?.querySelector('.ascii-tabs-copy');
|
|
1980
2028
|
if (!button) return;
|
|
1981
2029
|
this.#flashCopied(button);
|