@gui-chat-plugin/music 0.0.1
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 +90 -0
- package/dist/core/definition.d.ts +20 -0
- package/dist/core/index.d.ts +4 -0
- package/dist/core/plugin.d.ts +6 -0
- package/dist/core/samples.d.ts +2 -0
- package/dist/core/types.d.ts +8 -0
- package/dist/core.cjs +94 -0
- package/dist/core.js +143 -0
- package/dist/index.cjs +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +7 -0
- package/dist/style.css +1 -0
- package/dist/vue/Preview.vue.d.ts +8 -0
- package/dist/vue/View.vue.d.ts +9 -0
- package/dist/vue/index.d.ts +11 -0
- package/dist/vue.cjs +1 -0
- package/dist/vue.js +126 -0
- package/package.json +59 -0
package/README.md
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# @gui-chat-plugin/music
|
|
2
|
+
|
|
3
|
+
MusicXML sheet music plugin for GUI Chat applications. Displays and plays sheet music from MusicXML format.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Display sheet music from MusicXML format
|
|
8
|
+
- Audio playback with OpenSheetMusicDisplay and osmd-audio-player
|
|
9
|
+
- Tempo control
|
|
10
|
+
- Cursor following during playback
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
yarn add @gui-chat-plugin/music
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Usage
|
|
19
|
+
|
|
20
|
+
### Vue Integration
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
// In src/tools/index.ts
|
|
24
|
+
import MusicPlugin from "@gui-chat-plugin/music/vue";
|
|
25
|
+
|
|
26
|
+
const pluginList = [
|
|
27
|
+
// ... other plugins
|
|
28
|
+
MusicPlugin,
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
// In src/main.ts
|
|
32
|
+
import "@gui-chat-plugin/music/style.css";
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Core-only Usage
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
import { executeMusic, TOOL_DEFINITION } from "@gui-chat-plugin/music";
|
|
39
|
+
|
|
40
|
+
// Show sheet music
|
|
41
|
+
const result = await executeMusic(context, {
|
|
42
|
+
musicXML: "<musicxml-content>",
|
|
43
|
+
title: "My Song",
|
|
44
|
+
});
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## API
|
|
48
|
+
|
|
49
|
+
### MusicArgs
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
interface MusicArgs {
|
|
53
|
+
musicXML: string; // The music in MusicXML format
|
|
54
|
+
title?: string; // Optional title for the music piece
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### MusicToolData
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
interface MusicToolData {
|
|
62
|
+
musicXML: string;
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Dependencies
|
|
67
|
+
|
|
68
|
+
This plugin requires:
|
|
69
|
+
- `opensheetmusicdisplay`: For rendering MusicXML as sheet music
|
|
70
|
+
- `osmd-audio-player`: For audio playback
|
|
71
|
+
|
|
72
|
+
## Development
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
# Install dependencies
|
|
76
|
+
yarn install
|
|
77
|
+
|
|
78
|
+
# Run demo
|
|
79
|
+
yarn dev
|
|
80
|
+
|
|
81
|
+
# Build
|
|
82
|
+
yarn build
|
|
83
|
+
|
|
84
|
+
# Lint
|
|
85
|
+
yarn lint
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## License
|
|
89
|
+
|
|
90
|
+
MIT
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export declare const TOOL_NAME = "showMusic";
|
|
2
|
+
export declare const TOOL_DEFINITION: {
|
|
3
|
+
type: "function";
|
|
4
|
+
name: string;
|
|
5
|
+
description: string;
|
|
6
|
+
parameters: {
|
|
7
|
+
type: "object";
|
|
8
|
+
properties: {
|
|
9
|
+
musicXML: {
|
|
10
|
+
type: string;
|
|
11
|
+
description: string;
|
|
12
|
+
};
|
|
13
|
+
title: {
|
|
14
|
+
type: string;
|
|
15
|
+
description: string;
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
required: string[];
|
|
19
|
+
};
|
|
20
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ToolContext, ToolResult } from "gui-chat-protocol/vue";
|
|
2
|
+
import type { MusicToolData, MusicArgs } from "./types";
|
|
3
|
+
import { TOOL_DEFINITION } from "./definition";
|
|
4
|
+
export type MusicResult = ToolResult<MusicToolData>;
|
|
5
|
+
export declare const showMusic: (_context: ToolContext, args: MusicArgs) => Promise<MusicResult>;
|
|
6
|
+
export { TOOL_DEFINITION };
|
package/dist/core.cjs
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e="showMusic",s={type:"function",name:e,description:"Display sheet music from MusicXML format.",parameters:{type:"object",properties:{musicXML:{type:"string",description:"The music in MusicXML format"},title:{type:"string",description:"Optional title for the music piece"}},required:["musicXML"]}},o=async(c,i)=>{try{const{musicXML:t,title:r}=i;if(!t||typeof t!="string")throw new Error("musicXML parameter is required and must be a string");return{message:"Sheet music displayed",title:r||"Sheet Music",data:{musicXML:t},instructions:"Acknowledge that the sheet music has been displayed to the user."}}catch(t){return console.error(`ERR: exception
|
|
2
|
+
Music rendering failed`,t),{message:`Music rendering failed: ${t instanceof Error?t.message:"Unknown error"}`,instructions:"Acknowledge that the music rendering failed."}}},a=`<?xml version="1.0" encoding="UTF-8"?>
|
|
3
|
+
<!DOCTYPE score-partwise PUBLIC "-//Recordare//DTD MusicXML 3.1 Partwise//EN" "http://www.musicxml.org/dtds/partwise.dtd">
|
|
4
|
+
<score-partwise version="3.1">
|
|
5
|
+
<part-list>
|
|
6
|
+
<score-part id="P1">
|
|
7
|
+
<part-name>Piano</part-name>
|
|
8
|
+
</score-part>
|
|
9
|
+
</part-list>
|
|
10
|
+
<part id="P1">
|
|
11
|
+
<measure number="1">
|
|
12
|
+
<attributes>
|
|
13
|
+
<divisions>1</divisions>
|
|
14
|
+
<key>
|
|
15
|
+
<fifths>0</fifths>
|
|
16
|
+
</key>
|
|
17
|
+
<time>
|
|
18
|
+
<beats>4</beats>
|
|
19
|
+
<beat-type>4</beat-type>
|
|
20
|
+
</time>
|
|
21
|
+
<clef>
|
|
22
|
+
<sign>G</sign>
|
|
23
|
+
<line>2</line>
|
|
24
|
+
</clef>
|
|
25
|
+
</attributes>
|
|
26
|
+
<note>
|
|
27
|
+
<pitch>
|
|
28
|
+
<step>C</step>
|
|
29
|
+
<octave>4</octave>
|
|
30
|
+
</pitch>
|
|
31
|
+
<duration>1</duration>
|
|
32
|
+
<type>quarter</type>
|
|
33
|
+
</note>
|
|
34
|
+
<note>
|
|
35
|
+
<pitch>
|
|
36
|
+
<step>D</step>
|
|
37
|
+
<octave>4</octave>
|
|
38
|
+
</pitch>
|
|
39
|
+
<duration>1</duration>
|
|
40
|
+
<type>quarter</type>
|
|
41
|
+
</note>
|
|
42
|
+
<note>
|
|
43
|
+
<pitch>
|
|
44
|
+
<step>E</step>
|
|
45
|
+
<octave>4</octave>
|
|
46
|
+
</pitch>
|
|
47
|
+
<duration>1</duration>
|
|
48
|
+
<type>quarter</type>
|
|
49
|
+
</note>
|
|
50
|
+
<note>
|
|
51
|
+
<pitch>
|
|
52
|
+
<step>F</step>
|
|
53
|
+
<octave>4</octave>
|
|
54
|
+
</pitch>
|
|
55
|
+
<duration>1</duration>
|
|
56
|
+
<type>quarter</type>
|
|
57
|
+
</note>
|
|
58
|
+
</measure>
|
|
59
|
+
<measure number="2">
|
|
60
|
+
<note>
|
|
61
|
+
<pitch>
|
|
62
|
+
<step>G</step>
|
|
63
|
+
<octave>4</octave>
|
|
64
|
+
</pitch>
|
|
65
|
+
<duration>1</duration>
|
|
66
|
+
<type>quarter</type>
|
|
67
|
+
</note>
|
|
68
|
+
<note>
|
|
69
|
+
<pitch>
|
|
70
|
+
<step>A</step>
|
|
71
|
+
<octave>4</octave>
|
|
72
|
+
</pitch>
|
|
73
|
+
<duration>1</duration>
|
|
74
|
+
<type>quarter</type>
|
|
75
|
+
</note>
|
|
76
|
+
<note>
|
|
77
|
+
<pitch>
|
|
78
|
+
<step>B</step>
|
|
79
|
+
<octave>4</octave>
|
|
80
|
+
</pitch>
|
|
81
|
+
<duration>1</duration>
|
|
82
|
+
<type>quarter</type>
|
|
83
|
+
</note>
|
|
84
|
+
<note>
|
|
85
|
+
<pitch>
|
|
86
|
+
<step>C</step>
|
|
87
|
+
<octave>5</octave>
|
|
88
|
+
</pitch>
|
|
89
|
+
<duration>1</duration>
|
|
90
|
+
<type>quarter</type>
|
|
91
|
+
</note>
|
|
92
|
+
</measure>
|
|
93
|
+
</part>
|
|
94
|
+
</score-partwise>`,n=[{name:"C Major Scale",args:{musicXML:a,title:"C Major Scale"}}];exports.TOOL_DEFINITION=s;exports.TOOL_NAME=e;exports.samples=n;exports.showMusic=o;
|
package/dist/core.js
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
const r = "showMusic", o = {
|
|
2
|
+
type: "function",
|
|
3
|
+
name: r,
|
|
4
|
+
description: "Display sheet music from MusicXML format.",
|
|
5
|
+
parameters: {
|
|
6
|
+
type: "object",
|
|
7
|
+
properties: {
|
|
8
|
+
musicXML: {
|
|
9
|
+
type: "string",
|
|
10
|
+
description: "The music in MusicXML format"
|
|
11
|
+
},
|
|
12
|
+
title: {
|
|
13
|
+
type: "string",
|
|
14
|
+
description: "Optional title for the music piece"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
required: ["musicXML"]
|
|
18
|
+
}
|
|
19
|
+
}, n = async (a, e) => {
|
|
20
|
+
try {
|
|
21
|
+
const { musicXML: t, title: i } = e;
|
|
22
|
+
if (!t || typeof t != "string")
|
|
23
|
+
throw new Error("musicXML parameter is required and must be a string");
|
|
24
|
+
return {
|
|
25
|
+
message: "Sheet music displayed",
|
|
26
|
+
title: i || "Sheet Music",
|
|
27
|
+
data: { musicXML: t },
|
|
28
|
+
instructions: "Acknowledge that the sheet music has been displayed to the user."
|
|
29
|
+
};
|
|
30
|
+
} catch (t) {
|
|
31
|
+
return console.error(`ERR: exception
|
|
32
|
+
Music rendering failed`, t), {
|
|
33
|
+
message: `Music rendering failed: ${t instanceof Error ? t.message : "Unknown error"}`,
|
|
34
|
+
instructions: "Acknowledge that the music rendering failed."
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
}, s = `<?xml version="1.0" encoding="UTF-8"?>
|
|
38
|
+
<!DOCTYPE score-partwise PUBLIC "-//Recordare//DTD MusicXML 3.1 Partwise//EN" "http://www.musicxml.org/dtds/partwise.dtd">
|
|
39
|
+
<score-partwise version="3.1">
|
|
40
|
+
<part-list>
|
|
41
|
+
<score-part id="P1">
|
|
42
|
+
<part-name>Piano</part-name>
|
|
43
|
+
</score-part>
|
|
44
|
+
</part-list>
|
|
45
|
+
<part id="P1">
|
|
46
|
+
<measure number="1">
|
|
47
|
+
<attributes>
|
|
48
|
+
<divisions>1</divisions>
|
|
49
|
+
<key>
|
|
50
|
+
<fifths>0</fifths>
|
|
51
|
+
</key>
|
|
52
|
+
<time>
|
|
53
|
+
<beats>4</beats>
|
|
54
|
+
<beat-type>4</beat-type>
|
|
55
|
+
</time>
|
|
56
|
+
<clef>
|
|
57
|
+
<sign>G</sign>
|
|
58
|
+
<line>2</line>
|
|
59
|
+
</clef>
|
|
60
|
+
</attributes>
|
|
61
|
+
<note>
|
|
62
|
+
<pitch>
|
|
63
|
+
<step>C</step>
|
|
64
|
+
<octave>4</octave>
|
|
65
|
+
</pitch>
|
|
66
|
+
<duration>1</duration>
|
|
67
|
+
<type>quarter</type>
|
|
68
|
+
</note>
|
|
69
|
+
<note>
|
|
70
|
+
<pitch>
|
|
71
|
+
<step>D</step>
|
|
72
|
+
<octave>4</octave>
|
|
73
|
+
</pitch>
|
|
74
|
+
<duration>1</duration>
|
|
75
|
+
<type>quarter</type>
|
|
76
|
+
</note>
|
|
77
|
+
<note>
|
|
78
|
+
<pitch>
|
|
79
|
+
<step>E</step>
|
|
80
|
+
<octave>4</octave>
|
|
81
|
+
</pitch>
|
|
82
|
+
<duration>1</duration>
|
|
83
|
+
<type>quarter</type>
|
|
84
|
+
</note>
|
|
85
|
+
<note>
|
|
86
|
+
<pitch>
|
|
87
|
+
<step>F</step>
|
|
88
|
+
<octave>4</octave>
|
|
89
|
+
</pitch>
|
|
90
|
+
<duration>1</duration>
|
|
91
|
+
<type>quarter</type>
|
|
92
|
+
</note>
|
|
93
|
+
</measure>
|
|
94
|
+
<measure number="2">
|
|
95
|
+
<note>
|
|
96
|
+
<pitch>
|
|
97
|
+
<step>G</step>
|
|
98
|
+
<octave>4</octave>
|
|
99
|
+
</pitch>
|
|
100
|
+
<duration>1</duration>
|
|
101
|
+
<type>quarter</type>
|
|
102
|
+
</note>
|
|
103
|
+
<note>
|
|
104
|
+
<pitch>
|
|
105
|
+
<step>A</step>
|
|
106
|
+
<octave>4</octave>
|
|
107
|
+
</pitch>
|
|
108
|
+
<duration>1</duration>
|
|
109
|
+
<type>quarter</type>
|
|
110
|
+
</note>
|
|
111
|
+
<note>
|
|
112
|
+
<pitch>
|
|
113
|
+
<step>B</step>
|
|
114
|
+
<octave>4</octave>
|
|
115
|
+
</pitch>
|
|
116
|
+
<duration>1</duration>
|
|
117
|
+
<type>quarter</type>
|
|
118
|
+
</note>
|
|
119
|
+
<note>
|
|
120
|
+
<pitch>
|
|
121
|
+
<step>C</step>
|
|
122
|
+
<octave>5</octave>
|
|
123
|
+
</pitch>
|
|
124
|
+
<duration>1</duration>
|
|
125
|
+
<type>quarter</type>
|
|
126
|
+
</note>
|
|
127
|
+
</measure>
|
|
128
|
+
</part>
|
|
129
|
+
</score-partwise>`, c = [
|
|
130
|
+
{
|
|
131
|
+
name: "C Major Scale",
|
|
132
|
+
args: {
|
|
133
|
+
musicXML: s,
|
|
134
|
+
title: "C Major Scale"
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
];
|
|
138
|
+
export {
|
|
139
|
+
o as TOOL_DEFINITION,
|
|
140
|
+
r as TOOL_NAME,
|
|
141
|
+
c as samples,
|
|
142
|
+
n as showMusic
|
|
143
|
+
};
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./core.cjs");exports.TOOL_DEFINITION=e.TOOL_DEFINITION;exports.TOOL_NAME=e.TOOL_NAME;exports.samples=e.samples;exports.showMusic=e.showMusic;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./core";
|
package/dist/index.js
ADDED
package/dist/style.css
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style:solid;--tw-font-weight:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000}}}@layer theme{:root,:host{--font-sans:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--color-red-500:oklch(63.7% .237 25.331);--color-blue-500:oklch(62.3% .214 259.815);--color-indigo-100:oklch(93% .034 272.788);--color-indigo-200:oklch(87% .065 274.039);--color-indigo-700:oklch(45.7% .24 277.023);--color-purple-50:oklch(97.7% .014 308.299);--color-purple-600:oklch(55.8% .288 302.321);--color-gray-100:oklch(96.7% .003 264.542);--color-gray-200:oklch(92.8% .006 264.531);--color-gray-300:oklch(87.2% .01 258.338);--color-gray-600:oklch(44.6% .03 256.802);--color-gray-700:oklch(37.3% .034 259.733);--color-gray-800:oklch(27.8% .033 256.848);--color-gray-900:oklch(21% .034 264.665);--color-white:#fff;--spacing:.25rem;--container-3xl:48rem;--text-xs:.75rem;--text-xs--line-height:calc(1/.75);--text-sm:.875rem;--text-sm--line-height:calc(1.25/.875);--text-xl:1.25rem;--text-xl--line-height:calc(1.75/1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2/1.5);--font-weight-medium:500;--font-weight-bold:700;--radius-md:.375rem;--radius-lg:.5rem;--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.mx-auto{margin-inline:auto}.mt-1{margin-top:calc(var(--spacing)*1)}.mb-4{margin-bottom:calc(var(--spacing)*4)}.mb-5{margin-bottom:calc(var(--spacing)*5)}.mb-8{margin-bottom:calc(var(--spacing)*8)}.contents{display:contents}.flex{display:flex}.h-\[600px\]{height:600px}.h-full{height:100%}.min-h-full{min-height:100%}.w-20{width:calc(var(--spacing)*20)}.w-full{width:100%}.max-w-3xl{max-width:var(--container-3xl)}.max-w-\[200px\]{max-width:200px}.flex-1{flex:1}.cursor-pointer{cursor:pointer}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-center{justify-content:center}.gap-1{gap:calc(var(--spacing)*1)}.gap-2{gap:calc(var(--spacing)*2)}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.border{border-style:var(--tw-border-style);border-width:1px}.border-gray-300{border-color:var(--color-gray-300)}.border-gray-600{border-color:var(--color-gray-600)}.border-indigo-200{border-color:var(--color-indigo-200)}.bg-blue-500{background-color:var(--color-blue-500)}.bg-gray-100{background-color:var(--color-gray-100)}.bg-gray-800{background-color:var(--color-gray-800)}.bg-indigo-100{background-color:var(--color-indigo-100)}.bg-purple-50{background-color:var(--color-purple-50)}.bg-red-500{background-color:var(--color-red-500)}.bg-white{background-color:var(--color-white)}.p-4{padding:calc(var(--spacing)*4)}.p-5{padding:calc(var(--spacing)*5)}.p-8{padding:calc(var(--spacing)*8)}.px-2{padding-inline:calc(var(--spacing)*2)}.px-4{padding-inline:calc(var(--spacing)*4)}.py-1{padding-block:calc(var(--spacing)*1)}.py-2{padding-block:calc(var(--spacing)*2)}.text-center{text-align:center}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.text-gray-200{color:var(--color-gray-200)}.text-gray-600{color:var(--color-gray-600)}.text-gray-700{color:var(--color-gray-700)}.text-gray-800{color:var(--color-gray-800)}.text-gray-900{color:var(--color-gray-900)}.text-indigo-700{color:var(--color-indigo-700)}.text-purple-600{color:var(--color-purple-600)}.text-red-500{color:var(--color-red-500)}.text-white{color:var(--color-white)}.shadow-md{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a),0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}@media(hover:hover){.hover\:bg-indigo-200:hover{background-color:var(--color-indigo-200)}}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:bg-gray-300:disabled{background-color:var(--color-gray-300)}}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ToolResult } from "gui-chat-protocol/vue";
|
|
2
|
+
import type { MusicToolData } from "../core/types";
|
|
3
|
+
type __VLS_Props = {
|
|
4
|
+
result: ToolResult<MusicToolData>;
|
|
5
|
+
};
|
|
6
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
7
|
+
declare const _default: typeof __VLS_export;
|
|
8
|
+
export default _default;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ToolResult } from "gui-chat-protocol/vue";
|
|
2
|
+
import type { MusicToolData } from "../core/types";
|
|
3
|
+
type __VLS_Props = {
|
|
4
|
+
selectedResult: ToolResult<MusicToolData>;
|
|
5
|
+
sendTextMessage: (text?: string) => void;
|
|
6
|
+
};
|
|
7
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
8
|
+
declare const _default: typeof __VLS_export;
|
|
9
|
+
export default _default;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import "../style.css";
|
|
2
|
+
import type { ToolPlugin } from "gui-chat-protocol/vue";
|
|
3
|
+
import type { MusicToolData, MusicArgs } from "../core/types";
|
|
4
|
+
export declare const plugin: ToolPlugin<MusicToolData, unknown, MusicArgs>;
|
|
5
|
+
export { showMusic as executeMusic } from "../core/plugin";
|
|
6
|
+
export * from "../core/types";
|
|
7
|
+
export { TOOL_DEFINITION } from "../core/definition";
|
|
8
|
+
declare const _default: {
|
|
9
|
+
plugin: ToolPlugin<MusicToolData, unknown, MusicArgs, import("gui-chat-protocol/vue").InputHandler, Record<string, unknown>>;
|
|
10
|
+
};
|
|
11
|
+
export default _default;
|
package/dist/vue.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const c=require("./core.cjs"),e=require("vue"),h=require("opensheetmusicdisplay"),w=require("osmd-audio-player"),b={class:"w-full h-full overflow-y-auto"},E={class:"min-h-full flex flex-col p-4"},M={key:0,class:"mb-4 text-center"},_={class:"text-2xl font-bold text-gray-900"},N={class:"mb-4 flex gap-2 items-center justify-center"},T=["disabled"],V={class:"text-xl"},k={class:"flex items-center gap-2"},S=e.defineComponent({__name:"View",props:{selectedResult:{},sendTextMessage:{type:Function}},setup(r){const d=r,l=e.ref(null);let i=null,t=null;const m=e.ref(!1),o=e.ref(!1),a=e.ref(200),p=e.ref(!1),f=e.ref(!1),v=async()=>{if(!(!l.value||!d.selectedResult.data?.musicXML))try{l.value.innerHTML="",m.value=!1,o.value=!1,i=new h.OpenSheetMusicDisplay(l.value,{autoResize:!0,backend:"svg",drawTitle:!1,followCursor:!0}),await i.load(d.selectedResult.data.musicXML),await i.render(),t||(t=new w),await t.loadScore(i);const n=i.Sheet?.SourceMeasures?.[0]?.TempoExpressions;if(n&&n.length>0){const s=n[0],u=s.TempoInBpm||s.tempoInBpm;u&&(a.value=u)}t.on("iteration",s=>{!p.value&&s&&s.length===0&&(t.stop(),o.value=!1)}),m.value=!0}catch(n){console.error("Error rendering music:",n),l.value&&(l.value.innerHTML=`<div class="text-red-500">Error rendering sheet music: ${n instanceof Error?n.message:"Unknown error"}</div>`)}},x=async()=>{!t||!m.value||(t.setBpm(Math.max(30,Math.min(300,a.value))),t.metronomeVolume=f.value?.7:0,t.isLooping=p.value,await t.play(),o.value=!0)},y=()=>{t&&(t.stop(),o.value=!1)};return e.watch(a,()=>{t&&t.setBpm(Math.max(30,Math.min(300,a.value)))}),e.watch(p,()=>{t&&(t.isLooping=p.value)}),e.watch(f,()=>{t&&(t.metronomeVolume=f.value?.7:0)}),e.onMounted(()=>{v()}),e.watch(()=>d.selectedResult.data?.musicXML,()=>{v()}),e.onUnmounted(()=>{t&&t.stop()}),(n,s)=>(e.openBlock(),e.createElementBlock("div",b,[e.createElementVNode("div",E,[r.selectedResult.title?(e.openBlock(),e.createElementBlock("div",M,[e.createElementVNode("h2",_,e.toDisplayString(r.selectedResult.title),1)])):e.createCommentVNode("",!0),e.createElementVNode("div",N,[e.createElementVNode("button",{onClick:s[0]||(s[0]=u=>o.value?y():x()),disabled:!m.value,class:e.normalizeClass(["px-4 py-2 text-white rounded disabled:bg-gray-300 disabled:cursor-not-allowed flex items-center gap-1",o.value?"bg-red-500":"bg-blue-500"])},[e.createElementVNode("span",V,e.toDisplayString(o.value?"⏹":"▶"),1),e.createTextVNode(" "+e.toDisplayString(o.value?"Stop":"Play"),1)],10,T),e.createElementVNode("label",k,[s[2]||(s[2]=e.createTextVNode(" Tempo ",-1)),e.withDirectives(e.createElementVNode("input",{"onUpdate:modelValue":s[1]||(s[1]=u=>a.value=u),type:"number",min:"30",max:"300",class:"w-20 px-2 py-1 border border-gray-300 rounded"},null,512),[[e.vModelText,a.value,void 0,{number:!0}]]),s[3]||(s[3]=e.createTextVNode(" bpm ",-1))])]),e.createElementVNode("div",{ref_key:"musicContainer",ref:l,class:"flex-1 flex items-center justify-center bg-white rounded-lg p-4"},null,512)])]))}}),B={class:"text-center p-4 bg-purple-50 rounded"},C={key:0,class:"text-xs text-gray-600 mt-1 truncate"},I=e.defineComponent({__name:"Preview",props:{result:{}},setup(r){return(d,l)=>(e.openBlock(),e.createElementBlock("div",B,[l[0]||(l[0]=e.createElementVNode("div",{class:"text-purple-600 font-medium"},"🎵 Sheet Music",-1)),r.result.title?(e.openBlock(),e.createElementBlock("div",C,e.toDisplayString(r.result.title),1)):e.createCommentVNode("",!0)]))}}),g={toolDefinition:c.TOOL_DEFINITION,execute:c.showMusic,generatingMessage:"Rendering sheet music...",isEnabled:()=>!0,viewComponent:S,previewComponent:I,samples:c.samples},L={plugin:g};exports.TOOL_DEFINITION=c.TOOL_DEFINITION;exports.executeMusic=c.showMusic;exports.default=L;exports.plugin=g;
|
package/dist/vue.js
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { samples as k, showMusic as C, TOOL_DEFINITION as E } from "./core.js";
|
|
2
|
+
import { defineComponent as w, ref as i, watch as f, onMounted as L, onUnmounted as R, createElementBlock as x, openBlock as g, createElementVNode as l, createCommentVNode as M, toDisplayString as v, normalizeClass as S, createTextVNode as h, withDirectives as V, vModelText as B } from "vue";
|
|
3
|
+
import { OpenSheetMusicDisplay as D } from "opensheetmusicdisplay";
|
|
4
|
+
import I from "osmd-audio-player";
|
|
5
|
+
const N = { class: "w-full h-full overflow-y-auto" }, P = { class: "min-h-full flex flex-col p-4" }, $ = {
|
|
6
|
+
key: 0,
|
|
7
|
+
class: "mb-4 text-center"
|
|
8
|
+
}, O = { class: "text-2xl font-bold text-gray-900" }, U = { class: "mb-4 flex gap-2 items-center justify-center" }, X = ["disabled"], j = { class: "text-xl" }, z = { class: "flex items-center gap-2" }, F = /* @__PURE__ */ w({
|
|
9
|
+
__name: "View",
|
|
10
|
+
props: {
|
|
11
|
+
selectedResult: {},
|
|
12
|
+
sendTextMessage: { type: Function }
|
|
13
|
+
},
|
|
14
|
+
setup(a) {
|
|
15
|
+
const d = a, s = i(null);
|
|
16
|
+
let u = null, e = null;
|
|
17
|
+
const m = i(!1), o = i(!1), r = i(200), p = i(!1), y = i(!1), b = async () => {
|
|
18
|
+
if (!(!s.value || !d.selectedResult.data?.musicXML))
|
|
19
|
+
try {
|
|
20
|
+
s.value.innerHTML = "", m.value = !1, o.value = !1, u = new D(s.value, {
|
|
21
|
+
autoResize: !0,
|
|
22
|
+
backend: "svg",
|
|
23
|
+
drawTitle: !1,
|
|
24
|
+
followCursor: !0
|
|
25
|
+
}), await u.load(d.selectedResult.data.musicXML), await u.render(), e || (e = new I()), await e.loadScore(u);
|
|
26
|
+
const n = u.Sheet?.SourceMeasures?.[0]?.TempoExpressions;
|
|
27
|
+
if (n && n.length > 0) {
|
|
28
|
+
const t = n[0], c = t.TempoInBpm || t.tempoInBpm;
|
|
29
|
+
c && (r.value = c);
|
|
30
|
+
}
|
|
31
|
+
e.on("iteration", (t) => {
|
|
32
|
+
!p.value && t && t.length === 0 && (e.stop(), o.value = !1);
|
|
33
|
+
}), m.value = !0;
|
|
34
|
+
} catch (n) {
|
|
35
|
+
console.error("Error rendering music:", n), s.value && (s.value.innerHTML = `<div class="text-red-500">Error rendering sheet music: ${n instanceof Error ? n.message : "Unknown error"}</div>`);
|
|
36
|
+
}
|
|
37
|
+
}, _ = async () => {
|
|
38
|
+
!e || !m.value || (e.setBpm(Math.max(30, Math.min(300, r.value))), e.metronomeVolume = y.value ? 0.7 : 0, e.isLooping = p.value, await e.play(), o.value = !0);
|
|
39
|
+
}, T = () => {
|
|
40
|
+
e && (e.stop(), o.value = !1);
|
|
41
|
+
};
|
|
42
|
+
return f(r, () => {
|
|
43
|
+
e && e.setBpm(Math.max(30, Math.min(300, r.value)));
|
|
44
|
+
}), f(p, () => {
|
|
45
|
+
e && (e.isLooping = p.value);
|
|
46
|
+
}), f(y, () => {
|
|
47
|
+
e && (e.metronomeVolume = y.value ? 0.7 : 0);
|
|
48
|
+
}), L(() => {
|
|
49
|
+
b();
|
|
50
|
+
}), f(
|
|
51
|
+
() => d.selectedResult.data?.musicXML,
|
|
52
|
+
() => {
|
|
53
|
+
b();
|
|
54
|
+
}
|
|
55
|
+
), R(() => {
|
|
56
|
+
e && e.stop();
|
|
57
|
+
}), (n, t) => (g(), x("div", N, [
|
|
58
|
+
l("div", P, [
|
|
59
|
+
a.selectedResult.title ? (g(), x("div", $, [
|
|
60
|
+
l("h2", O, v(a.selectedResult.title), 1)
|
|
61
|
+
])) : M("", !0),
|
|
62
|
+
l("div", U, [
|
|
63
|
+
l("button", {
|
|
64
|
+
onClick: t[0] || (t[0] = (c) => o.value ? T() : _()),
|
|
65
|
+
disabled: !m.value,
|
|
66
|
+
class: S(["px-4 py-2 text-white rounded disabled:bg-gray-300 disabled:cursor-not-allowed flex items-center gap-1", o.value ? "bg-red-500" : "bg-blue-500"])
|
|
67
|
+
}, [
|
|
68
|
+
l("span", j, v(o.value ? "⏹" : "▶"), 1),
|
|
69
|
+
h(" " + v(o.value ? "Stop" : "Play"), 1)
|
|
70
|
+
], 10, X),
|
|
71
|
+
l("label", z, [
|
|
72
|
+
t[2] || (t[2] = h(" Tempo ", -1)),
|
|
73
|
+
V(l("input", {
|
|
74
|
+
"onUpdate:modelValue": t[1] || (t[1] = (c) => r.value = c),
|
|
75
|
+
type: "number",
|
|
76
|
+
min: "30",
|
|
77
|
+
max: "300",
|
|
78
|
+
class: "w-20 px-2 py-1 border border-gray-300 rounded"
|
|
79
|
+
}, null, 512), [
|
|
80
|
+
[
|
|
81
|
+
B,
|
|
82
|
+
r.value,
|
|
83
|
+
void 0,
|
|
84
|
+
{ number: !0 }
|
|
85
|
+
]
|
|
86
|
+
]),
|
|
87
|
+
t[3] || (t[3] = h(" bpm ", -1))
|
|
88
|
+
])
|
|
89
|
+
]),
|
|
90
|
+
l("div", {
|
|
91
|
+
ref_key: "musicContainer",
|
|
92
|
+
ref: s,
|
|
93
|
+
class: "flex-1 flex items-center justify-center bg-white rounded-lg p-4"
|
|
94
|
+
}, null, 512)
|
|
95
|
+
])
|
|
96
|
+
]));
|
|
97
|
+
}
|
|
98
|
+
}), H = { class: "text-center p-4 bg-purple-50 rounded" }, q = {
|
|
99
|
+
key: 0,
|
|
100
|
+
class: "text-xs text-gray-600 mt-1 truncate"
|
|
101
|
+
}, A = /* @__PURE__ */ w({
|
|
102
|
+
__name: "Preview",
|
|
103
|
+
props: {
|
|
104
|
+
result: {}
|
|
105
|
+
},
|
|
106
|
+
setup(a) {
|
|
107
|
+
return (d, s) => (g(), x("div", H, [
|
|
108
|
+
s[0] || (s[0] = l("div", { class: "text-purple-600 font-medium" }, "🎵 Sheet Music", -1)),
|
|
109
|
+
a.result.title ? (g(), x("div", q, v(a.result.title), 1)) : M("", !0)
|
|
110
|
+
]));
|
|
111
|
+
}
|
|
112
|
+
}), G = {
|
|
113
|
+
toolDefinition: E,
|
|
114
|
+
execute: C,
|
|
115
|
+
generatingMessage: "Rendering sheet music...",
|
|
116
|
+
isEnabled: () => !0,
|
|
117
|
+
viewComponent: F,
|
|
118
|
+
previewComponent: A,
|
|
119
|
+
samples: k
|
|
120
|
+
}, Y = { plugin: G };
|
|
121
|
+
export {
|
|
122
|
+
E as TOOL_DEFINITION,
|
|
123
|
+
Y as default,
|
|
124
|
+
C as executeMusic,
|
|
125
|
+
G as plugin
|
|
126
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@gui-chat-plugin/music",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "MusicXML sheet music plugin for GUI Chat",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
},
|
|
15
|
+
"./core": {
|
|
16
|
+
"types": "./dist/core/index.d.ts",
|
|
17
|
+
"import": "./dist/core.js",
|
|
18
|
+
"require": "./dist/core.cjs"
|
|
19
|
+
},
|
|
20
|
+
"./vue": {
|
|
21
|
+
"types": "./dist/vue/index.d.ts",
|
|
22
|
+
"import": "./dist/vue.js",
|
|
23
|
+
"require": "./dist/vue.cjs"
|
|
24
|
+
},
|
|
25
|
+
"./style.css": "./dist/style.css"
|
|
26
|
+
},
|
|
27
|
+
"files": ["dist"],
|
|
28
|
+
"scripts": {
|
|
29
|
+
"dev": "vite",
|
|
30
|
+
"build": "vite build && vue-tsc -p tsconfig.build.json --emitDeclarationOnly",
|
|
31
|
+
"typecheck": "vue-tsc --noEmit",
|
|
32
|
+
"lint": "eslint src demo"
|
|
33
|
+
},
|
|
34
|
+
"peerDependencies": {
|
|
35
|
+
"vue": "^3.5.0"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"gui-chat-protocol": "^0.0.1",
|
|
39
|
+
"opensheetmusicdisplay": "^1.9.3",
|
|
40
|
+
"osmd-audio-player": "^0.7.0"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@tailwindcss/vite": "^4.1.18",
|
|
44
|
+
"@typescript-eslint/eslint-plugin": "^8.53.0",
|
|
45
|
+
"@typescript-eslint/parser": "^8.53.0",
|
|
46
|
+
"@vitejs/plugin-vue": "^6.0.3",
|
|
47
|
+
"eslint": "^9.39.2",
|
|
48
|
+
"eslint-plugin-vue": "^10.6.2",
|
|
49
|
+
"globals": "^17.0.0",
|
|
50
|
+
"tailwindcss": "^4.1.18",
|
|
51
|
+
"typescript": "~5.9.3",
|
|
52
|
+
"vite": "^7.3.1",
|
|
53
|
+
"vue": "^3.5.26",
|
|
54
|
+
"vue-eslint-parser": "^10.2.0",
|
|
55
|
+
"vue-tsc": "^3.2.2"
|
|
56
|
+
},
|
|
57
|
+
"keywords": ["guichat", "plugin", "music", "musicxml", "sheet-music"],
|
|
58
|
+
"license": "MIT"
|
|
59
|
+
}
|