@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 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,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#039;"),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;