extract speech plugin, enable speech in analysis

v2
Thibault Duplessis 2019-04-29 22:26:07 +07:00
parent 6b3cd629ed
commit 50b874bfb3
11 changed files with 108 additions and 27 deletions

View File

@ -37,6 +37,7 @@
"ui/editor", "ui/editor",
"ui/game", "ui/game",
"ui/nvui", "ui/nvui",
"ui/speech",
"ui/insight", "ui/insight",
"ui/learn", "ui/learn",
"ui/lobby", "ui/lobby",

View File

@ -13,10 +13,10 @@ interface Lichess {
compiledScript(path: string): string compiledScript(path: string): string
keyboardMove: any keyboardMove: any
slider(): any slider(): any
raf(f: () => void): void raf(f: () => void): void;
requestIdleCallback(f: () => void): void requestIdleCallback(f: () => void): void;
loadCss(path: string): void loadCss(path: string): void;
loadCssPath(path: string): void loadCssPath(path: string): void;
loadedCss: { loadedCss: {
[key: string]: boolean; [key: string]: boolean;
} }
@ -54,16 +54,18 @@ interface Lichess {
render(ctrl: any): any; render(ctrl: any): any;
} }
playMusic(): any; playMusic(): any;
Speech?: { LichessSpeech?: LichessSpeech;
say(t: string, cut: boolean): void;
step(s: { san?: San }, cut: boolean): void;
};
spinnerHtml: string; spinnerHtml: string;
movetimeChart: any; movetimeChart: any;
hasTouchEvents: boolean; hasTouchEvents: boolean;
mousedownEvent: 'mousedown' | 'touchstart'; mousedownEvent: 'mousedown' | 'touchstart';
} }
interface LichessSpeech {
say(t: string, cut: boolean): void;
step(s: { san?: San }, cut: boolean): void;
}
interface Cookie { interface Cookie {
name: string; name: string;
value: string; value: string;
@ -88,9 +90,9 @@ interface Trans {
type PubsubCallback = (...data: any[]) => void; type PubsubCallback = (...data: any[]) => void;
interface Pubsub { interface Pubsub {
on(msg: string, f: PubsubCallback): void on(msg: string, f: PubsubCallback): void;
off(msg: string, f: PubsubCallback): void off(msg: string, f: PubsubCallback): void;
emit(msg: string): (...args: any[]) => void emit(msg: string): (...args: any[]) => void;
} }
interface LichessStorageHelper { interface LichessStorageHelper {

View File

@ -29,6 +29,7 @@ import { make as makePractice, PracticeCtrl } from './practice/practiceCtrl';
import { make as makeEvalCache, EvalCache } from './evalCache'; import { make as makeEvalCache, EvalCache } from './evalCache';
import { compute as computeAutoShapes } from './autoShape'; import { compute as computeAutoShapes } from './autoShape';
import { nextGlyphSymbol } from './nodeFinder'; import { nextGlyphSymbol } from './nodeFinder';
import * as speech from './speech';
import { AnalyseOpts, AnalyseData, ServerEvalData, Key, CgDests, JustCaptured, NvuiPlugin, Redraw } from './interfaces'; import { AnalyseOpts, AnalyseData, ServerEvalData, Key, CgDests, JustCaptured, NvuiPlugin, Redraw } from './interfaces';
import GamebookPlayCtrl from './study/gamebook/gamebookPlayCtrl'; import GamebookPlayCtrl from './study/gamebook/gamebookPlayCtrl';
import { ctrl as treeViewCtrl, TreeView } from './treeView/treeView'; import { ctrl as treeViewCtrl, TreeView } from './treeView/treeView';
@ -168,6 +169,8 @@ export default class AnalyseCtrl {
this.jumpToIndex(index); this.jumpToIndex(index);
this.redraw() this.redraw()
}); });
speech.setup();
} }
initialize(data: AnalyseData, merge: boolean): void { initialize(data: AnalyseData, merge: boolean): void {
@ -339,6 +342,7 @@ export default class AnalyseCtrl {
this.threatMode(false); this.threatMode(false);
this.ceval.stop(); this.ceval.stop();
this.startCeval(); this.startCeval();
speech.node(this.node);
} }
this.justPlayed = this.justDropped = this.justCaptured = undefined; this.justPlayed = this.justDropped = this.justCaptured = undefined;
this.explorer.setNode(); this.explorer.setNode();

View File

@ -0,0 +1,18 @@
export function setup() {
window.lichess.pubsub.on('speech.enabled', onSpeechChange);
onSpeechChange(window.lichess.sound.speech());
}
function onSpeechChange(enabled: boolean) {
if (!window.LichessSpeech && enabled)
window.lichess.loadScript(window.lichess.compiledScript('speech'));
else if (window.LichessSpeech && !enabled) window.LichessSpeech = undefined;
}
export function node(n: Tree.Node) {
withSpeech(s => s.step(n, true));
}
export function withSpeech(f: (speech: LichessSpeech) => void) {
if (window.LichessSpeech) f(window.LichessSpeech);
}

View File

@ -10,11 +10,6 @@ lilaGulpPlugins([
entries: ['src/plugins/keyboardMove.ts'], entries: ['src/plugins/keyboardMove.ts'],
target: 'lichess.round.keyboardMove.min.js' target: 'lichess.round.keyboardMove.min.js'
}, },
{
standalone: 'Speech',
entries: ['src/plugins/speech.ts'],
target: 'lichess.round.speech.min.js'
},
{ {
standalone: 'NVUI', standalone: 'NVUI',
entries: ['src/plugins/nvui.ts'], entries: ['src/plugins/nvui.ts'],

View File

@ -710,8 +710,7 @@ export default class RoundController {
if (!this.nvui) keyboard.init(this); if (!this.nvui) keyboard.init(this);
li.pubsub.on('speech.enabled', speech.onSpeechChange(this)); speech.setup(this);
speech.onSpeechChange(this)(li.sound.speech());
this.onChange(); this.onChange();
}); });

