@ovnonvo/abc-editor 0.1.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.
- package/LICENSE +21 -0
- package/README.md +213 -0
- package/dist/abc-editor.cjs.js +16 -0
- package/dist/abc-editor.es.js +924 -0
- package/dist/lib/index.d.ts +13 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/src/components/AbcEditor.d.ts +7 -0
- package/dist/src/components/AbcEditor.d.ts.map +1 -0
- package/dist/src/components/AbcPreview.d.ts +4 -0
- package/dist/src/components/AbcPreview.d.ts.map +1 -0
- package/dist/src/components/SuggestionList.d.ts +14 -0
- package/dist/src/components/SuggestionList.d.ts.map +1 -0
- package/dist/src/data/abcCommands.d.ts +4 -0
- package/dist/src/data/abcCommands.d.ts.map +1 -0
- package/dist/src/data/abcSuggestions.d.ts +9 -0
- package/dist/src/data/abcSuggestions.d.ts.map +1 -0
- package/dist/src/hooks/useAbcAutoComplete.d.ts +21 -0
- package/dist/src/hooks/useAbcAutoComplete.d.ts.map +1 -0
- package/dist/src/hooks/useLineNumbers.d.ts +2 -0
- package/dist/src/hooks/useLineNumbers.d.ts.map +1 -0
- package/dist/src/types/abc.d.ts +26 -0
- package/dist/src/types/abc.d.ts.map +1 -0
- package/dist/src/utils/highlightAbc.d.ts +24 -0
- package/dist/src/utils/highlightAbc.d.ts.map +1 -0
- package/dist/src/utils/validateAbc.d.ts +14 -0
- package/dist/src/utils/validateAbc.d.ts.map +1 -0
- package/package.json +83 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 [Your Name]
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# ABC Editor
|
|
2
|
+
|
|
3
|
+
A React component for editing ABC music notation with syntax highlighting, autocomplete, and real-time validation.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Syntax Highlighting**: Color-coded ABC notation elements
|
|
8
|
+
- **Line Numbers**: Clear visual reference for navigation
|
|
9
|
+
- **Autocomplete**: Smart suggestions for ABC notation commands and fields
|
|
10
|
+
- **Real-time Validation**: Measure beat count validation with visual feedback
|
|
11
|
+
- **Preview**: Live preview of music notation using abcjs
|
|
12
|
+
- **Error Highlighting**: Hover over validation errors to highlight the problematic measure
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install @yourusername/abc-editor
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
or
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
yarn add @yourusername/abc-editor
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Peer Dependencies
|
|
27
|
+
|
|
28
|
+
This package requires the following peer dependencies:
|
|
29
|
+
|
|
30
|
+
```json
|
|
31
|
+
{
|
|
32
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
33
|
+
"react-dom": "^18.0.0 || ^19.0.0",
|
|
34
|
+
"abcjs": "^6.0.0"
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Install them if you haven't already:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npm install react react-dom abcjs
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Usage
|
|
45
|
+
|
|
46
|
+
### Basic Example
|
|
47
|
+
|
|
48
|
+
```tsx
|
|
49
|
+
import { useState } from 'react';
|
|
50
|
+
import { AbcEditor, AbcPreview } from '@yourusername/abc-editor';
|
|
51
|
+
|
|
52
|
+
function App() {
|
|
53
|
+
const [abcCode, setAbcCode] = useState(`X:1
|
|
54
|
+
T:Untitled
|
|
55
|
+
M:4/4
|
|
56
|
+
K:C
|
|
57
|
+
C D E F | G A B c |`);
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<div style={{ display: 'flex', height: '100vh' }}>
|
|
61
|
+
<AbcEditor value={abcCode} onChange={setAbcCode} />
|
|
62
|
+
<AbcPreview value={abcCode} />
|
|
63
|
+
</div>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export default App;
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Components
|
|
71
|
+
|
|
72
|
+
#### `AbcEditor`
|
|
73
|
+
|
|
74
|
+
The main editor component with syntax highlighting and validation.
|
|
75
|
+
|
|
76
|
+
**Props:**
|
|
77
|
+
- `value` (string): The ABC notation code
|
|
78
|
+
- `onChange` ((value: string) => void): Callback when the code changes
|
|
79
|
+
|
|
80
|
+
**Features:**
|
|
81
|
+
- Line numbers
|
|
82
|
+
- Syntax highlighting
|
|
83
|
+
- Autocomplete (type `/` to trigger)
|
|
84
|
+
- Real-time validation
|
|
85
|
+
- Error display with hover highlighting
|
|
86
|
+
|
|
87
|
+
#### `AbcPreview`
|
|
88
|
+
|
|
89
|
+
Preview component that renders the ABC notation as sheet music.
|
|
90
|
+
|
|
91
|
+
**Props:**
|
|
92
|
+
- `value` (string): The ABC notation code to render
|
|
93
|
+
|
|
94
|
+
### Autocomplete
|
|
95
|
+
|
|
96
|
+
Type `/` followed by a command to trigger autocomplete:
|
|
97
|
+
|
|
98
|
+
- `/header` - Insert ABC header template
|
|
99
|
+
- `/note` - Insert note example
|
|
100
|
+
- `/chord` - Insert chord example
|
|
101
|
+
- `/measure` - Insert measure example
|
|
102
|
+
|
|
103
|
+
### Validation
|
|
104
|
+
|
|
105
|
+
The editor validates measure beat counts based on the time signature (M:) and unit note length (L:):
|
|
106
|
+
|
|
107
|
+
- Errors are displayed at the bottom of the editor
|
|
108
|
+
- Hover over an error to highlight the problematic measure
|
|
109
|
+
- Shows line number, measure number, and expected vs. actual beat count
|
|
110
|
+
|
|
111
|
+
### Hooks
|
|
112
|
+
|
|
113
|
+
#### `useLineNumbers(value: string): string`
|
|
114
|
+
|
|
115
|
+
Generates line numbers for the editor.
|
|
116
|
+
|
|
117
|
+
```tsx
|
|
118
|
+
import { useLineNumbers } from '@yourusername/abc-editor';
|
|
119
|
+
|
|
120
|
+
const lineNumbers = useLineNumbers(abcCode);
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
#### `useAbcAutoComplete(options)`
|
|
124
|
+
|
|
125
|
+
Provides autocomplete functionality for ABC notation.
|
|
126
|
+
|
|
127
|
+
```tsx
|
|
128
|
+
import { useAbcAutoComplete } from '@yourusername/abc-editor';
|
|
129
|
+
|
|
130
|
+
const {
|
|
131
|
+
isOpen,
|
|
132
|
+
suggestions,
|
|
133
|
+
selectedIndex,
|
|
134
|
+
position,
|
|
135
|
+
handleKeyDown,
|
|
136
|
+
selectSuggestion,
|
|
137
|
+
handleMouseEnter,
|
|
138
|
+
} = useAbcAutoComplete({
|
|
139
|
+
value: abcCode,
|
|
140
|
+
textareaRef,
|
|
141
|
+
onChange: setAbcCode,
|
|
142
|
+
});
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Utilities
|
|
146
|
+
|
|
147
|
+
#### `highlightAbc(code: string): string`
|
|
148
|
+
|
|
149
|
+
Returns syntax-highlighted HTML for ABC notation.
|
|
150
|
+
|
|
151
|
+
```tsx
|
|
152
|
+
import { highlightAbc } from '@yourusername/abc-editor';
|
|
153
|
+
|
|
154
|
+
const highlighted = highlightAbc('C D E F | G A B c |');
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
#### `validateAbc(code: string): ValidationError[]`
|
|
158
|
+
|
|
159
|
+
Validates ABC notation and returns errors.
|
|
160
|
+
|
|
161
|
+
```tsx
|
|
162
|
+
import { validateAbc } from '@yourusername/abc-editor';
|
|
163
|
+
|
|
164
|
+
const errors = validateAbc(abcCode);
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Types
|
|
168
|
+
|
|
169
|
+
```tsx
|
|
170
|
+
import type {
|
|
171
|
+
AbcField,
|
|
172
|
+
AbcFieldKey,
|
|
173
|
+
ValidationError,
|
|
174
|
+
} from '@yourusername/abc-editor';
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Styling
|
|
178
|
+
|
|
179
|
+
The editor uses Tailwind CSS internally. Make sure your project supports Tailwind or includes the generated CSS.
|
|
180
|
+
|
|
181
|
+
If you need custom styling, you can override the default classes or wrap the components in your own styled containers.
|
|
182
|
+
|
|
183
|
+
## Development
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
# Install dependencies
|
|
187
|
+
npm install
|
|
188
|
+
|
|
189
|
+
# Run dev server
|
|
190
|
+
npm run dev
|
|
191
|
+
|
|
192
|
+
# Build library
|
|
193
|
+
npm run build
|
|
194
|
+
|
|
195
|
+
# Run tests
|
|
196
|
+
npm test
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## License
|
|
200
|
+
|
|
201
|
+
MIT
|
|
202
|
+
|
|
203
|
+
## Contributing
|
|
204
|
+
|
|
205
|
+
Contributions are welcome! Please open an issue or submit a pull request.
|
|
206
|
+
|
|
207
|
+
## Credits
|
|
208
|
+
|
|
209
|
+
Built with:
|
|
210
|
+
- [React](https://reactjs.org/)
|
|
211
|
+
- [abcjs](https://www.abcjs.net/)
|
|
212
|
+
- [Vite](https://vitejs.dev/)
|
|
213
|
+
- [Tailwind CSS](https://tailwindcss.com/)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const u=require("react/jsx-runtime"),b=require("react"),ne=require("abcjs"),L=e=>e.split(`
|
|
2
|
+
`).map((n,r)=>r+1).join(`
|
|
3
|
+
`),N=/^([ABCDFGHIKLMmNOPQRrSsTUVWwXZ]:)(.*)$/,C=/[A-Ga-g]/,O=/[\^_=]/,M=/[',]/,$=/\|[:|\]]?|:?\|/,k=/[\\[\]]/,j=/[()]/,D=/^\(\d+$/,R=/^\/?\d+(\/\d+)?$/,y=/[zZx]/,B=/^\.?-$/,U=/[.~HLMOPSTuv]/,H=/^"[^"]*"$/,P=/^"[\^_<>@][^"]*"$/,K=/^![^!]+!$/,W=/^\{[^}]+\}$/,F=/^\[\d+$/,V=/^\[([ABCDFGHIKLMmNOPQRrSsTUVWwXZ]):([^\]]*)\]$/,G=/^[<>]+$/,Q=/^%.*$/,p=e=>e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'"),re=(e,t)=>{const n=e[t];if(n!=="|"&&n!==":")return null;let r=n,l=t+1;for(;l<e.length&&(e[l]==="|"||e[l]===":"||e[l]==="]");)r+=e[l],l++;return $.test(r)?{html:`<span class="${r==="||"?"abc-bar-double":"abc-bar"}">${p(r)}</span>`,nextIndex:l}:null},le=(e,t)=>{const n=e[t];return O.test(n)?{html:`<span class="abc-accidental">${p(n)}</span>`,nextIndex:t+1}:null},ae=(e,t,n)=>{const r=e[t];if(!j.test(r))return null;if(r==="("){if(t+1<e.length&&/\d/.test(e[t+1])){let l=r,s=t+1;for(;s<e.length&&/\d/.test(e[s]);)l+=e[s],s++;if(D.test(l))return{html:`<span class="abc-tuplet">${p(l)}</span>`,nextIndex:s,slurLevelDelta:0}}return{html:`<span class="abc-slur abc-slur-level-${n%5}">${p(r)}</span>`,nextIndex:t+1,slurLevelDelta:1}}else if(r===")")return{html:`<span class="abc-slur abc-slur-level-${Math.max(0,n-1)%5}">${p(r)}</span>`,nextIndex:t+1,slurLevelDelta:-1};return null},oe=(e,t)=>{const n=e[t];if(!C.test(n))return null;let r=`<span class="abc-note">${p(n)}</span>`,l=t+1,s="";for(;l<e.length&&M.test(e[l]);)s+=e[l],l++;if(s){const c=s[0]==="'"?"abc-octave-high":"abc-octave-low";r+=`<span class="${c}">${p(s)}</span>`}let a="";if(l<e.length&&(e[l]==="/"||/\d/.test(e[l]))){for(e[l]==="/"&&(a+=e[l],l++);l<e.length&&/\d/.test(e[l]);)a+=e[l],l++;if(l<e.length&&e[l]==="/")for(a+=e[l],l++;l<e.length&&/\d/.test(e[l]);)a+=e[l],l++;if(a&&R.test(a)){let c="abc-duration";a.startsWith("/")?c+=" abc-duration-short":a.includes("/")?c+=" abc-duration-fraction":c+=" abc-duration-long",r+=`<span class="${c}">${p(a)}</span>`}}return{html:r,nextIndex:l}},ce=(e,t)=>{const n=e[t];return k.test(n)?{html:`<span class="abc-chord">${p(n)}</span>`,nextIndex:t+1}:null},ie=(e,t)=>{const n=e[t];if(!y.test(n))return null;let l=`<span class="${n==="x"?"abc-rest-invisible":"abc-rest"}">${p(n)}</span>`,s=t+1,a="";if(s<e.length&&(e[s]==="/"||/\d/.test(e[s]))){for(e[s]==="/"&&(a+=e[s],s++);s<e.length&&/\d/.test(e[s]);)a+=e[s],s++;if(s<e.length&&e[s]==="/")for(a+=e[s],s++;s<e.length&&/\d/.test(e[s]);)a+=e[s],s++;if(a&&R.test(a)){let c="abc-duration";a.startsWith("/")?c+=" abc-duration-short":a.includes("/")?c+=" abc-duration-fraction":c+=" abc-duration-long",l+=`<span class="${c}">${p(a)}</span>`}}return{html:l,nextIndex:s}},ue=(e,t)=>{const n=e[t];return n==="."&&t+1<e.length&&e[t+1]==="-"&&B.test(".-")?{html:`<span class="abc-tie abc-tie-dotted">${p(".-")}</span>`,nextIndex:t+2}:n==="-"&&B.test(n)?{html:`<span class="abc-tie">${p(n)}</span>`,nextIndex:t+1}:null},de=(e,t)=>{const n=e[t];return!U.test(n)||n==="."&&t+1<e.length&&e[t+1]==="-"?null:{html:`<span class="abc-ornament">${p(n)}</span>`,nextIndex:t+1}},pe=(e,t)=>{if(e[t]!=='"')return null;let r=t+1;for(;r<e.length&&e[r]!=='"';)r++;if(r>=e.length)return null;const l=e.substring(t,r+1);return P.test(l)?{html:`<span class="abc-annotation">${p(l)}</span>`,nextIndex:r+1}:null},he=(e,t)=>{if(e[t]!=='"')return null;let r=t+1;for(;r<e.length&&e[r]!=='"';)r++;if(r>=e.length)return null;const l=e.substring(t,r+1);return P.test(l)||!H.test(l)?null:{html:`<span class="abc-chord-symbol">${p(l)}</span>`,nextIndex:r+1}},fe=(e,t)=>{if(e[t]!=="!")return null;let r=t+1;for(;r<e.length&&e[r]!=="!";)r++;if(r>=e.length)return null;const l=e.substring(t,r+1);return K.test(l)?{html:`<span class="abc-decoration">${p(l)}</span>`,nextIndex:r+1}:null},me=(e,t)=>{if(e[t]!=="{")return null;let r=t+1;for(;r<e.length&&e[r]!=="}";)r++;if(r>=e.length)return null;const l=e.substring(t,r+1);return W.test(l)?{html:`<span class="abc-grace-note">${p(l)}</span>`,nextIndex:r+1}:null},ge=(e,t)=>{if(e[t]!=="[")return null;let r=t+1;for(;r<e.length&&e[r]!=="]";)r++;if(r>=e.length)return null;const s=e.substring(t,r+1).match(V);if(!s)return null;const[,a,c]=s;return{html:`<span class="abc-inline-field-bracket">[</span><span class="abc-inline-field-key">${p(a)}:</span><span class="abc-inline-field-value">${p(c)}</span><span class="abc-inline-field-bracket">]</span>`,nextIndex:r+1}},be=(e,t)=>{if(e[t]!=="["||t+1>=e.length||!/\d/.test(e[t+1]))return null;let r=t+1;for(;r<e.length&&/\d/.test(e[r]);)r++;const l=e.substring(t,r);return F.test(l)?{html:`<span class="abc-volta-bracket">${p(l)}</span>`,nextIndex:r}:null},Ae=(e,t)=>{const n=e[t];if(n!==">"&&n!=="<")return null;let r=t+1;for(;r<e.length&&e[r]===n;)r++;const l=e.substring(t,r);return G.test(l)?{html:`<span class="abc-broken-rhythm">${p(l)}</span>`,nextIndex:r}:null},ve=e=>{let t="",n=0,r=0;for(;n<e.length;){const l=re(e,n);if(l){t+=l.html,n=l.nextIndex;continue}const s=fe(e,n);if(s){t+=s.html,n=s.nextIndex;continue}const a=me(e,n);if(a){t+=a.html,n=a.nextIndex;continue}const c=le(e,n);if(c){t+=c.html,n=c.nextIndex;continue}const v=de(e,n);if(v){t+=v.html,n=v.nextIndex;continue}const f=pe(e,n);if(f){t+=f.html,n=f.nextIndex;continue}const i=he(e,n);if(i){t+=i.html,n=i.nextIndex;continue}const o=ae(e,n,r);if(o){t+=o.html,n=o.nextIndex,o.slurLevelDelta&&(r+=o.slurLevelDelta);continue}const d=ie(e,n);if(d){t+=d.html,n=d.nextIndex;continue}const h=oe(e,n);if(h){t+=h.html,n=h.nextIndex;continue}const T=Ae(e,n);if(T){t+=T.html,n=T.nextIndex;continue}const m=ge(e,n);if(m){t+=m.html,n=m.nextIndex;continue}const g=be(e,n);if(g){t+=g.html,n=g.nextIndex;continue}const x=ce(e,n);if(x){t+=x.html,n=x.nextIndex;continue}const A=ue(e,n);if(A){t+=A.html,n=A.nextIndex;continue}t+=`<span class="abc-text">${p(e[n])}</span>`,n++}return t},I=e=>e.split(`
|
|
4
|
+
`).map(n=>{if(Q.test(n))return`<span class="abc-comment">${p(n)}</span>`;const r=n.match(N);if(r){const[,l,s]=r;return l==="w:"||l==="W:"?`<span class="abc-lyrics-key">${p(l)}</span><span class="abc-lyrics-value">${p(s)}</span>`:`<span class="abc-meta-key">${p(l)}</span><span class="abc-meta-value">${p(s)}</span>`}return ve(n)}).join(`
|
|
5
|
+
`),z={"X:":[{value:"1",description:"Reference number 1"},{value:"2",description:"Reference number 2"}],"T:":[{value:"タイトル",description:"Japanese title template"},{value:"Untitled",description:"Default title"}],"M:":[{value:"4/4",description:"Common time"},{value:"3/4",description:"Waltz time"},{value:"2/4",description:"March time"},{value:"6/8",description:"Compound duple"},{value:"9/8",description:"Compound triple"},{value:"12/8",description:"Compound quadruple"},{value:"5/4",description:"Quintuple meter"},{value:"7/8",description:"Septuple meter"},{value:"C",description:"Common time (4/4)"},{value:"C|",description:"Cut time (2/2)"}],"L:":[{value:"1/4",description:"Quarter note"},{value:"1/8",description:"Eighth note"},{value:"1/16",description:"Sixteenth note"}],"Q:":[{value:"1/4=120",description:"Quarter note = 120 BPM"},{value:"1/4=100",description:"Quarter note = 100 BPM"},{value:"1/4=80",description:"Quarter note = 80 BPM"},{value:"1/8=180",description:"Eighth note = 180 BPM"}],"K:":[{value:"C"},{value:"G"},{value:"D"},{value:"A"},{value:"E"},{value:"B"},{value:"F#"},{value:"C#"},{value:"F"},{value:"Bb"},{value:"Eb"},{value:"Ab"},{value:"Db"},{value:"Gb"},{value:"Cb"},{value:"Am"},{value:"Em"},{value:"Bm"},{value:"F#m"},{value:"C#m"},{value:"G#m"},{value:"D#m"},{value:"Dm"},{value:"Gm"},{value:"Cm"},{value:"Fm"},{value:"Bbm"},{value:"Ebm"},{value:"Abm"}],"C:":[{value:"Traditional",description:"Traditional composer"},{value:"Unknown",description:"Unknown composer"}],"R:":[{value:"reel",description:"Reel rhythm"},{value:"jig",description:"Jig rhythm"},{value:"hornpipe",description:"Hornpipe rhythm"},{value:"waltz",description:"Waltz rhythm"},{value:"march",description:"March rhythm"}],"A:":[{value:"Ireland",description:"Area: Ireland"},{value:"Scotland",description:"Area: Scotland"},{value:"England",description:"Area: England"},{value:"USA",description:"Area: USA"},{value:"Japan",description:"Area: Japan"}],"B:":[{value:"The Session",description:"Book: The Session"},{value:"O'Neill's Music of Ireland",description:"Book: O'Neill's Music of Ireland"}],"D:":[{value:"Album Name (Year)",description:"Discography template"}],"F:":[{value:"https://thesession.org/",description:"File URL template"}],"G:":[{value:"Group name",description:"Group template"}],"H:":[{value:"History note",description:"History template"}],"I:":[{value:"abc-charset utf-8",description:"Character set: UTF-8"}],"m:":[{value:"~g2 = {a}g{f}g",description:"Macro definition template"}],"N:":[{value:"Note text",description:"Notes template"}],"O:":[{value:"Traditional",description:"Origin: Traditional"},{value:"Ireland",description:"Origin: Ireland"},{value:"Scotland",description:"Origin: Scotland"}],"P:":[{value:"AABB",description:"Parts: AABB"},{value:"ABCD",description:"Parts: ABCD"},{value:"A",description:"Part: A"},{value:"B",description:"Part: B"}],"r:":[{value:"Remark text",description:"Remark template"}],"S:":[{value:"Session tune",description:"Source: Session"},{value:"Traditional",description:"Source: Traditional"}],"s:":[{value:"Symbol line",description:"Symbol line template"}],"U:":[{value:"u = !trill!",description:"User defined: trill"}],"V:":[{value:"1",description:"Voice 1"},{value:"2",description:"Voice 2"},{value:"T",description:"Tenor voice"},{value:"B",description:"Bass voice"}],"W:":[{value:"Lyrics line",description:"Words (multi-line)"}],"w:":[{value:"Lyrics for this line",description:"Words (aligned)"}],"Z:":[{value:"Your Name",description:"Transcriber name"},{value:"ABC transcription",description:"Transcription note"}]},Te=e=>z[e]||[],Y=[{value:"/header",description:"Insert basic ABC header template",template:`X:1
|
|
6
|
+
T:Untitled
|
|
7
|
+
M:4/4
|
|
8
|
+
L:1/4
|
|
9
|
+
K:C
|
|
10
|
+
`}],xe=e=>e.startsWith("/")?Y.filter(t=>t.value.toLowerCase().startsWith(e.toLowerCase())):[],q=({value:e,textareaRef:t,onChange:n})=>{const[r,l]=b.useState({isOpen:!1,suggestions:[],selectedIndex:0,position:{top:0,left:0},fieldKey:null,inputValue:"",isCommand:!1}),s=b.useCallback(()=>{if(!t.current)return null;const i=t.current.selectionStart,o=e.substring(0,i).split(`
|
|
11
|
+
`),d=o[o.length-1];if(d.startsWith("/"))return{fieldKey:null,inputValue:d,line:d,lineStartPos:i-d.length,isCommand:!0};const h=d.match(N);if(h){const[,T,m]=h;return{fieldKey:T,inputValue:m,line:d,lineStartPos:i-d.length,isCommand:!1}}return null},[e,t]),a=b.useCallback(()=>{if(!t.current)return{top:0,left:0};const i=t.current,o=i.selectionStart,h=e.substring(0,o).split(`
|
|
12
|
+
`).length-1,T=parseFloat(getComputedStyle(i).lineHeight)||24;return{top:(parseFloat(getComputedStyle(i).paddingTop)||16)+(h+1)*T-i.scrollTop,left:60}},[e,t]);b.useEffect(()=>{const i=s();if(!i){l(d=>({...d,isOpen:!1}));return}let o=[];if(i.isCommand?o=xe(i.inputValue):i.fieldKey&&(o=Te(i.fieldKey).filter(h=>h.value.toLowerCase().startsWith(i.inputValue.toLowerCase()))),o.length>0){const d=a();l({isOpen:!0,suggestions:o,selectedIndex:0,position:d,fieldKey:i.fieldKey,inputValue:i.inputValue,isCommand:i.isCommand})}else l(d=>({...d,isOpen:!1}))},[e,s,a]);const c=b.useCallback(i=>{if(!t.current)return;const o=t.current.selectionStart,d=e.substring(0,o).split(`
|
|
13
|
+
`),h=d[d.length-1];let T,m;if(r.isCommand&&i.template){const g=e.substring(0,o-h.length),x=e.substring(o);T=g+i.template+x,m=g.length+i.template.length}else if(r.fieldKey){const g=r.fieldKey+i.value,x=e.substring(0,o-h.length),A=e.substring(o);T=x+g+A,m=x.length+g.length}else return;n(T),setTimeout(()=>{t.current&&(t.current.selectionStart=m,t.current.selectionEnd=m,t.current.focus())},0),l(g=>({...g,isOpen:!1}))},[t,r.fieldKey,r.isCommand,e,n]),v=b.useCallback(i=>{if(r.isOpen)switch(i.key){case"ArrowDown":i.preventDefault(),l(o=>({...o,selectedIndex:Math.min(o.selectedIndex+1,o.suggestions.length-1)}));break;case"ArrowUp":i.preventDefault(),l(o=>({...o,selectedIndex:Math.max(o.selectedIndex-1,0)}));break;case"Enter":r.suggestions.length>0&&(i.preventDefault(),c(r.suggestions[r.selectedIndex]));break;case"Escape":i.preventDefault(),l(o=>({...o,isOpen:!1}));break}},[r.isOpen,r.suggestions,r.selectedIndex,c]),f=b.useCallback(i=>{l(o=>({...o,selectedIndex:i}))},[]);return{isOpen:r.isOpen,suggestions:r.suggestions,selectedIndex:r.selectedIndex,position:r.position,handleKeyDown:v,selectSuggestion:c,handleMouseEnter:f}},J=({suggestions:e,selectedIndex:t,position:n,onSelect:r,onMouseEnter:l})=>{const s=b.useRef(null);return b.useEffect(()=>{s.current?.scrollIntoView({block:"nearest",behavior:"smooth"})},[t]),e.length===0?null:u.jsx("div",{className:"absolute z-50 bg-slate-800 border border-slate-600 rounded-md shadow-lg overflow-hidden",style:{top:`${n.top}px`,left:`${n.left}px`,minWidth:"200px",maxWidth:"400px",maxHeight:"300px",overflowY:"auto"},children:e.map((a,c)=>u.jsxs("div",{ref:c===t?s:null,className:`px-3 py-2 cursor-pointer ${c===t?"bg-blue-600 text-white":"text-slate-200 hover:bg-slate-700"}`,onClick:()=>r(a),onMouseEnter:()=>l(c),children:[u.jsx("div",{className:"font-mono text-sm",children:a.value}),a.description&&u.jsx("div",{className:"text-xs opacity-75 mt-1",children:a.description})]},`${a.value}-${c}`))})},Ne=e=>{const t=e.trim();if(t==="C")return{beatsPerMeasure:4,beatUnit:4};if(t==="C|")return{beatsPerMeasure:2,beatUnit:2};const n=t.match(/^(\d+)\/(\d+)$/);return n?{beatsPerMeasure:parseInt(n[1],10),beatUnit:parseInt(n[2],10)}:{beatsPerMeasure:4,beatUnit:4}},Ce=e=>{const n=e.trim().match(/^(\d+)\/(\d+)$/);return n?parseInt(n[1],10)/parseInt(n[2],10):1/8},Ee=e=>{const t=e.split(`
|
|
14
|
+
`);let n={beatsPerMeasure:4,beatUnit:4},r=1/8;for(const l of t){const s=l.match(N);if(s){const[,a,c]=s;a==="M:"?n=Ne(c):a==="L:"&&(r=Ce(c))}}return{meter:n,unitNoteLength:r}},S=e=>{if(!e)return 1;if(e.startsWith("/"))return 1/parseInt(e.substring(1),10);if(e.includes("/")){const[t,n]=e.split("/");return parseInt(t,10)/parseInt(n,10)}return parseInt(e,10)},_e=(e,t)=>{let n=0,r=0;for(;r<e.length;){const l=e[r];if(C.test(l)||y.test(l)){let s=r+1;for(;s<e.length&&(e[s]==="'"||e[s]===",");)s++;let a="";if(s<e.length&&(e[s]==="/"||/\d/.test(e[s]))){const o=s;for(e[s]==="/"&&s++;s<e.length&&/\d/.test(e[s]);)s++;if(s<e.length&&e[s]==="/")for(s++;s<e.length&&/\d/.test(e[s]);)s++;a=e.substring(o,s)}const c=S(a),v=t.unitNoteLength*c,f=1/t.meter.beatUnit,i=v/f;n+=i,r=s}else if(l==="["){let s=r+1;for(;s<e.length&&e[s]!=="]";){const a=e[s];if(C.test(a)){let c=s+1;for(;c<e.length&&(e[c]==="'"||e[c]===",");)c++;s=c}else s++}if(s<e.length&&e[s]==="]"){s++;let a="";if(s<e.length&&(e[s]==="/"||/\d/.test(e[s]))){const o=s;for(e[s]==="/"&&s++;s<e.length&&/\d/.test(e[s]);)s++;if(s<e.length&&e[s]==="/")for(s++;s<e.length&&/\d/.test(e[s]);)s++;a=e.substring(o,s)}const c=S(a),v=t.unitNoteLength*c,f=1/t.meter.beatUnit,i=v/f;n+=i}r=s}else r++}return n},X=e=>{const t=[],n=e.split(`
|
|
15
|
+
`),r=Ee(e);return n.forEach((l,s)=>{if(!(l.trim().startsWith("%")||N.test(l))&&l.includes("|")){let a=0;const c=l.split("|");let v=0;c.forEach((f,i)=>{if(i===c.length-1&&f.trim()==="")return;const o=f.trim();if(o.startsWith(":")||o.startsWith("[")||o===""){a+=f.length+1;return}const d=_e(o,r),h=r.meter.beatsPerMeasure;if(Math.abs(d-h)>.01){const m=l.indexOf(o,a),g=m+o.length;t.push({line:s,measureIndex:v,startCol:m,endCol:g,expected:h,actual:d,message:`Expected ${h} beats, got ${d.toFixed(2)}`})}v++,a+=f.length+1})}}),t},Be=({value:e,onChange:t})=>{const n=b.useRef(null),r=b.useRef(null),l=b.useRef(null),[s,a]=b.useState(null),c=L(e),v=I(e),f=b.useMemo(()=>X(e),[e]),{isOpen:i,suggestions:o,selectedIndex:d,position:h,handleKeyDown:T,selectSuggestion:m,handleMouseEnter:g}=q({value:e,textareaRef:n,onChange:t}),x=()=>{n.current&&r.current&&l.current&&(r.current.scrollTop=n.current.scrollTop,l.current.scrollTop=n.current.scrollTop)};return u.jsx("div",{className:"w-full md:w-1/2 h-2/3 md:h-full flex flex-col p-4",style:{backgroundColor:"#161616"},children:u.jsxs("div",{className:"w-full flex-1 flex flex-col rounded-lg border border-slate-600 overflow-hidden",style:{backgroundColor:"#1a1a1a"},children:[u.jsxs("div",{className:"flex-1 flex overflow-hidden",children:[u.jsx("div",{ref:r,className:"overflow-hidden text-right text-sm font-mono leading-relaxed text-slate-500 select-none",style:{backgroundColor:"#0f0f0f",minWidth:"2.5rem"},children:u.jsx("pre",{className:"m-0 pl-1 pr-3 py-4",children:c})}),u.jsxs("div",{className:"flex-1 relative",children:[u.jsx("div",{ref:l,className:"absolute inset-0 overflow-hidden px-4 py-4 text-sm font-mono leading-relaxed pointer-events-none",style:{backgroundColor:"#1a1a1a",opacity:s?.3:1,transition:"opacity 0.2s"},children:u.jsx("pre",{className:"m-0",dangerouslySetInnerHTML:{__html:v}})}),s&&(()=>{const A=e.split(`
|
|
16
|
+
`),Z=(A[s.line]||"").substring(s.startCol,s.endCol),ee=I(Z);return u.jsx("div",{className:"absolute inset-0 overflow-hidden px-4 py-4 text-sm font-mono leading-relaxed pointer-events-none",style:{backgroundColor:"transparent"},children:u.jsx("pre",{className:"m-0",children:A.map((E,_)=>{if(_===s.line){const te=E.substring(0,s.startCol),se=E.substring(s.endCol);return u.jsxs("div",{children:[u.jsx("span",{style:{opacity:0},children:te}),u.jsx("span",{className:"bg-amber-500/20 px-1 rounded",dangerouslySetInnerHTML:{__html:ee}}),u.jsx("span",{style:{opacity:0},children:se})]},_)}return u.jsx("div",{style:{opacity:0},children:E},_)})})})})(),u.jsx("textarea",{ref:n,className:"absolute inset-0 w-full h-full resize-none px-4 py-4 text-sm font-mono leading-relaxed outline-none border-0",style:{backgroundColor:"transparent",color:"transparent",caretColor:"#fff"},value:e,onChange:A=>t(A.target.value),onScroll:x,onKeyDown:T,spellCheck:!1,placeholder:"Type /header to insert template..."}),i&&u.jsx(J,{suggestions:o,selectedIndex:d,position:h,onSelect:m,onMouseEnter:g})]})]}),f.length>0&&u.jsxs("div",{className:"border-t border-slate-600 px-4 py-3 text-xs font-mono overflow-auto",style:{backgroundColor:"#0f0f0f",maxHeight:"8rem"},children:[u.jsxs("div",{className:"text-slate-500 mb-2 text-[10px] uppercase tracking-wide",children:["Validation Errors (",f.length,")"]}),f.map((A,w)=>u.jsxs("div",{className:"flex items-start gap-3 mb-2 last:mb-0 hover:bg-slate-800/30 px-2 py-1 rounded transition-colors cursor-pointer",onMouseEnter:()=>a(A),onMouseLeave:()=>a(null),children:[u.jsx("span",{className:"text-amber-500 shrink-0 mt-0.5",children:"⚠️"}),u.jsxs("div",{className:"flex-1 flex gap-2",children:[u.jsxs("span",{className:"text-cyan-400 shrink-0",children:["Ln ",A.line+1,", M",A.measureIndex+1,":"]}),u.jsx("span",{className:"text-amber-300",children:A.message})]})]},w))]})]})})},Ie=({abc:e})=>{const t=b.useRef(null);return b.useEffect(()=>{t.current&&ne.renderAbc(t.current,e,{responsive:"resize",foregroundColor:"#ffffff",format:{titlefont:"serif 20"}})},[e]),u.jsx("div",{ref:t,className:"w-full abc-preview"})};exports.ABC_ACCIDENTAL_PATTERN=O;exports.ABC_ANNOTATION_PATTERN=P;exports.ABC_BAR_PATTERN=$;exports.ABC_BROKEN_RHYTHM_PATTERN=G;exports.ABC_CHORD_BRACKET_PATTERN=k;exports.ABC_CHORD_SYMBOL_PATTERN=H;exports.ABC_COMMANDS=Y;exports.ABC_COMMENT_PATTERN=Q;exports.ABC_DECORATION_PATTERN=K;exports.ABC_DURATION_PATTERN=R;exports.ABC_FIELD_PATTERN=N;exports.ABC_GRACE_NOTE_PATTERN=W;exports.ABC_INLINE_FIELD_PATTERN=V;exports.ABC_NOTE_PATTERN=C;exports.ABC_OCTAVE_PATTERN=M;exports.ABC_ORNAMENT_PATTERN=U;exports.ABC_REST_PATTERN=y;exports.ABC_SLUR_PATTERN=j;exports.ABC_SUGGESTIONS=z;exports.ABC_TIE_PATTERN=B;exports.ABC_TUPLET_PATTERN=D;exports.ABC_VOLTA_BRACKET_PATTERN=F;exports.AbcEditor=Be;exports.AbcPreview=Ie;exports.SuggestionList=J;exports.highlightAbc=I;exports.useAbcAutoComplete=q;exports.useLineNumbers=L;exports.validateAbc=X;
|