ssb-chess-mithril 2.0.9 → 2.1.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/css/chessground-examples/README.md +14 -4
- package/css/chessground-examples/assets/3d.css +24 -23
- package/css/chessground-examples/assets/chessground.css +73 -15
- package/css/chessground-examples/assets/examples.css +1 -1
- package/css/chessground-examples/assets/theme.css +37 -26
- package/css/chessground-examples/index.html +5 -5
- package/css/chessground-examples/package-lock.json +1747 -8086
- package/css/chessground-examples/package.json +32 -17
- package/css/chessground-examples/pnpm-lock.yaml +874 -0
- package/css/chessground-examples/rollup.config.mjs +20 -0
- package/css/chessground-examples/src/main.ts +81 -48
- package/css/chessground-examples/src/units/anim.ts +63 -42
- package/css/chessground-examples/src/units/basics.ts +73 -13
- package/css/chessground-examples/src/units/fen.ts +21 -18
- package/css/chessground-examples/src/units/in3d.ts +26 -26
- package/css/chessground-examples/src/units/perf.ts +29 -15
- package/css/chessground-examples/src/units/play.ts +71 -52
- package/css/chessground-examples/src/units/svg.ts +230 -102
- package/css/chessground-examples/src/units/unit.ts +47 -18
- package/css/chessground-examples/src/units/viewOnly.ts +11 -11
- package/css/chessground-examples/src/units/zh.ts +25 -23
- package/css/chessground-examples/src/util.ts +30 -18
- package/css/chessground-examples/tsconfig.json +6 -4
- package/css/miniboards.css +1 -0
- package/index.js +87 -78
- package/package.json +4 -4
- package/ui/game/PieceGraveyard.js +1 -1
- package/ui/game/chat.js +51 -0
- package/ui/game/gameHistory.js +5 -1
- package/ui/game/gameView.js +242 -269
- package/ui/miniboard/miniboard.js +8 -5
- package/ui/pageLayout/navigation.js +11 -7
- package/css/chessground-examples/gulpfile.js +0 -52
- package/css/chessground-examples/index.standalone.html +0 -17
- package/css/chessground-examples/yarn.lock +0 -3529
|
@@ -1,46 +1,58 @@
|
|
|
1
|
-
import { Api } from
|
|
1
|
+
import { Api } from "chessground/api";
|
|
2
|
+
import { Color, Key } from "chessground/types";
|
|
3
|
+
import { ChessInstance, SQUARES } from "chess.js";
|
|
2
4
|
|
|
3
|
-
export function toDests(chess:
|
|
4
|
-
const dests =
|
|
5
|
-
|
|
6
|
-
const ms = chess.moves({square: s, verbose: true});
|
|
7
|
-
if (ms.length)
|
|
5
|
+
export function toDests(chess: ChessInstance): Map<Key, Key[]> {
|
|
6
|
+
const dests = new Map();
|
|
7
|
+
SQUARES.forEach((s) => {
|
|
8
|
+
const ms = chess.moves({ square: s, verbose: true });
|
|
9
|
+
if (ms.length)
|
|
10
|
+
dests.set(
|
|
11
|
+
s,
|
|
12
|
+
ms.map((m) => m.to),
|
|
13
|
+
);
|
|
8
14
|
});
|
|
9
15
|
return dests;
|
|
10
16
|
}
|
|
11
17
|
|
|
12
|
-
export function toColor(chess:
|
|
13
|
-
return
|
|
14
|
-
|
|
18
|
+
export function toColor(chess: ChessInstance): Color {
|
|
19
|
+
return chess.turn() === "w" ? "white" : "black";
|
|
15
20
|
}
|
|
16
21
|
|
|
17
22
|
export function playOtherSide(cg: Api, chess) {
|
|
18
23
|
return (orig, dest) => {
|
|
19
|
-
chess.move({from: orig, to: dest});
|
|
24
|
+
chess.move({ from: orig, to: dest });
|
|
20
25
|
cg.set({
|
|
21
26
|
turnColor: toColor(chess),
|
|
22
27
|
movable: {
|
|
23
28
|
color: toColor(chess),
|
|
24
|
-
dests: toDests(chess)
|
|
25
|
-
}
|
|
29
|
+
dests: toDests(chess),
|
|
30
|
+
},
|
|
26
31
|
});
|
|
27
32
|
};
|
|
28
33
|
}
|
|
29
34
|
|
|
30
|
-
export function aiPlay(
|
|
35
|
+
export function aiPlay(
|
|
36
|
+
cg: Api,
|
|
37
|
+
chess: ChessInstance,
|
|
38
|
+
delay: number,
|
|
39
|
+
firstMove: boolean,
|
|
40
|
+
) {
|
|
31
41
|
return (orig, dest) => {
|
|
32
|
-
chess.move({from: orig, to: dest});
|
|
42
|
+
chess.move({ from: orig, to: dest });
|
|
33
43
|
setTimeout(() => {
|
|
34
|
-
const moves = chess.moves({verbose:true});
|
|
35
|
-
const move = firstMove
|
|
44
|
+
const moves = chess.moves({ verbose: true });
|
|
45
|
+
const move = firstMove
|
|
46
|
+
? moves[0]
|
|
47
|
+
: moves[Math.floor(Math.random() * moves.length)];
|
|
36
48
|
chess.move(move.san);
|
|
37
49
|
cg.move(move.from, move.to);
|
|
38
50
|
cg.set({
|
|
39
51
|
turnColor: toColor(chess),
|
|
40
52
|
movable: {
|
|
41
53
|
color: toColor(chess),
|
|
42
|
-
dests: toDests(chess)
|
|
43
|
-
}
|
|
54
|
+
dests: toDests(chess),
|
|
55
|
+
},
|
|
44
56
|
});
|
|
45
57
|
cg.playPremove();
|
|
46
58
|
}, delay);
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
{
|
|
2
|
-
"include": [
|
|
3
|
-
"src/*.ts"
|
|
4
|
-
],
|
|
2
|
+
"include": ["src/*.ts"],
|
|
5
3
|
"compilerOptions": {
|
|
6
4
|
"outDir": "./dist",
|
|
5
|
+
"rootDir": "./src",
|
|
7
6
|
"noImplicitAny": false,
|
|
8
7
|
"strictNullChecks": true,
|
|
9
8
|
"noUnusedLocals": true,
|
|
@@ -12,6 +11,9 @@
|
|
|
12
11
|
"noImplicitReturns": true,
|
|
13
12
|
"noImplicitThis": true,
|
|
14
13
|
"noUnusedParameters": true,
|
|
15
|
-
"
|
|
14
|
+
"moduleResolution": "node",
|
|
15
|
+
"target": "ES2018",
|
|
16
|
+
"module": "esnext",
|
|
17
|
+
"lib": ["ES2018", "DOM"]
|
|
16
18
|
}
|
|
17
19
|
}
|
package/css/miniboards.css
CHANGED
package/index.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
const m = require('mithril');
|
|
2
|
-
const onceTrue = require('mutant/once-true');
|
|
3
|
-
|
|
4
2
|
const MainCtrl = require('ssb-chess');
|
|
3
|
+
const util = require('util');
|
|
5
4
|
|
|
6
5
|
const MiniboardListComponent = require('./ui/miniboard/miniboard_list');
|
|
7
6
|
const NavigationBar = require('./ui/pageLayout/navigation');
|
|
@@ -15,66 +14,71 @@ const Notifier = require('./ui/notify/notifier');
|
|
|
15
14
|
module.exports = (attachToElement, dataAccess, opts = {}) => {
|
|
16
15
|
const { initialView } = opts;
|
|
17
16
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
17
|
+
// function cssFileElements() {
|
|
18
|
+
// const styles = m('div', {}, cssPaths().map(file => m('link', { rel: 'stylesheet', href: file })));
|
|
19
|
+
// return styles
|
|
20
|
+
// }
|
|
21
|
+
|
|
22
|
+
function cssPaths() {
|
|
23
|
+
const cssFiles = [
|
|
24
|
+
'./css/global.css',
|
|
25
|
+
'./css/chessground-examples/assets/chessground.css',
|
|
26
|
+
'./css/chessground-examples/assets/theme.css',
|
|
27
|
+
'./css/board-theme.css',
|
|
28
|
+
'./css/miniboards.css',
|
|
29
|
+
'./css/largeBoard.css',
|
|
30
|
+
'./css/invites.css',
|
|
31
|
+
'./css/loading.css',
|
|
32
|
+
'./css/promote.css',
|
|
33
|
+
'./css/game.css',
|
|
34
|
+
'./css/historyArea.css',
|
|
35
|
+
'./css/playerProfiles.css',
|
|
36
|
+
'./css/actionButtons.css',
|
|
37
|
+
'./css/activity.css',
|
|
38
|
+
'./css/pgnExport.css',
|
|
39
|
+
'./css/nextPrevious.css'
|
|
40
|
+
];
|
|
39
41
|
|
|
40
42
|
const rootDir = `${__dirname}/`;
|
|
41
|
-
|
|
42
|
-
const styles = m('div', {}, cssFiles.map(file => m('link', { rel: 'stylesheet', href: rootDir + file })));
|
|
43
|
-
|
|
44
|
-
m.render(dom, styles);
|
|
43
|
+
return cssFiles.map(file => rootDir + file)
|
|
45
44
|
}
|
|
46
45
|
|
|
47
|
-
function
|
|
48
|
-
const navBar = NavigationBar(mainCtrl, settingsCtrl);
|
|
46
|
+
function appendCss() {
|
|
49
47
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
m(navBar),
|
|
53
|
-
]),
|
|
54
|
-
};
|
|
48
|
+
cssPaths().forEach((filepath) => {
|
|
49
|
+
const link = document.createElement('link');
|
|
55
50
|
|
|
56
|
-
|
|
51
|
+
link.setAttribute('rel', 'stylesheet');
|
|
52
|
+
link.setAttribute('href', filepath);
|
|
53
|
+
document.head.appendChild(link);
|
|
54
|
+
})
|
|
57
55
|
}
|
|
58
56
|
|
|
59
|
-
function appRouter(
|
|
57
|
+
async function appRouter(mountElement, mainCtrl, settingsCtrl) {
|
|
58
|
+
appendCss();
|
|
59
|
+
|
|
60
60
|
const gamesInProgressObs = mainCtrl.getGameCtrl().getMyGamesInProgress();
|
|
61
61
|
const gamesMyMoveObs = mainCtrl.getGameCtrl().getGamesWhereMyMove();
|
|
62
62
|
const observableGamesObs = mainCtrl.getGameCtrl().getFriendsObservableGames();
|
|
63
63
|
const userRecentActivity = mainCtrl.getRecentActivityCtrl().getRecentActivityForUserGames();
|
|
64
64
|
|
|
65
65
|
// Hack: keep observables loaded with the latest value.
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
66
|
+
const watches = [
|
|
67
|
+
gamesInProgressObs(e => e),
|
|
68
|
+
gamesMyMoveObs(),
|
|
69
|
+
observableGamesObs(e => e),
|
|
70
|
+
userRecentActivity(e => e)
|
|
71
|
+
];
|
|
70
72
|
|
|
71
73
|
const defaultView = initialView || '/my_games';
|
|
72
74
|
|
|
73
|
-
m.route.prefix
|
|
75
|
+
m.route.prefix = '?';
|
|
76
|
+
|
|
77
|
+
const navigationBar = NavigationBar(mainCtrl, settingsCtrl)
|
|
74
78
|
|
|
75
|
-
m.route(
|
|
76
|
-
'/my_games': MiniboardListComponent(mainCtrl, gamesInProgressObs, mainCtrl.getMyIdent()),
|
|
77
|
-
'/games_my_move': MiniboardListComponent(mainCtrl, gamesMyMoveObs, mainCtrl.getMyIdent()),
|
|
79
|
+
m.route(mountElement, defaultView, {
|
|
80
|
+
'/my_games': layoutTemplate(navigationBar, MiniboardListComponent(mainCtrl, gamesInProgressObs, mainCtrl.getMyIdent())),
|
|
81
|
+
'/games_my_move': layoutTemplate(navigationBar, MiniboardListComponent(mainCtrl, gamesMyMoveObs, mainCtrl.getMyIdent())),
|
|
78
82
|
'/games/:gameId': {
|
|
79
83
|
onmatch(args) {
|
|
80
84
|
const gameId = atob(args.gameId);
|
|
@@ -86,67 +90,72 @@ module.exports = (attachToElement, dataAccess, opts = {}) => {
|
|
|
86
90
|
observableGames: observableGamesObs
|
|
87
91
|
}
|
|
88
92
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
const gameComponent = GameComponent(dataAccess, mainCtrl, observables, settingsCtrl);
|
|
94
|
-
resolve(gameComponent);
|
|
95
|
-
});
|
|
96
|
-
});
|
|
93
|
+
return GameComponent(gameId, dataAccess, mainCtrl, observables, settingsCtrl).then((gameComponent) => {
|
|
94
|
+
return layoutTemplate(navigationBar, gameComponent);
|
|
95
|
+
})
|
|
96
|
+
|
|
97
97
|
},
|
|
98
98
|
},
|
|
99
|
-
'/invitations': InvitationsComponent(mainCtrl),
|
|
100
|
-
'/activity': RecentActivityComponent(mainCtrl, userRecentActivity),
|
|
101
|
-
'/observable': MiniboardListComponent(mainCtrl, observableGamesObs, mainCtrl.getMyIdent()),
|
|
102
|
-
'/player/:playerId': PlayerProfileComponent(mainCtrl),
|
|
99
|
+
'/invitations': layoutTemplate(navigationBar, InvitationsComponent(mainCtrl)),
|
|
100
|
+
'/activity': layoutTemplate(navigationBar, RecentActivityComponent(mainCtrl, userRecentActivity)),
|
|
101
|
+
'/observable': layoutTemplate(navigationBar, MiniboardListComponent(mainCtrl, observableGamesObs, mainCtrl.getMyIdent())),
|
|
102
|
+
'/player/:playerId': layoutTemplate(navigationBar, PlayerProfileComponent(mainCtrl)),
|
|
103
103
|
'/games/:gameId/pgn': {
|
|
104
104
|
onmatch(args) {
|
|
105
105
|
const gameId = atob(args.gameId);
|
|
106
106
|
return mainCtrl.getPgnCtrl()
|
|
107
107
|
.getPgnExport(gameId)
|
|
108
|
-
.then(pgnText => PgnExportComponent(gameId, pgnText));
|
|
108
|
+
.then(pgnText => layoutTemplate(navigationBar, PgnExportComponent(gameId, pgnText)));
|
|
109
109
|
},
|
|
110
110
|
},
|
|
111
111
|
});
|
|
112
112
|
|
|
113
|
+
return watches;
|
|
113
114
|
}
|
|
114
115
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
116
|
+
function layoutTemplate(navigationBarComponent, component) {
|
|
117
|
+
|
|
118
|
+
// TODO: the nav bar should accept observables by injection and we should give it
|
|
119
|
+
// the same instances on each render so there's not jitter on the numbers in the nav items
|
|
120
|
+
return {
|
|
121
|
+
view: function() {
|
|
122
|
+
return m('div', {className: 'ssb-chess-container'}, [
|
|
123
|
+
m(navigationBarComponent),
|
|
124
|
+
m('div', m(component))
|
|
125
|
+
])
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
}
|
|
129
|
+
}
|
|
128
130
|
|
|
129
|
-
|
|
130
|
-
cssFilesToStyleTag(cssDiv);
|
|
131
|
+
}
|
|
131
132
|
|
|
132
|
-
|
|
133
|
-
mainBody.appendChild(navDiv);
|
|
134
|
-
mainBody.appendChild(bodyDiv);
|
|
133
|
+
const whoAmIAsync = util.promisify(dataAccess.whoAmI.bind(dataAccess))
|
|
135
134
|
|
|
136
|
-
|
|
135
|
+
const globalWatches = whoAmIAsync().then((ident) => {
|
|
136
|
+
const mainCtrl = MainCtrl(dataAccess, ident.id);
|
|
137
|
+
const settingsCtrl = mainCtrl.getSettingsCtrl();
|
|
137
138
|
|
|
138
139
|
// Display HTML5 notifications if the user is not viewing the chess app
|
|
139
140
|
// and one of their games has an update.
|
|
140
141
|
const notifier = Notifier(mainCtrl);
|
|
141
142
|
notifier.startNotifying();
|
|
142
143
|
|
|
143
|
-
appRouter(
|
|
144
|
-
})
|
|
144
|
+
return appRouter(attachToElement, mainCtrl, settingsCtrl);
|
|
145
|
+
})
|
|
145
146
|
|
|
146
147
|
return {
|
|
147
148
|
goToGame: (gameId) => {
|
|
148
149
|
const gameRoute = `/games/${btoa(gameId)}`;
|
|
149
150
|
m.route.set(gameRoute);
|
|
150
151
|
},
|
|
152
|
+
destroy: () => {
|
|
153
|
+
// unmount the app
|
|
154
|
+
m.route(attachToElement, null);
|
|
155
|
+
globalWatches.then((watches) => watches.forEach(w => w()))
|
|
156
|
+
}
|
|
151
157
|
};
|
|
152
158
|
};
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ssb-chess-mithril",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.1",
|
|
4
4
|
"description": "An ssb-chess client",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -14,17 +14,17 @@
|
|
|
14
14
|
"author": "Gordon Martin",
|
|
15
15
|
"license": "GPL-3.0",
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"chessground": "
|
|
17
|
+
"@lichess-org/chessground": "^10.4.2",
|
|
18
18
|
"howler": "^2.0.15",
|
|
19
19
|
"human-time": "0.0.2",
|
|
20
20
|
"hyperscript": "^2.0.2",
|
|
21
|
-
"mithril": "
|
|
21
|
+
"mithril": "2.3.6",
|
|
22
22
|
"mutant": "3.29.0",
|
|
23
23
|
"pubsub-js": "^1.7.0",
|
|
24
24
|
"pull-scroll": "^1.0.9",
|
|
25
25
|
"pull-stream": "^3.6.14",
|
|
26
26
|
"ramda": "^0.25.0",
|
|
27
|
-
"ssb-chess": "4.0.
|
|
27
|
+
"ssb-chess": "4.0.7",
|
|
28
28
|
"ssb-embedded-chat": "2.0.2"
|
|
29
29
|
}
|
|
30
30
|
}
|
package/ui/game/chat.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
|
|
2
|
+
const m = require('mithril')
|
|
3
|
+
const EmbeddedChat = require('ssb-embedded-chat');
|
|
4
|
+
const pull = require('pull-stream');
|
|
5
|
+
|
|
6
|
+
module.exports = function ChatBox(myIdent, dataAccess, originalSituation) {
|
|
7
|
+
|
|
8
|
+
return {
|
|
9
|
+
view: () => {
|
|
10
|
+
return m('div', {className: 'ssb-chess-chat'});
|
|
11
|
+
},
|
|
12
|
+
oncreate: function (vNode) {
|
|
13
|
+
this.embeddedChat = makeEmbeddedChat(myIdent, dataAccess, originalSituation);
|
|
14
|
+
vNode.dom.appendChild(this.embeddedChat.getChatboxElement());
|
|
15
|
+
},
|
|
16
|
+
onremove: function() {
|
|
17
|
+
this.embeddedChat.destroy();
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function makeEmbeddedChat(myIdent, dataAccess, situation) {
|
|
23
|
+
const config = {
|
|
24
|
+
rootMessageId: situation.gameId,
|
|
25
|
+
chatMessageType: 'chess_chat',
|
|
26
|
+
chatMessageField: 'msg',
|
|
27
|
+
chatboxEnabled: true,
|
|
28
|
+
previousChatId: situation.rematchFrom
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
if (situation.players[myIdent] != null) {
|
|
32
|
+
config.isPublic = false;
|
|
33
|
+
config.participants = Object.keys(situation.players);
|
|
34
|
+
} else {
|
|
35
|
+
config.isPublic = true;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
config.publishPublic = dataAccess.publishPublicChessMessage.bind(dataAccess);
|
|
39
|
+
config.publishPrivate = dataAccess.publishPrivateChessMessage.bind(dataAccess);
|
|
40
|
+
|
|
41
|
+
config.getChatStream = (gameId, live) =>
|
|
42
|
+
pull(dataAccess.allGameMessages(gameId, live), pull.filter(msg => !msg.sync && msg.value && msg.value.content.type == "chess_chat"));
|
|
43
|
+
|
|
44
|
+
config.aboutSelfChangeStream = (since) => dataAccess.aboutSelfChangesUserIds(since);
|
|
45
|
+
|
|
46
|
+
config.getDisplayName = (id, cb) => dataAccess.getPlayerDisplayName(id, cb);
|
|
47
|
+
|
|
48
|
+
const chat = EmbeddedChat(config);
|
|
49
|
+
|
|
50
|
+
return chat;
|
|
51
|
+
}
|
package/ui/game/gameHistory.js
CHANGED
|
@@ -7,7 +7,11 @@ const R = require('ramda');
|
|
|
7
7
|
const UserLocationUtils = require('../viewer_perspective/user_location')();
|
|
8
8
|
|
|
9
9
|
module.exports = (gameObservable) => {
|
|
10
|
-
|
|
10
|
+
// TODO(hack): the router calls this function inside a 'onceTrue' which releases the observable once it exits the function body,
|
|
11
|
+
// at that point the observable would have no subscribers left and would have an initial value of null in the places it is used,
|
|
12
|
+
// so subscribing immediately stops that happening
|
|
13
|
+
const unwatch = gameObservable(e => e);
|
|
14
|
+
let watchesToClear = [unwatch];
|
|
11
15
|
|
|
12
16
|
let moveNumberSelected = 'live';
|
|
13
17
|
const moveSelectedObservable = Value(moveNumberSelected);
|