text-guitar-chart 0.1.2 → 0.2.0

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.
@@ -958,6 +958,227 @@ describe("fingeringToString", () => {
958
958
  assert.equal(result, expected);
959
959
  });
960
960
 
961
+ test("grey color outputs O marker (ASCII)", () => {
962
+ /** @type {import("svguitar").Chord} */
963
+ const chord = {
964
+ fingers: [
965
+ [3, 2, { text: "", color: "#9B9B9B" }],
966
+ [4, 2, { text: "", color: "#000000" }],
967
+ ],
968
+ barres: [],
969
+ title: "Test",
970
+ };
971
+
972
+ const result = fingeringToString(chord);
973
+
974
+ const expected = ` Test
975
+ ######
976
+ ------
977
+ ||||||
978
+ ||oO||
979
+ ||||||`;
980
+
981
+ assert.equal(result, expected);
982
+ });
983
+
984
+ test("blue color outputs + marker (ASCII)", () => {
985
+ /** @type {import("svguitar").Chord} */
986
+ const chord = {
987
+ fingers: [
988
+ [3, 2, { text: "", color: "#4A90E2" }],
989
+ [4, 2, { text: "", color: "#000000" }],
990
+ ],
991
+ barres: [],
992
+ title: "Test",
993
+ };
994
+
995
+ const result = fingeringToString(chord);
996
+
997
+ const expected = ` Test
998
+ ######
999
+ ------
1000
+ ||||||
1001
+ ||o+||
1002
+ ||||||`;
1003
+
1004
+ assert.equal(result, expected);
1005
+ });
1006
+
1007
+ test("grey color outputs □ marker (Unicode)", () => {
1008
+ /** @type {import("svguitar").Chord} */
1009
+ const chord = {
1010
+ fingers: [
1011
+ [3, 2, { text: "", color: "#9B9B9B" }],
1012
+ [4, 2, { text: "", color: "#000000" }],
1013
+ ],
1014
+ barres: [],
1015
+ title: "Test",
1016
+ };
1017
+
1018
+ const result = fingeringToString(chord, { useUnicode: true });
1019
+
1020
+ const expected = ` Test
1021
+ ‾‾‾‾‾‾‾‾‾‾‾
1022
+ ┌─┬─┬─┬─┬─┐
1023
+ │ │ │ │ │ │
1024
+ ├─┼─┼─┼─┼─┤
1025
+ │ │ ○ □ │ │
1026
+ ├─┼─┼─┼─┼─┤
1027
+ │ │ │ │ │ │
1028
+ └─┴─┴─┴─┴─┘`;
1029
+
1030
+ assert.equal(result, expected);
1031
+ });
1032
+
1033
+ test("blue color outputs ■ marker (Unicode)", () => {
1034
+ /** @type {import("svguitar").Chord} */
1035
+ const chord = {
1036
+ fingers: [
1037
+ [3, 2, { text: "", color: "#4A90E2" }],
1038
+ [4, 2, { text: "", color: "#000000" }],
1039
+ ],
1040
+ barres: [],
1041
+ title: "Test",
1042
+ };
1043
+
1044
+ const result = fingeringToString(chord, { useUnicode: true });
1045
+
1046
+ const expected = ` Test
1047
+ ‾‾‾‾‾‾‾‾‾‾‾
1048
+ ┌─┬─┬─┬─┬─┐
1049
+ │ │ │ │ │ │
1050
+ ├─┼─┼─┼─┼─┤
1051
+ │ │ ○ ■ │ │
1052
+ ├─┼─┼─┼─┼─┤
1053
+ │ │ │ │ │ │
1054
+ └─┴─┴─┴─┴─┘`;
1055
+
1056
+ assert.equal(result, expected);
1057
+ });
1058
+
1059
+ test("grey/blue color ignores text like red does (ASCII)", () => {
1060
+ /** @type {import("svguitar").Chord} */
1061
+ const chord = {
1062
+ fingers: [
1063
+ [3, 2, { text: "R", color: "#9B9B9B" }],
1064
+ [4, 2, { text: "B", color: "#4A90E2" }],
1065
+ ],
1066
+ barres: [],
1067
+ title: "Test",
1068
+ };
1069
+
1070
+ const result = fingeringToString(chord);
1071
+
1072
+ const expected = ` Test
1073
+ ######
1074
+ ------
1075
+ ||||||
1076
+ ||+O||
1077
+ ||||||`;
1078
+
1079
+ assert.equal(result, expected);
1080
+ });
1081
+
1082
+ test("mixed colors chord with all four colors (ASCII)", () => {
1083
+ /** @type {import("svguitar").Chord} */
1084
+ const chord = {
1085
+ fingers: [
1086
+ [6, 1, { text: "", color: "#000000" }],
1087
+ [5, 1, { text: "", color: "#e74c3c" }],
1088
+ [4, 1, { text: "", color: "#9B9B9B" }],
1089
+ [3, 1, { text: "", color: "#4A90E2" }],
1090
+ ],
1091
+ barres: [],
1092
+ title: "Mix",
1093
+ };
1094
+
1095
+ const result = fingeringToString(chord);
1096
+
1097
+ const expected = ` Mix
1098
+ ######
1099
+ ------
1100
+ o*O+||
1101
+ ||||||
1102
+ ||||||`;
1103
+
1104
+ assert.equal(result, expected);
1105
+ });
1106
+
1107
+ test("mixed colors chord with all four colors (Unicode)", () => {
1108
+ /** @type {import("svguitar").Chord} */
1109
+ const chord = {
1110
+ fingers: [
1111
+ [6, 1, { text: "", color: "#000000" }],
1112
+ [5, 1, { text: "", color: "#e74c3c" }],
1113
+ [4, 1, { text: "", color: "#9B9B9B" }],
1114
+ [3, 1, { text: "", color: "#4A90E2" }],
1115
+ ],
1116
+ barres: [],
1117
+ title: "Mix",
1118
+ };
1119
+
1120
+ const result = fingeringToString(chord, { useUnicode: true });
1121
+
1122
+ const expected = ` Mix
1123
+ ‾‾‾‾‾‾‾‾‾‾‾
1124
+ ┌─┬─┬─┬─┬─┐
1125
+ ○ ● □ ■ │ │
1126
+ ├─┼─┼─┼─┼─┤
1127
+ │ │ │ │ │ │
1128
+ ├─┼─┼─┼─┼─┤
1129
+ │ │ │ │ │ │
1130
+ └─┴─┴─┴─┴─┘`;
1131
+
1132
+ assert.equal(result, expected);
1133
+ });
1134
+
1135
+ test("grey/blue colors ignored for open strings (ASCII)", () => {
1136
+ /** @type {import("svguitar").Chord} */
1137
+ const chord = {
1138
+ fingers: [
1139
+ [6, 0, { text: "", color: "#9B9B9B" }],
1140
+ [5, 0, { text: "", color: "#4A90E2" }],
1141
+ [4, 0, { text: "R", color: "#9B9B9B" }],
1142
+ ],
1143
+ barres: [],
1144
+ };
1145
+
1146
+ const result = fingeringToString(chord);
1147
+
1148
+ const expected = ` ooR
1149
+ ------
1150
+ ||||||
1151
+ ||||||
1152
+ ||||||`;
1153
+
1154
+ assert.equal(result, expected);
1155
+ });
1156
+
1157
+ test("grey/blue colors ignored for open strings (Unicode)", () => {
1158
+ /** @type {import("svguitar").Chord} */
1159
+ const chord = {
1160
+ fingers: [
1161
+ [6, 0, { text: "", color: "#9B9B9B" }],
1162
+ [5, 0, { text: "", color: "#4A90E2" }],
1163
+ [4, 0, { text: "R", color: "#9B9B9B" }],
1164
+ ],
1165
+ barres: [],
1166
+ };
1167
+
1168
+ const result = fingeringToString(chord, { useUnicode: true });
1169
+
1170
+ const expected = ` ○ ○ R
1171
+ ┌─┬─┬─┬─┬─┐
1172
+ │ │ │ │ │ │
1173
+ ├─┼─┼─┼─┼─┤
1174
+ │ │ │ │ │ │
1175
+ ├─┼─┼─┼─┼─┤
1176
+ │ │ │ │ │ │
1177
+ └─┴─┴─┴─┴─┘`;
1178
+
1179
+ assert.equal(result, expected);
1180
+ });
1181
+
961
1182
  test("empty options object in finger array treated as default", () => {
962
1183
  /** @type {import("svguitar").Chord} */
963
1184
  const chord = {
@@ -1494,4 +1494,180 @@ describe("stringToFingering", () => {
1494
1494
  assert.deepEqual(result?.barres, []);
1495
1495
  });
1496
1496
  });
1497
+
1498
+ describe("Grey and blue color markers", () => {
1499
+ test("parses grey marker (O) in ASCII format", () => {
1500
+ const fingeringStr = ` Test
1501
+ ######
1502
+ x
1503
+ ------
1504
+ |||||O
1505
+ ||||||
1506
+ ||||||`;
1507
+
1508
+ const result = stringToFingering(fingeringStr);
1509
+ assert.equal(
1510
+ fingersContains(result, [
1511
+ [6, "x", { text: "", color: "#000000" }],
1512
+ [1, 1, { text: "", color: "#9B9B9B" }],
1513
+ ]),
1514
+ true,
1515
+ "Fingering does not match expected grey marker"
1516
+ );
1517
+ });
1518
+
1519
+ test("parses blue marker (+) in ASCII format", () => {
1520
+ const fingeringStr = ` Test
1521
+ ######
1522
+ x
1523
+ ------
1524
+ |||||+
1525
+ ||||||
1526
+ ||||||`;
1527
+
1528
+ const result = stringToFingering(fingeringStr);
1529
+ assert.equal(
1530
+ fingersContains(result, [
1531
+ [6, "x", { text: "", color: "#000000" }],
1532
+ [1, 1, { text: "", color: "#4A90E2" }],
1533
+ ]),
1534
+ true,
1535
+ "Fingering does not match expected blue marker"
1536
+ );
1537
+ });
1538
+
1539
+ test("parses grey marker (□) in Unicode format", () => {
1540
+ const fingeringStr = ` Test
1541
+ ‾‾‾‾‾‾‾‾‾‾‾
1542
+ ×
1543
+ ┌─┬─┬─┬─┬─┐
1544
+ │ │ │ │ │ □
1545
+ ├─┼─┼─┼─┼─┤
1546
+ │ │ │ │ │ │
1547
+ ├─┼─┼─┼─┼─┤
1548
+ │ │ │ │ │ │
1549
+ └─┴─┴─┴─┴─┘`;
1550
+
1551
+ const result = stringToFingering(fingeringStr);
1552
+ assert.equal(
1553
+ fingersContains(result, [
1554
+ [6, "x", { text: "", color: "#000000" }],
1555
+ [1, 1, { text: "", color: "#9B9B9B" }],
1556
+ ]),
1557
+ true,
1558
+ "Fingering does not match expected grey Unicode marker"
1559
+ );
1560
+ });
1561
+
1562
+ test("parses blue marker (■) in Unicode format", () => {
1563
+ const fingeringStr = ` Test
1564
+ ‾‾‾‾‾‾‾‾‾‾‾
1565
+ ×
1566
+ ┌─┬─┬─┬─┬─┐
1567
+ │ │ │ │ │ ■
1568
+ ├─┼─┼─┼─┼─┤
1569
+ │ │ │ │ │ │
1570
+ ├─┼─┼─┼─┼─┤
1571
+ │ │ │ │ │ │
1572
+ └─┴─┴─┴─┴─┘`;
1573
+
1574
+ const result = stringToFingering(fingeringStr);
1575
+ assert.equal(
1576
+ fingersContains(result, [
1577
+ [6, "x", { text: "", color: "#000000" }],
1578
+ [1, 1, { text: "", color: "#4A90E2" }],
1579
+ ]),
1580
+ true,
1581
+ "Fingering does not match expected blue Unicode marker"
1582
+ );
1583
+ });
1584
+
1585
+ test("parses mixed 4-color chord in ASCII format", () => {
1586
+ const fingeringStr = ` Mixed
1587
+ ######
1588
+
1589
+ ------
1590
+ o*O+||
1591
+ ||||||
1592
+ ||||||`;
1593
+
1594
+ const result = stringToFingering(fingeringStr);
1595
+ assert.equal(
1596
+ fingersContains(result, [
1597
+ [6, 1, { text: "", color: "#000000" }],
1598
+ [5, 1, { text: "", color: "#e74c3c" }],
1599
+ [4, 1, { text: "", color: "#9B9B9B" }],
1600
+ [3, 1, { text: "", color: "#4A90E2" }],
1601
+ ]),
1602
+ true,
1603
+ "Fingering does not match expected mixed 4-color chord"
1604
+ );
1605
+ });
1606
+
1607
+ test("parses mixed 4-color chord in Unicode format", () => {
1608
+ const fingeringStr = ` Mixed
1609
+ ‾‾‾‾‾‾‾‾‾‾‾
1610
+
1611
+ ┌─┬─┬─┬─┬─┐
1612
+ ○ ● □ ■ │ │
1613
+ ├─┼─┼─┼─┼─┤
1614
+ │ │ │ │ │ │
1615
+ ├─┼─┼─┼─┼─┤
1616
+ │ │ │ │ │ │
1617
+ └─┴─┴─┴─┴─┘`;
1618
+
1619
+ const result = stringToFingering(fingeringStr);
1620
+ assert.equal(
1621
+ fingersContains(result, [
1622
+ [6, 1, { text: "", color: "#000000" }],
1623
+ [5, 1, { text: "", color: "#e74c3c" }],
1624
+ [4, 1, { text: "", color: "#9B9B9B" }],
1625
+ [3, 1, { text: "", color: "#4A90E2" }],
1626
+ ]),
1627
+ true,
1628
+ "Fingering does not match expected mixed 4-color Unicode chord"
1629
+ );
1630
+ });
1631
+
1632
+ test("parses custom grey color option", () => {
1633
+ const fingeringStr = ` ------
1634
+ |||||O
1635
+ ||||||
1636
+ ||||||`;
1637
+
1638
+ const result = stringToFingering(fingeringStr, { greyColor: "#AAAAAA" });
1639
+ assert.equal(
1640
+ fingersContains(result, [
1641
+ [1, 1, { text: "", color: "#AAAAAA" }],
1642
+ ]),
1643
+ true,
1644
+ "Should use custom grey color"
1645
+ );
1646
+ });
1647
+
1648
+ test("parses custom blue color option", () => {
1649
+ const fingeringStr = ` ------
1650
+ |||||+
1651
+ ||||||
1652
+ ||||||`;
1653
+
1654
+ const result = stringToFingering(fingeringStr, { blueColor: "#0000FF" });
1655
+ assert.equal(
1656
+ fingersContains(result, [
1657
+ [1, 1, { text: "", color: "#0000FF" }],
1658
+ ]),
1659
+ true,
1660
+ "Should use custom blue color"
1661
+ );
1662
+ });
1663
+
1664
+ test("grey/blue markers detected as Unicode format", () => {
1665
+ // □ alone should trigger Unicode detection
1666
+ const greyStr = " □";
1667
+ const blueStr = " ■";
1668
+ // Use a helper to check - these strings contain Unicode markers
1669
+ assert.ok(greyStr.includes("□"), "Should contain grey Unicode marker");
1670
+ assert.ok(blueStr.includes("■"), "Should contain blue Unicode marker");
1671
+ });
1672
+ });
1497
1673
  });