View File

@ -2,29 +2,40 @@ import RoundController from './ctrl';
import { Step } from './interfaces'; import { Step } from './interfaces';
import viewStatus from 'game/view/status'; import viewStatus from 'game/view/status';
export function onSpeechChange(ctrl: RoundController) { export function setup(ctrl: RoundController) {
window.lichess.pubsub.on('speech.enabled', onSpeechChange(ctrl));
onSpeechChange(ctrl)(window.lichess.sound.speech());
}
function onSpeechChange(ctrl: RoundController) {
return function(enabled: boolean) { return function(enabled: boolean) {
if (!window.Speech && enabled) if (!window.LichessSpeech && enabled)
window.lichess.loadScript('compiled/lichess.round.speech.min.js').then(() => status(ctrl)); window.lichess.loadScript(
else if (window.Speech && !enabled) window.Speech = undefined; window.lichess.compiledScript('speech')
).then(() => status(ctrl));
else if (window.LichessSpeech && !enabled) window.LichessSpeech = undefined;
}; };
} }
export function status(ctrl: RoundController) { export function status(ctrl: RoundController) {
const s = viewStatus(ctrl); const s = viewStatus(ctrl);
if (s == 'playingRightNow') window.Speech!.step(ctrl.stepAt(ctrl.ply), false); if (s == 'playingRightNow') window.LichessSpeech!.step(ctrl.stepAt(ctrl.ply), false);
else { else {
window.Speech!.say(s); withSpeech(speech => speech.say(s, false));
const w = ctrl.data.game.winner; const w = ctrl.data.game.winner;
if (w) window.Speech!.say(ctrl.trans.noarg(w + 'IsVictorious'), false) if (w) withSpeech(speech => speech.say(ctrl.trans.noarg(w + 'IsVictorious'), false));
} }
} }
export function userJump(ctrl: RoundController, ply: Ply) { export function userJump(ctrl: RoundController, ply: Ply) {
if (window.Speech) window.Speech.step(ctrl.stepAt(ply), true); withSpeech(s => s.step(ctrl.stepAt(ply), true));
} }
export function step(step: Step) { export function step(step: Step) {
if (window.Speech) window.Speech.step(step, false); withSpeech(s => s.step(step, false));
}
export function withSpeech(f: (speech: LichessSpeech) => void) {
if (window.LichessSpeech) f(window.LichessSpeech);
} }

View File

@ -0,0 +1,3 @@
const lilaGulp = require('../gulp/tsProject.js');
lilaGulp('LichessSpeech', 'lichess.speech', __dirname);

View File

@ -0,0 +1,33 @@
{
"name": "speech",
"version": "1.0.0",
"description": "lichess.org speech synthesis",
"main": "dist/main.js",
"types": "dist/main",
"repository": {
"type": "git",
"url": "https://github.com/ornicar/lila"
},
"keywords": [
"chess",
"lichess",
"non-visual",
"speech synthesis",
"accessibility"
],
"author": "Thibault Duplessis",
"license": "AGPL-3.0",
"bugs": {
"url": "https://github.com/ornicar/lila/issues"
},
"homepage": "https://github.com/ornicar/lila",
"scripts": {
"compile": "tsc"
},
"devDependencies": {
"@types/lichess": "1.0.0",
"typescript": "^3"
},
"dependencies": {
}
}

View File

@ -0,0 +1,15 @@
{
"compilerOptions": {
"noImplicitAny": true,
"strictNullChecks": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noEmitOnError": false,
"alwaysStrict": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"moduleResolution": "node",
"target": "ES5",
"lib": ["DOM", "ES5", "es2015.core", "es2015.Promise"]
}
}