@gui-chat-plugin/othello 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/README.md ADDED
@@ -0,0 +1,101 @@
1
+ # @gui-chat-plugin/othello
2
+
3
+ Othello/Reversi game plugin for GUI Chat applications. Play Othello against an AI assistant or let it guide you through the game.
4
+
5
+ ## Features
6
+
7
+ - Full Othello/Reversi game implementation
8
+ - Interactive board with clickable moves
9
+ - Legal move indicators
10
+ - Turn tracking (user vs computer)
11
+ - Game end detection and winner announcement
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ npm install @gui-chat-plugin/othello
17
+ ```
18
+
19
+ ## Usage
20
+
21
+ ### Vue Integration
22
+
23
+ ```typescript
24
+ import { plugin } from "@gui-chat-plugin/othello/vue";
25
+ import "@gui-chat-plugin/othello/style.css";
26
+
27
+ // Register the plugin with your GUI Chat application
28
+ registerPlugin(plugin);
29
+ ```
30
+
31
+ ### Core-only Usage
32
+
33
+ ```typescript
34
+ import { executeOthello, playOthello, TOOL_DEFINITION } from "@gui-chat-plugin/othello";
35
+
36
+ // Start a new game
37
+ const result = await executeOthello(context, {
38
+ action: "new_game",
39
+ firstPlayer: "user",
40
+ });
41
+
42
+ // Use the game logic directly
43
+ import { playOthello } from "@gui-chat-plugin/othello";
44
+
45
+ const state = playOthello({
46
+ action: "new_game",
47
+ playerNames: { B: "user", W: "computer" },
48
+ });
49
+ ```
50
+
51
+ ## API
52
+
53
+ ### OthelloArgs
54
+
55
+ ```typescript
56
+ interface OthelloArgs {
57
+ action: "new_game" | "move" | "pass";
58
+ col?: number; // 0-7 for move action
59
+ row?: number; // 0-7 for move action
60
+ board?: Cell[][]; // Current board state
61
+ currentSide?: Side; // "B" or "W"
62
+ playerNames?: { B: OthelloPlayerType; W: OthelloPlayerType };
63
+ firstPlayer?: OthelloPlayerType; // "user" or "computer"
64
+ }
65
+ ```
66
+
67
+ ### OthelloState
68
+
69
+ ```typescript
70
+ interface OthelloState {
71
+ board: OthelloBoard; // 8x8 grid of ".", "B", or "W"
72
+ currentSide: Side; // Whose turn is next
73
+ playerNames: { B: string; W: string };
74
+ legalMoves: { row: number; col: number }[];
75
+ counts: { B: number; W: number; empty: number };
76
+ isTerminal: boolean;
77
+ winner: Side | "draw" | null;
78
+ lastAction: { type: "new_game" | "move" | "pass"; ... };
79
+ error?: string;
80
+ }
81
+ ```
82
+
83
+ ## Development
84
+
85
+ ```bash
86
+ # Install dependencies
87
+ yarn install
88
+
89
+ # Run demo
90
+ yarn dev
91
+
92
+ # Build
93
+ yarn build
94
+
95
+ # Lint
96
+ yarn lint
97
+ ```
98
+
99
+ ## License
100
+
101
+ MIT
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Othello Plugin - Tool Definition
3
+ */
4
+ import type { ToolDefinition } from "gui-chat-protocol";
5
+ export declare const TOOL_NAME = "playOthello";
6
+ export declare const TOOL_DEFINITION: ToolDefinition;
7
+ export declare const SYSTEM_PROMPT = "You can play Othello/Reversi with users. When a user wants to play:\n1. Start a new game with the \"new_game\" action\n2. For moves, the user specifies column (A-H) and row (1-8)\n3. Use the \"move\" action with the current board state\n4. Pass the current board state as-is - the game logic handles piece placement and flipping";
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Othello Plugin - Core Entry Point
3
+ */
4
+ export type { Cell, Side, OthelloBoard, OthelloCellValue, OthelloPlayerType, OthelloArgs, OthelloState, Command, NewGameCommand, MoveCommand, } from "./types";
5
+ export { TOOL_NAME, TOOL_DEFINITION, SYSTEM_PROMPT, executeOthello, pluginCore, playOthello, } from "./plugin";
6
+ export { samples } from "./samples";
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Othello Plugin - Game Logic
3
+ */
4
+ import type { Command, OthelloState } from "./types";
5
+ export declare function playOthello(cmd: Command): OthelloState;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Othello Plugin Core
3
+ */
4
+ import type { ToolPluginCore, ToolContext, ToolResult } from "gui-chat-protocol";
5
+ import type { OthelloArgs, OthelloState } from "./types";
6
+ export { TOOL_NAME, TOOL_DEFINITION, SYSTEM_PROMPT } from "./definition";
7
+ export { playOthello } from "./logic";
8
+ export declare const executeOthello: (_context: ToolContext, args: OthelloArgs) => Promise<ToolResult<never, OthelloState>>;
9
+ export declare const pluginCore: ToolPluginCore<never, OthelloState, OthelloArgs>;
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Othello Plugin - Samples
3
+ */
4
+ import type { ToolSample } from "gui-chat-protocol";
5
+ export declare const samples: ToolSample[];
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Othello Plugin - Type Definitions
3
+ */
4
+ export type Cell = "." | "B" | "W";
5
+ export type Side = "B" | "W";
6
+ export type OthelloBoard = Cell[][];
7
+ export type OthelloCellValue = Cell;
8
+ export type OthelloPlayerType = "user" | "computer";
9
+ export interface OthelloArgs {
10
+ action: "new_game" | "move" | "pass";
11
+ col?: number;
12
+ row?: number;
13
+ board?: OthelloCellValue[][];
14
+ currentSide?: Side;
15
+ playerNames?: {
16
+ B: OthelloPlayerType;
17
+ W: OthelloPlayerType;
18
+ };
19
+ firstPlayer?: OthelloPlayerType;
20
+ }
21
+ export type NewGameCommand = {
22
+ action: "new_game";
23
+ playerNames: {
24
+ B: string;
25
+ W: string;
26
+ };
27
+ };
28
+ export type MoveCommand = {
29
+ action: "move";
30
+ row: number;
31
+ col: number;
32
+ board: OthelloBoard;
33
+ currentSide: Side;
34
+ playerNames: {
35
+ B: string;
36
+ W: string;
37
+ };
38
+ } | {
39
+ action: "pass";
40
+ board: OthelloBoard;
41
+ currentSide: Side;
42
+ playerNames: {
43
+ B: string;
44
+ W: string;
45
+ };
46
+ };
47
+ export type Command = NewGameCommand | MoveCommand;
48
+ export interface OthelloState {
49
+ board: OthelloBoard;
50
+ currentSide: Side;
51
+ playerNames: {
52
+ B: string;
53
+ W: string;
54
+ };
55
+ legalMoves: {
56
+ row: number;
57
+ col: number;
58
+ }[];
59
+ counts: {
60
+ B: number;
61
+ W: number;
62
+ empty: number;
63
+ };
64
+ isTerminal: boolean;
65
+ winner: Side | "draw" | null;
66
+ lastAction: {
67
+ type: "new_game";
68
+ } | {
69
+ type: "move";
70
+ row: number;
71
+ col: number;
72
+ flipped: number;
73
+ } | {
74
+ type: "pass";
75
+ };
76
+ error?: string;
77
+ }
package/dist/core.cjs ADDED
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./index.cjs");exports.SYSTEM_PROMPT=e.SYSTEM_PROMPT;exports.TOOL_DEFINITION=e.TOOL_DEFINITION;exports.TOOL_NAME=e.TOOL_NAME;exports.executeOthello=e.executeOthello;exports.playOthello=e.playOthello;exports.pluginCore=e.pluginCore;exports.samples=e.samples;
package/dist/core.js ADDED
@@ -0,0 +1,10 @@
1
+ import { SYSTEM_PROMPT as l, TOOL_DEFINITION as o, TOOL_NAME as T, executeOthello as p, playOthello as t, pluginCore as r, samples as E } from "./index.js";
2
+ export {
3
+ l as SYSTEM_PROMPT,
4
+ o as TOOL_DEFINITION,
5
+ T as TOOL_NAME,
6
+ p as executeOthello,
7
+ t as playOthello,
8
+ r as pluginCore,
9
+ E as samples
10
+ };
package/dist/index.cjs ADDED
@@ -0,0 +1,6 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const S="playOthello",B={type:"function",name:S,description:"Play Othello/Reversi game with the user. You can start a new game, make moves, or pass turns.",parameters:{type:"object",properties:{action:{type:"string",enum:["new_game","move","pass"],description:"The action to perform: start a new game, make a move, or pass the turn"},col:{type:"number",description:"Column position for the move (0-7, required for 'move' action). The user will tell you the column by specifying A to H",minimum:0,maximum:7},row:{type:"number",description:"Row position for the move (0-7, required for 'move' action). The user will tell you the row by specifying 1 to 8",minimum:0,maximum:7},board:{type:"array",description:"Current 8x8 board state BEFORE the move (required for 'move' and 'pass' actions). IMPORTANT: Do NOT modify the board yourself - pass the current board state as-is, and the game logic will handle placing the piece and flipping opponent pieces.",items:{type:"array",items:{type:"string",enum:[".","B","W"]}}},currentSide:{type:"string",enum:["B","W"],description:"Current player's side (required for 'move' and 'pass' actions)"},playerNames:{type:"object",description:"Player assignments (required for 'move' and 'pass' actions)",properties:{B:{type:"string",enum:["user","computer"]},W:{type:"string",enum:["user","computer"]}},required:["B","W"]},firstPlayer:{type:"string",enum:["user","computer"],description:"Optional: Which player should play as Black (goes first) for 'new_game' action. If not specified, will be chosen randomly."}},required:["action"],additionalProperties:!1}},P=`You can play Othello/Reversi with users. When a user wants to play:
2
+ 1. Start a new game with the "new_game" action
3
+ 2. For moves, the user specifies column (A-H) and row (1-8)
4
+ 3. Use the "move" action with the current board state
5
+ 4. Pass the current board state as-is - the game logic handles piece placement and flipping`,k=[[-1,-1],[-1,0],[-1,1],[0,-1],[0,1],[1,-1],[1,0],[1,1]];function $(){const r=[];for(let e=0;e<8;e++)r[e]=new Array(8).fill(".");return r}function C(){const r=$();return r[3][3]="W",r[3][4]="B",r[4][3]="B",r[4][4]="W",r}function x(r){return r.map(e=>[...e])}function O(r,e){return r>=0&&r<8&&e>=0&&e<8}function w(r){return r==="B"?"W":"B"}function R(r,e,o,t,n,a){const i=w(a),s=[];let l=e+t,c=o+n;for(;O(l,c)&&r[l][c]===i;)s.push({row:l,col:c}),l+=t,c+=n;return O(l,c)&&r[l][c]===a&&s.length>0?s:[]}function M(r,e,o,t){if(r[e][o]!==".")return[];const n=[];for(const[a,i]of k){const s=R(r,e,o,a,i,t);n.push(...s)}return n}function A(r,e,o,t){return M(r,e,o,t).length>0}function d(r,e){const o=[];for(let t=0;t<8;t++)for(let n=0;n<8;n++)A(r,t,n,e)&&o.push({row:t,col:n});return o}function q(r,e,o,t){const n=x(r),a=M(r,e,o,t);n[e][o]=t;for(const{row:i,col:s}of a)n[i][s]=t;return{newBoard:n,flippedCount:a.length}}function y(r){let e=0,o=0,t=0;for(let n=0;n<8;n++)for(let a=0;a<8;a++){const i=r[n][a];i==="B"?e++:i==="W"?o++:t++}return{B:e,W:o,empty:t}}function N(r){return r.empty>0?null:r.B>r.W?"B":r.W>r.B?"W":"draw"}function _(r){if(r.action==="new_game"){const{playerNames:u}=r,m=C(),h=d(m,"B"),f=y(m);return{board:m,currentSide:"B",playerNames:u,legalMoves:h,counts:f,isTerminal:!1,winner:null,lastAction:{type:"new_game"}}}if(r.action==="pass"){const{board:u,currentSide:m,playerNames:h}=r,f=w(m),v=d(u,f),b=y(u),T=v.length===0,I=T?N(b):null;return{board:u,currentSide:f,playerNames:h,legalMoves:v,counts:b,isTerminal:T,winner:I,lastAction:{type:"pass"}}}const{row:e,col:o,board:t,currentSide:n,playerNames:a}=r;if(!A(t,e,o,n)){const u=d(t,n),m=y(t);return{board:t,currentSide:n,playerNames:a,legalMoves:u,counts:m,isTerminal:!1,winner:null,lastAction:{type:"pass"},error:`Invalid move: (${e}, ${o}) is not a legal move for ${n}`}}const{newBoard:i,flippedCount:s}=q(t,e,o,n),l=w(n),c=d(i,l),p=y(i),g=c.length===0&&d(i,n).length===0,E=g?N(p):null;return{board:i,currentSide:l,playerNames:a,legalMoves:c,counts:p,isTerminal:g,winner:E,lastAction:{type:"move",row:e,col:o,flipped:s}}}const W=async(r,e)=>{try{let o;if(e.action==="new_game"){let s;e.firstPlayer?s=e.firstPlayer:s=Math.random()<.5?"computer":"user",o={action:"new_game",playerNames:{B:s,W:s==="user"?"computer":"user"}}}else if(e.action==="move"){if(typeof e.row!="number"||typeof e.col!="number"||!e.board||!e.currentSide||!e.playerNames)throw new Error("Move action requires row, col, board, currentSide, and playerNames parameters");o={action:"move",row:e.row,col:e.col,board:e.board,currentSide:e.currentSide,playerNames:e.playerNames}}else if(e.action==="pass"){if(!e.board||!e.currentSide||!e.playerNames)throw new Error("Pass action requires board, currentSide, and playerNames parameters");o={action:"pass",board:e.board,currentSide:e.currentSide,playerNames:e.playerNames}}else throw new Error(`Unknown action: ${e.action}`);const t=_(o);if(t.error){const s=t.playerNames[t.currentSide]==="computer",l=t.legalMoves.map(p=>`(${p.row}, ${p.col})`).join(", "),c=s?`Invalid move attempted. You must make a valid move. Legal moves are: ${l}. Choose one of these moves.`:`Invalid move attempted. Tell the user they must make a valid move. Legal moves are: ${l}. The user will tell you the move by specifying column (A to H) and row (1 to 8).`;return{message:t.error,jsonData:t,instructions:c,updating:!0}}let n="";t.lastAction.type==="new_game"?n="Started a new Othello game! Black (●) goes first.":t.lastAction.type==="move"?n=`Played at (${t.lastAction.row}, ${t.lastAction.col}) and flipped ${t.lastAction.flipped} pieces.`:t.lastAction.type==="pass"&&(n="Passed the turn."),t.isTerminal&&(t.winner==="draw"?n+=" Game over - it's a draw!":t.winner&&(n+=` Game over - ${t.winner==="B"?"Black":"White"} wins!`));const a=t.playerNames[t.currentSide]==="computer",i=t.isTerminal?"The game is over. Announce the game result.":a?"The game state has been updated. Do not describe the state of the game. It is assistant's turn. You MUSK choose your next move.":"The game state has been updated. Tell the user to make a move. Do not describe the state of the game. The user is able to see it. The user will tell you the move by specifying colum (A to H) and row (1 to 8)";return{message:n,jsonData:t,instructions:i,instructionsRequired:t.isTerminal||a,updating:e.action!=="new_game"}}catch(o){return console.error(`ERR: exception
6
+ Othello game error`,o),{message:`Othello game error: ${o instanceof Error?o.message:"Unknown error"}`,instructions:"Acknowledge that there was an error with the Othello game and suggest trying again."}}},D={toolDefinition:B,execute:W,generatingMessage:"Processing Othello move...",isEnabled:()=>!0,systemPrompt:P},F=[{name:"New Game (User First)",args:{action:"new_game",firstPlayer:"user"}},{name:"New Game (Computer First)",args:{action:"new_game",firstPlayer:"computer"}}];exports.SYSTEM_PROMPT=P;exports.TOOL_DEFINITION=B;exports.TOOL_NAME=S;exports.executeOthello=W;exports.playOthello=_;exports.pluginCore=D;exports.samples=F;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Othello Plugin - Main Entry Point
3
+ */
4
+ export type { Cell, Side, OthelloBoard, OthelloCellValue, OthelloPlayerType, OthelloArgs, OthelloState, Command, NewGameCommand, MoveCommand, } from "./core/types";
5
+ export { TOOL_NAME, TOOL_DEFINITION, SYSTEM_PROMPT, executeOthello, pluginCore, playOthello, } from "./core/plugin";
6
+ export { samples } from "./core/samples";
package/dist/index.js ADDED
@@ -0,0 +1,291 @@
1
+ const A = "playOthello", W = {
2
+ type: "function",
3
+ name: A,
4
+ description: "Play Othello/Reversi game with the user. You can start a new game, make moves, or pass turns.",
5
+ parameters: {
6
+ type: "object",
7
+ properties: {
8
+ action: {
9
+ type: "string",
10
+ enum: ["new_game", "move", "pass"],
11
+ description: "The action to perform: start a new game, make a move, or pass the turn"
12
+ },
13
+ col: {
14
+ type: "number",
15
+ description: "Column position for the move (0-7, required for 'move' action). The user will tell you the column by specifying A to H",
16
+ minimum: 0,
17
+ maximum: 7
18
+ },
19
+ row: {
20
+ type: "number",
21
+ description: "Row position for the move (0-7, required for 'move' action). The user will tell you the row by specifying 1 to 8",
22
+ minimum: 0,
23
+ maximum: 7
24
+ },
25
+ board: {
26
+ type: "array",
27
+ description: "Current 8x8 board state BEFORE the move (required for 'move' and 'pass' actions). IMPORTANT: Do NOT modify the board yourself - pass the current board state as-is, and the game logic will handle placing the piece and flipping opponent pieces.",
28
+ items: {
29
+ type: "array",
30
+ items: {
31
+ type: "string",
32
+ enum: [".", "B", "W"]
33
+ }
34
+ }
35
+ },
36
+ currentSide: {
37
+ type: "string",
38
+ enum: ["B", "W"],
39
+ description: "Current player's side (required for 'move' and 'pass' actions)"
40
+ },
41
+ playerNames: {
42
+ type: "object",
43
+ description: "Player assignments (required for 'move' and 'pass' actions)",
44
+ properties: {
45
+ B: {
46
+ type: "string",
47
+ enum: ["user", "computer"]
48
+ },
49
+ W: {
50
+ type: "string",
51
+ enum: ["user", "computer"]
52
+ }
53
+ },
54
+ required: ["B", "W"]
55
+ },
56
+ firstPlayer: {
57
+ type: "string",
58
+ enum: ["user", "computer"],
59
+ description: "Optional: Which player should play as Black (goes first) for 'new_game' action. If not specified, will be chosen randomly."
60
+ }
61
+ },
62
+ required: ["action"],
63
+ additionalProperties: !1
64
+ }
65
+ }, _ = `You can play Othello/Reversi with users. When a user wants to play:
66
+ 1. Start a new game with the "new_game" action
67
+ 2. For moves, the user specifies column (A-H) and row (1-8)
68
+ 3. Use the "move" action with the current board state
69
+ 4. Pass the current board state as-is - the game logic handles piece placement and flipping`, k = [
70
+ [-1, -1],
71
+ [-1, 0],
72
+ [-1, 1],
73
+ [0, -1],
74
+ [0, 1],
75
+ [1, -1],
76
+ [1, 0],
77
+ [1, 1]
78
+ ];
79
+ function E() {
80
+ const n = [];
81
+ for (let e = 0; e < 8; e++)
82
+ n[e] = new Array(8).fill(".");
83
+ return n;
84
+ }
85
+ function I() {
86
+ const n = E();
87
+ return n[3][3] = "W", n[3][4] = "B", n[4][3] = "B", n[4][4] = "W", n;
88
+ }
89
+ function $(n) {
90
+ return n.map((e) => [...e]);
91
+ }
92
+ function B(n, e) {
93
+ return n >= 0 && n < 8 && e >= 0 && e < 8;
94
+ }
95
+ function w(n) {
96
+ return n === "B" ? "W" : "B";
97
+ }
98
+ function x(n, e, o, t, r, a) {
99
+ const i = w(a), s = [];
100
+ let l = e + t, c = o + r;
101
+ for (; B(l, c) && n[l][c] === i; )
102
+ s.push({ row: l, col: c }), l += t, c += r;
103
+ return B(l, c) && n[l][c] === a && s.length > 0 ? s : [];
104
+ }
105
+ function S(n, e, o, t) {
106
+ if (n[e][o] !== ".")
107
+ return [];
108
+ const r = [];
109
+ for (const [a, i] of k) {
110
+ const s = x(n, e, o, a, i, t);
111
+ r.push(...s);
112
+ }
113
+ return r;
114
+ }
115
+ function O(n, e, o, t) {
116
+ return S(n, e, o, t).length > 0;
117
+ }
118
+ function d(n, e) {
119
+ const o = [];
120
+ for (let t = 0; t < 8; t++)
121
+ for (let r = 0; r < 8; r++)
122
+ O(n, t, r, e) && o.push({ row: t, col: r });
123
+ return o;
124
+ }
125
+ function C(n, e, o, t) {
126
+ const r = $(n), a = S(n, e, o, t);
127
+ r[e][o] = t;
128
+ for (const { row: i, col: s } of a)
129
+ r[i][s] = t;
130
+ return { newBoard: r, flippedCount: a.length };
131
+ }
132
+ function y(n) {
133
+ let e = 0, o = 0, t = 0;
134
+ for (let r = 0; r < 8; r++)
135
+ for (let a = 0; a < 8; a++) {
136
+ const i = n[r][a];
137
+ i === "B" ? e++ : i === "W" ? o++ : t++;
138
+ }
139
+ return { B: e, W: o, empty: t };
140
+ }
141
+ function N(n) {
142
+ return n.empty > 0 ? null : n.B > n.W ? "B" : n.W > n.B ? "W" : "draw";
143
+ }
144
+ function q(n) {
145
+ if (n.action === "new_game") {
146
+ const { playerNames: u } = n, m = I(), h = d(m, "B"), f = y(m);
147
+ return {
148
+ board: m,
149
+ currentSide: "B",
150
+ playerNames: u,
151
+ legalMoves: h,
152
+ counts: f,
153
+ isTerminal: !1,
154
+ winner: null,
155
+ lastAction: { type: "new_game" }
156
+ };
157
+ }
158
+ if (n.action === "pass") {
159
+ const { board: u, currentSide: m, playerNames: h } = n, f = w(m), v = d(u, f), b = y(u), T = v.length === 0, M = T ? N(b) : null;
160
+ return {
161
+ board: u,
162
+ currentSide: f,
163
+ playerNames: h,
164
+ legalMoves: v,
165
+ counts: b,
166
+ isTerminal: T,
167
+ winner: M,
168
+ lastAction: { type: "pass" }
169
+ };
170
+ }
171
+ const { row: e, col: o, board: t, currentSide: r, playerNames: a } = n;
172
+ if (!O(t, e, o, r)) {
173
+ const u = d(t, r), m = y(t);
174
+ return {
175
+ board: t,
176
+ currentSide: r,
177
+ playerNames: a,
178
+ legalMoves: u,
179
+ counts: m,
180
+ isTerminal: !1,
181
+ winner: null,
182
+ lastAction: { type: "pass" },
183
+ // Keep previous action type
184
+ error: `Invalid move: (${e}, ${o}) is not a legal move for ${r}`
185
+ };
186
+ }
187
+ const { newBoard: i, flippedCount: s } = C(t, e, o, r), l = w(r), c = d(i, l), p = y(i), g = c.length === 0 && d(i, r).length === 0, P = g ? N(p) : null;
188
+ return {
189
+ board: i,
190
+ currentSide: l,
191
+ playerNames: a,
192
+ legalMoves: c,
193
+ counts: p,
194
+ isTerminal: g,
195
+ winner: P,
196
+ lastAction: { type: "move", row: e, col: o, flipped: s }
197
+ };
198
+ }
199
+ const R = async (n, e) => {
200
+ try {
201
+ let o;
202
+ if (e.action === "new_game") {
203
+ let s;
204
+ e.firstPlayer ? s = e.firstPlayer : s = Math.random() < 0.5 ? "computer" : "user", o = {
205
+ action: "new_game",
206
+ playerNames: { B: s, W: s === "user" ? "computer" : "user" }
207
+ };
208
+ } else if (e.action === "move") {
209
+ if (typeof e.row != "number" || typeof e.col != "number" || !e.board || !e.currentSide || !e.playerNames)
210
+ throw new Error(
211
+ "Move action requires row, col, board, currentSide, and playerNames parameters"
212
+ );
213
+ o = {
214
+ action: "move",
215
+ row: e.row,
216
+ col: e.col,
217
+ board: e.board,
218
+ currentSide: e.currentSide,
219
+ playerNames: e.playerNames
220
+ };
221
+ } else if (e.action === "pass") {
222
+ if (!e.board || !e.currentSide || !e.playerNames)
223
+ throw new Error(
224
+ "Pass action requires board, currentSide, and playerNames parameters"
225
+ );
226
+ o = {
227
+ action: "pass",
228
+ board: e.board,
229
+ currentSide: e.currentSide,
230
+ playerNames: e.playerNames
231
+ };
232
+ } else
233
+ throw new Error(`Unknown action: ${e.action}`);
234
+ const t = q(o);
235
+ if (t.error) {
236
+ const s = t.playerNames[t.currentSide] === "computer", l = t.legalMoves.map((p) => `(${p.row}, ${p.col})`).join(", "), c = s ? `Invalid move attempted. You must make a valid move. Legal moves are: ${l}. Choose one of these moves.` : `Invalid move attempted. Tell the user they must make a valid move. Legal moves are: ${l}. The user will tell you the move by specifying column (A to H) and row (1 to 8).`;
237
+ return {
238
+ message: t.error,
239
+ jsonData: t,
240
+ instructions: c,
241
+ updating: !0
242
+ };
243
+ }
244
+ let r = "";
245
+ t.lastAction.type === "new_game" ? r = "Started a new Othello game! Black (●) goes first." : t.lastAction.type === "move" ? r = `Played at (${t.lastAction.row}, ${t.lastAction.col}) and flipped ${t.lastAction.flipped} pieces.` : t.lastAction.type === "pass" && (r = "Passed the turn."), t.isTerminal && (t.winner === "draw" ? r += " Game over - it's a draw!" : t.winner && (r += ` Game over - ${t.winner === "B" ? "Black" : "White"} wins!`));
246
+ const a = t.playerNames[t.currentSide] === "computer", i = t.isTerminal ? "The game is over. Announce the game result." : a ? "The game state has been updated. Do not describe the state of the game. It is assistant's turn. You MUSK choose your next move." : "The game state has been updated. Tell the user to make a move. Do not describe the state of the game. The user is able to see it. The user will tell you the move by specifying colum (A to H) and row (1 to 8)";
247
+ return {
248
+ message: r,
249
+ jsonData: t,
250
+ instructions: i,
251
+ instructionsRequired: t.isTerminal || a,
252
+ updating: e.action !== "new_game"
253
+ };
254
+ } catch (o) {
255
+ return console.error(`ERR: exception
256
+ Othello game error`, o), {
257
+ message: `Othello game error: ${o instanceof Error ? o.message : "Unknown error"}`,
258
+ instructions: "Acknowledge that there was an error with the Othello game and suggest trying again."
259
+ };
260
+ }
261
+ }, D = {
262
+ toolDefinition: W,
263
+ execute: R,
264
+ generatingMessage: "Processing Othello move...",
265
+ isEnabled: () => !0,
266
+ systemPrompt: _
267
+ }, F = [
268
+ {
269
+ name: "New Game (User First)",
270
+ args: {
271
+ action: "new_game",
272
+ firstPlayer: "user"
273
+ }
274
+ },
275
+ {
276
+ name: "New Game (Computer First)",
277
+ args: {
278
+ action: "new_game",
279
+ firstPlayer: "computer"
280
+ }
281
+ }
282
+ ];
283
+ export {
284
+ _ as SYSTEM_PROMPT,
285
+ W as TOOL_DEFINITION,
286
+ A as TOOL_NAME,
287
+ R as executeOthello,
288
+ q as playOthello,
289
+ D as pluginCore,
290
+ F as samples
291
+ };
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-space-y-reverse:0;--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-green-50:oklch(98.2% .018 155.826);--color-green-600:oklch(62.7% .194 149.214);--color-green-700:oklch(52.7% .154 150.069);--color-green-800:oklch(44.8% .119 151.328);--color-green-900:oklch(39.3% .095 152.535);--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-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-black:#000;--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-lg:1.125rem;--text-lg--line-height:calc(1.75/1.125);--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}.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}.grid{display:grid}.inline-block{display:inline-block}.h-3{height:calc(var(--spacing)*3)}.h-4{height:calc(var(--spacing)*4)}.h-10{height:calc(var(--spacing)*10)}.h-12{height:calc(var(--spacing)*12)}.h-\[500px\]{height:500px}.h-full{height:100%}.w-3{width:calc(var(--spacing)*3)}.w-4{width:calc(var(--spacing)*4)}.w-10{width:calc(var(--spacing)*10)}.w-12{width:calc(var(--spacing)*12)}.w-full{width:100%}.max-w-3xl{max-width:var(--container-3xl)}.max-w-\[200px\]{max-width:200px}.cursor-default{cursor:default}.cursor-pointer{cursor:pointer}.grid-cols-8{grid-template-columns:repeat(8,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-center{justify-content:center}.gap-0\.5{gap:calc(var(--spacing)*.5)}.gap-2{gap:calc(var(--spacing)*2)}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*1)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*1)*calc(1 - var(--tw-space-y-reverse)))}.rounded{border-radius:.25rem}.rounded-full{border-radius:3.40282e38px}.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-2{border-style:var(--tw-border-style);border-width:2px}.border-gray-600{border-color:var(--color-gray-600)}.border-green-900{border-color:var(--color-green-900)}.border-indigo-200{border-color:var(--color-indigo-200)}.bg-black{background-color:var(--color-black)}.bg-gray-100{background-color:var(--color-gray-100)}.bg-gray-800{background-color:var(--color-gray-800)}.bg-green-50{background-color:var(--color-green-50)}.bg-green-600{background-color:var(--color-green-600)}.bg-green-700{background-color:var(--color-green-700)}.bg-green-800{background-color:var(--color-green-800)}.bg-indigo-100{background-color:var(--color-indigo-100)}.bg-white{background-color:var(--color-white)}.p-3{padding:calc(var(--spacing)*3)}.p-4{padding:calc(var(--spacing)*4)}.p-5{padding:calc(var(--spacing)*5)}.p-8{padding:calc(var(--spacing)*8)}.px-4{padding-inline:calc(var(--spacing)*4)}.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-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--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-300{color:var(--color-gray-300)}.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-indigo-700{color:var(--color-indigo-700)}.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-green-600:hover{background-color:var(--color-green-600)}.hover\:bg-indigo-200:hover{background-color:var(--color-indigo-200)}}}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@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 { OthelloState } from "../core/types";
3
+ type __VLS_Props = {
4
+ result: ToolResult<never, OthelloState>;
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 { OthelloState } from "../core/types";
3
+ type __VLS_Props = {
4
+ selectedResult: ToolResult<never, OthelloState> | null;
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,17 @@
1
+ /**
2
+ * Othello Plugin - Vue Implementation
3
+ */
4
+ import "../style.css";
5
+ import type { ToolPlugin } from "gui-chat-protocol/vue";
6
+ import type { OthelloArgs, OthelloState } from "../core/types";
7
+ import View from "./View.vue";
8
+ import Preview from "./Preview.vue";
9
+ export declare const plugin: ToolPlugin<never, OthelloState, OthelloArgs>;
10
+ export type { Cell, Side, OthelloBoard, OthelloCellValue, OthelloPlayerType, OthelloArgs, OthelloState, } from "../core/types";
11
+ export { TOOL_NAME, TOOL_DEFINITION, SYSTEM_PROMPT, executeOthello, pluginCore, playOthello, } from "../core/plugin";
12
+ export { samples } from "../core/samples";
13
+ export { View, Preview };
14
+ declare const _default: {
15
+ plugin: ToolPlugin<never, OthelloState, OthelloArgs, import("gui-chat-protocol/vue").InputHandler, Record<string, unknown>>;
16
+ };
17
+ 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("./index.cjs"),e=require("vue"),C={class:"w-full h-full flex flex-col items-center justify-center p-4"},E={key:0,class:"flex flex-col items-center"},w={class:"text-white text-lg font-bold mb-4 text-center"},N={class:"grid grid-cols-8 gap-0.5 p-4 bg-green-800 rounded-lg border-2 border-green-900"},M=["onClick","onMouseenter","onMouseleave"],x={key:1,class:"w-10 h-10 flex items-center justify-center text-gray-300 text-sm font-bold"},h=e.defineComponent({__name:"View",props:{selectedResult:{},sendTextMessage:{type:Function}},setup(a){const m=a,t=e.ref(null),s=e.ref(null);e.watch(()=>m.selectedResult,l=>{l?.toolName==="playOthello"&&l.jsonData&&(t.value=l.jsonData)},{immediate:!0});const g=e.computed(()=>{if(!t.value?.playerNames)return"";const l=t.value.playerNames[t.value.currentSide];return l.charAt(0).toUpperCase()+l.slice(1)}),v=e.computed(()=>t.value?t.value.currentSide==="B"?"Black":"White":""),i=e.computed(()=>t.value?.playerNames&&t.value.playerNames[t.value.currentSide]==="computer"),d=e.computed(()=>{if(!t.value?.board)return[];const l=[];for(let r=0;r<8;r++)for(let o=0;o<8;o++){const n=t.value.board[r][o],u=t.value.legalMoves?.some(k=>k.row===r&&k.col===o);l.push({row:r,col:o,piece:n!=="."?n:null,isLegalMove:u??!1,label:u?String.fromCharCode(65+o)+(r+1):""})}return l});function p(l,r){const o="w-12 h-12 flex items-center justify-center border border-green-900 bg-green-700",n=l.isLegalMove&&!i.value&&s.value===r?"bg-green-600":"",u=l.isLegalMove&&!i.value&&!t.value?.isTerminal?"cursor-pointer hover:bg-green-600":"cursor-default";return`${o} ${n} ${u}`}function _(l){return l==="B"?"bg-black":"bg-white"}function b(l){if(!t.value||t.value.isTerminal||i.value)return;const r=d.value[l];if(!r.isLegalMove)return;const o=String.fromCharCode(65+r.col),n=r.row+1;m.sendTextMessage(`I want to play at ${o}${n}, which is column=${r.col}, row=${r.row} `)}function f(l,r){!t.value||t.value.isTerminal||i.value||!d.value[l].isLegalMove||(s.value=r?l:null)}return(l,r)=>(e.openBlock(),e.createElementBlock("div",C,[t.value?(e.openBlock(),e.createElementBlock("div",E,[e.createElementVNode("div",w," Current Turn: "+e.toDisplayString(g.value)+" ("+e.toDisplayString(v.value)+") ",1),e.createElementVNode("div",N,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(d.value,(o,n)=>(e.openBlock(),e.createElementBlock("div",{key:n,class:e.normalizeClass(p(o,n)),onClick:u=>b(n),onMouseenter:u=>f(n,!0),onMouseleave:u=>f(n,!1)},[o.piece?(e.openBlock(),e.createElementBlock("div",{key:0,class:e.normalizeClass([_(o.piece),"w-10 h-10 rounded-full border-2 border-gray-600"])},null,2)):o.isLegalMove&&!i.value?(e.openBlock(),e.createElementBlock("div",x,e.toDisplayString(o.label),1)):e.createCommentVNode("",!0)],42,M))),128))])])):e.createCommentVNode("",!0)]))}}),T={class:"p-3 bg-green-50 rounded"},$={key:0,class:"space-y-1"},O={class:"flex justify-center"},D={class:"inline-block",style:{"background-color":"#2d5016",padding:"1px"}},S={class:"grid grid-cols-8",style:{gap:"1px"}},j={key:0,class:"w-3 h-3 bg-black rounded-full"},L={key:1,class:"w-3 h-3 bg-white rounded-full"},V={class:"text-xs text-center space-y-1"},P={key:0,class:"text-gray-600"},F={key:1,class:"font-medium"},y=e.defineComponent({__name:"Preview",props:{result:{}},setup(a){function m(s){return s.isTerminal?s.winner==="draw"?"Draw!":s.winner==="B"?"⚫ Black Wins!":s.winner==="W"?"⚪ White Wins!":"Game Over":""}function t(s){return s.charAt(0).toUpperCase()+s.slice(1)}return(s,g)=>(e.openBlock(),e.createElementBlock("div",T,[a.result.jsonData?(e.openBlock(),e.createElementBlock("div",$,[e.createElementVNode("div",O,[e.createElementVNode("div",D,[e.createElementVNode("div",S,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(a.result.jsonData.board,(v,i)=>(e.openBlock(),e.createElementBlock(e.Fragment,{key:i},[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(v,(d,p)=>(e.openBlock(),e.createElementBlock("div",{key:`${i}-${p}`,class:"w-4 h-4 flex items-center justify-center",style:{"background-color":"#3d6b20"}},[d==="B"?(e.openBlock(),e.createElementBlock("div",j)):d==="W"?(e.openBlock(),e.createElementBlock("div",L)):e.createCommentVNode("",!0)]))),128))],64))),128))])])]),e.createElementVNode("div",V,[a.result.jsonData.isTerminal?(e.openBlock(),e.createElementBlock("div",F,e.toDisplayString(m(a.result.jsonData)),1)):(e.openBlock(),e.createElementBlock("div",P,e.toDisplayString(a.result.jsonData.currentSide==="B"?"⚫":"⚪")+" "+e.toDisplayString(t(a.result.jsonData.playerNames[a.result.jsonData.currentSide]))+" to play ",1))])])):e.createCommentVNode("",!0)]))}}),B={...c.pluginCore,viewComponent:h,previewComponent:y,samples:c.samples},I={plugin:B};exports.SYSTEM_PROMPT=c.SYSTEM_PROMPT;exports.TOOL_DEFINITION=c.TOOL_DEFINITION;exports.TOOL_NAME=c.TOOL_NAME;exports.executeOthello=c.executeOthello;exports.playOthello=c.playOthello;exports.pluginCore=c.pluginCore;exports.samples=c.samples;exports.Preview=y;exports.View=h;exports.default=I;exports.plugin=B;
package/dist/vue.js ADDED
@@ -0,0 +1,163 @@
1
+ import { samples as T, pluginCore as D } from "./index.js";
2
+ import { SYSTEM_PROMPT as re, TOOL_DEFINITION as se, TOOL_NAME as le, executeOthello as ne, playOthello as oe } from "./index.js";
3
+ import { defineComponent as M, ref as x, watch as L, computed as g, createElementBlock as l, openBlock as n, createCommentVNode as p, createElementVNode as v, toDisplayString as f, Fragment as h, renderList as b, normalizeClass as $ } from "vue";
4
+ const B = { class: "w-full h-full flex flex-col items-center justify-center p-4" }, O = {
5
+ key: 0,
6
+ class: "flex flex-col items-center"
7
+ }, S = { class: "text-white text-lg font-bold mb-4 text-center" }, P = { class: "grid grid-cols-8 gap-0.5 p-4 bg-green-800 rounded-lg border-2 border-green-900" }, W = ["onClick", "onMouseenter", "onMouseleave"], E = {
8
+ key: 1,
9
+ class: "w-10 h-10 flex items-center justify-center text-gray-300 text-sm font-bold"
10
+ }, V = /* @__PURE__ */ M({
11
+ __name: "View",
12
+ props: {
13
+ selectedResult: {},
14
+ sendTextMessage: { type: Function }
15
+ },
16
+ setup(u) {
17
+ const m = u, e = x(null), a = x(null);
18
+ L(
19
+ () => m.selectedResult,
20
+ (t) => {
21
+ t?.toolName === "playOthello" && t.jsonData && (e.value = t.jsonData);
22
+ },
23
+ { immediate: !0 }
24
+ );
25
+ const w = g(() => {
26
+ if (!e.value?.playerNames) return "";
27
+ const t = e.value.playerNames[e.value.currentSide];
28
+ return t.charAt(0).toUpperCase() + t.slice(1);
29
+ }), y = g(() => e.value ? e.value.currentSide === "B" ? "Black" : "White" : ""), c = g(() => e.value?.playerNames && e.value.playerNames[e.value.currentSide] === "computer"), d = g(() => {
30
+ if (!e.value?.board) return [];
31
+ const t = [];
32
+ for (let r = 0; r < 8; r++)
33
+ for (let s = 0; s < 8; s++) {
34
+ const o = e.value.board[r][s], i = e.value.legalMoves?.some(
35
+ (k) => k.row === r && k.col === s
36
+ );
37
+ t.push({
38
+ row: r,
39
+ col: s,
40
+ piece: o !== "." ? o : null,
41
+ isLegalMove: i ?? !1,
42
+ label: i ? String.fromCharCode(65 + s) + (r + 1) : ""
43
+ });
44
+ }
45
+ return t;
46
+ });
47
+ function _(t, r) {
48
+ const s = "w-12 h-12 flex items-center justify-center border border-green-900 bg-green-700", o = t.isLegalMove && !c.value && a.value === r ? "bg-green-600" : "", i = t.isLegalMove && !c.value && !e.value?.isTerminal ? "cursor-pointer hover:bg-green-600" : "cursor-default";
49
+ return `${s} ${o} ${i}`;
50
+ }
51
+ function j(t) {
52
+ return t === "B" ? "bg-black" : "bg-white";
53
+ }
54
+ function N(t) {
55
+ if (!e.value || e.value.isTerminal || c.value)
56
+ return;
57
+ const r = d.value[t];
58
+ if (!r.isLegalMove) return;
59
+ const s = String.fromCharCode(65 + r.col), o = r.row + 1;
60
+ m.sendTextMessage(
61
+ `I want to play at ${s}${o}, which is column=${r.col}, row=${r.row} `
62
+ );
63
+ }
64
+ function C(t, r) {
65
+ !e.value || e.value.isTerminal || c.value || !d.value[t].isLegalMove || (a.value = r ? t : null);
66
+ }
67
+ return (t, r) => (n(), l("div", B, [
68
+ e.value ? (n(), l("div", O, [
69
+ v("div", S, " Current Turn: " + f(w.value) + " (" + f(y.value) + ") ", 1),
70
+ v("div", P, [
71
+ (n(!0), l(h, null, b(d.value, (s, o) => (n(), l("div", {
72
+ key: o,
73
+ class: $(_(s, o)),
74
+ onClick: (i) => N(o),
75
+ onMouseenter: (i) => C(o, !0),
76
+ onMouseleave: (i) => C(o, !1)
77
+ }, [
78
+ s.piece ? (n(), l("div", {
79
+ key: 0,
80
+ class: $([j(s.piece), "w-10 h-10 rounded-full border-2 border-gray-600"])
81
+ }, null, 2)) : s.isLegalMove && !c.value ? (n(), l("div", E, f(s.label), 1)) : p("", !0)
82
+ ], 42, W))), 128))
83
+ ])
84
+ ])) : p("", !0)
85
+ ]));
86
+ }
87
+ }), F = { class: "p-3 bg-green-50 rounded" }, A = {
88
+ key: 0,
89
+ class: "space-y-1"
90
+ }, I = { class: "flex justify-center" }, z = {
91
+ class: "inline-block",
92
+ style: { "background-color": "#2d5016", padding: "1px" }
93
+ }, G = {
94
+ class: "grid grid-cols-8",
95
+ style: { gap: "1px" }
96
+ }, R = {
97
+ key: 0,
98
+ class: "w-3 h-3 bg-black rounded-full"
99
+ }, U = {
100
+ key: 1,
101
+ class: "w-3 h-3 bg-white rounded-full"
102
+ }, H = { class: "text-xs text-center space-y-1" }, Y = {
103
+ key: 0,
104
+ class: "text-gray-600"
105
+ }, q = {
106
+ key: 1,
107
+ class: "font-medium"
108
+ }, J = /* @__PURE__ */ M({
109
+ __name: "Preview",
110
+ props: {
111
+ result: {}
112
+ },
113
+ setup(u) {
114
+ function m(a) {
115
+ return a.isTerminal ? a.winner === "draw" ? "Draw!" : a.winner === "B" ? "⚫ Black Wins!" : a.winner === "W" ? "⚪ White Wins!" : "Game Over" : "";
116
+ }
117
+ function e(a) {
118
+ return a.charAt(0).toUpperCase() + a.slice(1);
119
+ }
120
+ return (a, w) => (n(), l("div", F, [
121
+ u.result.jsonData ? (n(), l("div", A, [
122
+ v("div", I, [
123
+ v("div", z, [
124
+ v("div", G, [
125
+ (n(!0), l(h, null, b(u.result.jsonData.board, (y, c) => (n(), l(h, { key: c }, [
126
+ (n(!0), l(h, null, b(y, (d, _) => (n(), l("div", {
127
+ key: `${c}-${_}`,
128
+ class: "w-4 h-4 flex items-center justify-center",
129
+ style: { "background-color": "#3d6b20" }
130
+ }, [
131
+ d === "B" ? (n(), l("div", R)) : d === "W" ? (n(), l("div", U)) : p("", !0)
132
+ ]))), 128))
133
+ ], 64))), 128))
134
+ ])
135
+ ])
136
+ ]),
137
+ v("div", H, [
138
+ u.result.jsonData.isTerminal ? (n(), l("div", q, f(m(u.result.jsonData)), 1)) : (n(), l("div", Y, f(u.result.jsonData.currentSide === "B" ? "⚫" : "⚪") + " " + f(e(
139
+ u.result.jsonData.playerNames[u.result.jsonData.currentSide]
140
+ )) + " to play ", 1))
141
+ ])
142
+ ])) : p("", !0)
143
+ ]));
144
+ }
145
+ }), K = {
146
+ ...D,
147
+ viewComponent: V,
148
+ previewComponent: J,
149
+ samples: T
150
+ }, Z = { plugin: K };
151
+ export {
152
+ J as Preview,
153
+ re as SYSTEM_PROMPT,
154
+ se as TOOL_DEFINITION,
155
+ le as TOOL_NAME,
156
+ V as View,
157
+ Z as default,
158
+ ne as executeOthello,
159
+ oe as playOthello,
160
+ K as plugin,
161
+ D as pluginCore,
162
+ T as samples
163
+ };
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "@gui-chat-plugin/othello",
3
+ "version": "0.1.0",
4
+ "description": "Othello/Reversi game 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
+ },
40
+ "devDependencies": {
41
+ "@tailwindcss/vite": "^4.1.18",
42
+ "@typescript-eslint/eslint-plugin": "^8.53.0",
43
+ "@typescript-eslint/parser": "^8.53.0",
44
+ "@vitejs/plugin-vue": "^6.0.3",
45
+ "eslint": "^9.39.2",
46
+ "eslint-plugin-vue": "^10.6.2",
47
+ "globals": "^17.0.0",
48
+ "tailwindcss": "^4.1.18",
49
+ "typescript": "~5.9.3",
50
+ "vite": "^7.3.1",
51
+ "vue": "^3.5.26",
52
+ "vue-eslint-parser": "^10.2.0",
53
+ "vue-tsc": "^3.2.2"
54
+ },
55
+ "keywords": ["guichat", "plugin", "othello", "reversi", "game"],
56
+ "license": "MIT"
57
+ }