@@ -1,6 +1,8 @@
1
1
  export namespace DOT_COLORS {
2
2
  let RED: string;
3
3
  let BLACK: string;
4
+ let GREY: string;
5
+ let BLUE: string;
4
6
  }
5
7
  /**
6
8
  * EditableSVGuitarChord - Wrapper around SVGuitarChord that adds interactive editing capabilities
@@ -51,8 +53,9 @@ export class EditableSVGuitarChord {
51
53
  createDialog(): void;
52
54
  dialog: HTMLDivElement;
53
55
  redRadio: HTMLInputElement;
56
+ greyRadio: HTMLInputElement;
57
+ blueRadio: HTMLInputElement;
54
58
  blackRadio: HTMLInputElement;
55
- textSection: HTMLDivElement;
56
59
  textInput: HTMLInputElement;
57
60
  backdrop: HTMLDivElement;
58
61
  /**
@@ -142,6 +145,24 @@ export class EditableSVGuitarChord {
142
145
  * Open the open string edit dialog
143
146
  */
144
147
  openOpenStringDialog(): void;
148
+ /**
149
+ * Calculate absolute position for a dialog relative to a reference element
150
+ * @param {HTMLElement} dialog - The dialog element to position
151
+ * @param {Element} referenceElement - The element to position relative to
152
+ * @param {object} options - Positioning options
153
+ * @param {'beside'|'below'} [options.placement] - Whether to place beside or below the reference
154
+ * @param {number} [options.offset] - Distance from reference element
155
+ * @returns {{x: number, y: number, arrowSide: string, elementCenterY: number}}
156
+ */
157
+ calculateDialogPosition(dialog: HTMLElement, referenceElement: Element, options?: {
158
+ placement?: "beside" | "below";
159
+ offset?: number;
160
+ }): {
161
+ x: number;
162
+ y: number;
163
+ arrowSide: string;
164
+ elementCenterY: number;
165
+ };
145
166
  /**
146
167
  * Position dialog relative to the clicked element
147
168
  */
@@ -1,10 +1,12 @@
1
1
  /**
2
2
  * Parse a string representation of a guitar fingering into internal format
3
3
  * @param {string} fingeringStr - The string representation of the fingering
4
- * @param {{ redColor?: string, blackColor?: string }} [options]
4
+ * @param {{ redColor?: string, blackColor?: string, greyColor?: string, blueColor?: string }} [options]
5
5
  * @returns {import("svguitar").Chord | null} The parsed fingering object
6
6
  */
7
7
  export default function stringToFingering(fingeringStr: string, options?: {
8
8
  redColor?: string;
9
9
  blackColor?: string;
10
+ greyColor?: string;
11
+ blueColor?: string;
10
12
  }): import("svguitar").Chord | null;