@vnejs/plugins.text 0.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/const/const.js +3 -0
- package/const/events.js +19 -0
- package/const/params.js +10 -0
- package/const/settings.js +3 -0
- package/index.js +22 -0
- package/modules/cycle.js +44 -0
- package/modules/interact.js +34 -0
- package/modules/meet.js +32 -0
- package/modules/replace.js +26 -0
- package/modules/speaker.js +36 -0
- package/modules/text.js +70 -0
- package/modules/utils.js +37 -0
- package/package.json +18 -0
package/const/const.js
ADDED
package/const/events.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export const SUBSCRIBE_EVENTS = {
|
|
2
|
+
EMIT: "vne:text:emit",
|
|
3
|
+
EMIT_END: "vne:text:emit_end",
|
|
4
|
+
EMIT_BEFORE: "vne:text:emit_before",
|
|
5
|
+
|
|
6
|
+
CICLE: "vne:text:cicle",
|
|
7
|
+
INTERACT: "vne:text:interact",
|
|
8
|
+
CHANGED: "vne:text:changed",
|
|
9
|
+
|
|
10
|
+
MEET: "vne:text:meet",
|
|
11
|
+
|
|
12
|
+
SPEAKER: "vne:text:speaker",
|
|
13
|
+
SPEAKER_CHANGED: "vne:text:speaker_changed",
|
|
14
|
+
|
|
15
|
+
RECALC: "vne:text:recalc",
|
|
16
|
+
|
|
17
|
+
REPLACE: "vne:text:replace",
|
|
18
|
+
REPLACE_REG: "vne:text:replace_reg",
|
|
19
|
+
};
|
package/const/params.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export const DEFAULT_TOKEN_RENDER_SPEED = 1;
|
|
2
|
+
|
|
3
|
+
export const TOKEN_RENDER_DELAY = 16;
|
|
4
|
+
|
|
5
|
+
export const DEFAULT_SPEAKER = { names: { default: [""] }, color: "#ffffff", opacity: 1 };
|
|
6
|
+
export const DEFAULT_SPEAKER_NAME = "default";
|
|
7
|
+
|
|
8
|
+
export const SPEAKERS_INFO = {
|
|
9
|
+
[DEFAULT_SPEAKER_NAME]: DEFAULT_SPEAKER,
|
|
10
|
+
};
|
package/index.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { regPlugin } from "@vnejs/shared";
|
|
2
|
+
|
|
3
|
+
import * as constants from "./const/const";
|
|
4
|
+
import { SUBSCRIBE_EVENTS } from "./const/events";
|
|
5
|
+
import * as params from "./const/params";
|
|
6
|
+
import { SETTINGS_KEYS } from "./const/settings";
|
|
7
|
+
|
|
8
|
+
import { TextCycle } from "./modules/cycle";
|
|
9
|
+
import { TextInteract } from "./modules/interact";
|
|
10
|
+
import { TextMeet } from "./modules/meet";
|
|
11
|
+
import { TextReplace } from "./modules/replace";
|
|
12
|
+
import { TextSpeaker } from "./modules/speaker";
|
|
13
|
+
import { Text } from "./modules/text";
|
|
14
|
+
|
|
15
|
+
regPlugin("TEXT", { constants, events: SUBSCRIBE_EVENTS, settings: SETTINGS_KEYS, params }, [
|
|
16
|
+
TextCycle,
|
|
17
|
+
TextInteract,
|
|
18
|
+
TextMeet,
|
|
19
|
+
TextReplace,
|
|
20
|
+
TextSpeaker,
|
|
21
|
+
Text,
|
|
22
|
+
]);
|
package/modules/cycle.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Module } from "@vnejs/module";
|
|
2
|
+
|
|
3
|
+
export class TextCycle extends Module {
|
|
4
|
+
name = "text.cycle";
|
|
5
|
+
|
|
6
|
+
textUpdateRaf = null;
|
|
7
|
+
|
|
8
|
+
subscribe = () => {
|
|
9
|
+
this.on(this.EVENTS.TEXT.CICLE, this.onCicle);
|
|
10
|
+
|
|
11
|
+
this.on(this.EVENTS.TEXT.EMIT_END, this.cancelRaf);
|
|
12
|
+
this.on(this.EVENTS.STATE.CLEAR, this.cancelRaf);
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
init = () => this.emit(this.EVENTS.SETTINGS.INIT, { name: this.SETTINGS.TEXT.TOKEN_RENDER_SPEED, value: this.PARAMS.TEXT.DEFAULT_TOKEN_RENDER_SPEED });
|
|
16
|
+
|
|
17
|
+
onCicle = ({ tokens = [] } = {}) => this.setUpdateTextRaf(tokens, new Date().getTime());
|
|
18
|
+
|
|
19
|
+
setUpdateTextRaf = (tokens, ts) => {
|
|
20
|
+
this.textUpdateRaf = requestAnimationFrame(this.createUpdateTextFunc(tokens, ts));
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
createUpdateTextFunc = (tokens, lastTs) => async () => {
|
|
24
|
+
const ts = new Date().getTime();
|
|
25
|
+
const diff = ts - lastTs;
|
|
26
|
+
|
|
27
|
+
const delayForOneChar = this.PARAMS.TEXT.TOKEN_RENDER_DELAY / this.shared.settings[this.SETTINGS.TEXT.TOKEN_RENDER_SPEED];
|
|
28
|
+
|
|
29
|
+
if (diff < delayForOneChar) return this.setUpdateTextRaf(tokens, lastTs);
|
|
30
|
+
|
|
31
|
+
this.globalState.text.tokensVisible += Math.floor(diff / delayForOneChar);
|
|
32
|
+
|
|
33
|
+
const { tokens: currentTokens, tokensVisible = 0, uid = "" } = this.globalState.text;
|
|
34
|
+
|
|
35
|
+
if (currentTokens !== tokens) return;
|
|
36
|
+
if (tokensVisible > tokens.length) return this.emit(this.EVENTS.TEXT.EMIT_END, { isForced: false });
|
|
37
|
+
|
|
38
|
+
await this.emit(this.EVENTS.TEXT.CHANGED, { tokens, tokensVisible, uid });
|
|
39
|
+
|
|
40
|
+
return this.setUpdateTextRaf(tokens, ts - (diff % delayForOneChar));
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
cancelRaf = () => cancelAnimationFrame(this.textUpdateRaf);
|
|
44
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Module } from "@vnejs/module";
|
|
2
|
+
|
|
3
|
+
export class TextInteract extends Module {
|
|
4
|
+
name = "text.interact";
|
|
5
|
+
|
|
6
|
+
subscribe = () => {
|
|
7
|
+
this.on(this.EVENTS.TEXT.INTERACT, this.onTextInteract);
|
|
8
|
+
|
|
9
|
+
this.on(this.EVENTS.STATE.SET, this.onStateSet);
|
|
10
|
+
this.on(this.EVENTS.STATE.CLEAR, this.onStateClear);
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
interactHandlerRendering = () => this.emit(this.EVENTS.TEXT.EMIT_END, { isForced: true });
|
|
14
|
+
interactHandlerShowed = async () => {
|
|
15
|
+
await this.emit(this.EVENTS.TEXT.INTERACT, { state: this.CONST.TEXT.INTERACT_STATES.NEXT });
|
|
16
|
+
|
|
17
|
+
this.emit(this.EVENTS.SCENARIO.NEXT, { module: this.name });
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
interactArgRendering = { key: `${this.name}.rendering`, handler: this.interactHandlerRendering };
|
|
21
|
+
interactArgShowed = { key: `${this.name}.showed`, handler: this.interactHandlerShowed };
|
|
22
|
+
|
|
23
|
+
onTextInteract = async ({ state = "" } = {}) => {
|
|
24
|
+
this.state.state = state;
|
|
25
|
+
|
|
26
|
+
await this.emit(state === this.CONST.TEXT.INTERACT_STATES.RENDERING ? this.EVENTS.INTERACT.PUSH : this.EVENTS.INTERACT.POP, this.interactArgRendering);
|
|
27
|
+
await this.emit(state === this.CONST.TEXT.INTERACT_STATES.SHOWED ? this.EVENTS.INTERACT.PUSH : this.EVENTS.INTERACT.POP, this.interactArgShowed);
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
onStateSet = ({ [this.name]: state }) => this.emit(this.EVENTS.TEXT.INTERACT, state);
|
|
31
|
+
onStateClear = () => this.emit(this.EVENTS.TEXT.INTERACT, this.getDefaultState());
|
|
32
|
+
|
|
33
|
+
getDefaultState = () => ({ state: this.CONST.TEXT.INTERACT_STATES.EMPTY });
|
|
34
|
+
}
|
package/modules/meet.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Module } from "@vnejs/module";
|
|
2
|
+
|
|
3
|
+
const MEET_REG = /^(?<char>[a-zA-Z0-9_\-]+)( (?<value>-?\d+))?$/;
|
|
4
|
+
|
|
5
|
+
export class TextMeet extends Module {
|
|
6
|
+
name = "text.meet";
|
|
7
|
+
|
|
8
|
+
subscribe = () => {
|
|
9
|
+
this.on(this.EVENTS.TEXT.MEET, this.onMeet);
|
|
10
|
+
|
|
11
|
+
this.on(this.EVENTS.STATE.SET, this.setStateFromGlobalState);
|
|
12
|
+
this.on(this.EVENTS.STATE.CLEAR, this.setDefaultState);
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
init = () => this.emit(this.EVENTS.SCENARIO.LINE_EXEC_REG, { module: "meet", handler: this.execHandler });
|
|
16
|
+
|
|
17
|
+
execHandler = async (line, args) => {
|
|
18
|
+
const { char, value } = line.match(MEET_REG).groups;
|
|
19
|
+
|
|
20
|
+
await this.emit(this.EVENTS.TEXT.MEET, { char, value, ...args });
|
|
21
|
+
|
|
22
|
+
this.emit(this.EVENTS.SCENARIO.NEXT, { module: this.name });
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
onMeet = ({ char, value = 1 }) => {
|
|
26
|
+
this.emit(this.EVENTS.LOGS.EMIT, { module: this.name, value: { char, value } });
|
|
27
|
+
|
|
28
|
+
this.state[char] = this.state[char] ? this.state[char] + Number(value) : Number(value);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
getDefaultState = () => ({ default: 0 });
|
|
32
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Module } from "@vnejs/module";
|
|
2
|
+
|
|
3
|
+
export class TextReplace extends Module {
|
|
4
|
+
name = "text.replace";
|
|
5
|
+
|
|
6
|
+
handlers = [];
|
|
7
|
+
|
|
8
|
+
subscribe = () => {
|
|
9
|
+
this.on(this.EVENTS.TEXT.REPLACE, this.onReplace);
|
|
10
|
+
this.on(this.EVENTS.TEXT.REPLACE_REG, this.onReplaceReg);
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
onReplace = async ({ text, label, state } = {}) => {
|
|
14
|
+
let resultText = text;
|
|
15
|
+
|
|
16
|
+
for (let i = 0; i < this.handlers.length; i++) resultText = await this.handlers[i].handler(resultText, label, state);
|
|
17
|
+
|
|
18
|
+
return resultText;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
onReplaceReg = ({ priority = 0, handler } = {}) => {
|
|
22
|
+
this.handlers.push({ handler, priority });
|
|
23
|
+
|
|
24
|
+
this.handlers.sort((i, j) => i.priority - j.priority);
|
|
25
|
+
};
|
|
26
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Module } from "@vnejs/module";
|
|
2
|
+
|
|
3
|
+
export class TextSpeaker extends Module {
|
|
4
|
+
name = "text.speaker";
|
|
5
|
+
|
|
6
|
+
subscribe = () => {
|
|
7
|
+
this.on(this.EVENTS.TEXT.SPEAKER, this.onTextSpeaker);
|
|
8
|
+
|
|
9
|
+
this.on(this.EVENTS.STATE.CLEAR, this.onStateClear);
|
|
10
|
+
this.on(this.EVENTS.STATE.SET, this.onStateSet);
|
|
11
|
+
};
|
|
12
|
+
init = () => this.emit(this.EVENTS.SCENARIO.LINE_EXEC_REG, { module: "text", handler: this.onLineTextHandler });
|
|
13
|
+
onLineTextHandler = async (line, args) => {
|
|
14
|
+
const { speaker: lineSpeaker } = line.match(this.CONST.TEXT.LINE_REG).groups;
|
|
15
|
+
|
|
16
|
+
const speaker = lineSpeaker || this.PARAMS.TEXT.DEFAULT_SPEAKER_NAME;
|
|
17
|
+
const meet = this.globalState["text.meet"][speaker] ?? 0;
|
|
18
|
+
|
|
19
|
+
return this.emit(this.EVENTS.TEXT.SPEAKER, { speaker, meet, args });
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
onTextSpeaker = ({ speaker, meet, ...args }) => {
|
|
23
|
+
this.setState({ speaker, meet, args });
|
|
24
|
+
|
|
25
|
+
console.log(99999999, speaker);
|
|
26
|
+
|
|
27
|
+
this.emit(this.EVENTS.TEXT.SPEAKER_CHANGED, { speaker, meet });
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
onStateSet = ({ [this.name]: state }) => this.setNewState(state);
|
|
31
|
+
onStateClear = () => this.setNewState(this.getDefaultState());
|
|
32
|
+
|
|
33
|
+
setNewState = ({ speaker, meet, args } = {}) => this.emit(this.EVENTS.TEXT.SPEAKER, { speaker, meet, ...args });
|
|
34
|
+
|
|
35
|
+
getDefaultState = () => ({ speaker: this.PARAMS.TEXT.DEFAULT_SPEAKER_NAME, meet: 0, args: {} });
|
|
36
|
+
}
|
package/modules/text.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { Module } from "@vnejs/module";
|
|
2
|
+
|
|
3
|
+
import { textToTokens } from "./utils";
|
|
4
|
+
|
|
5
|
+
const BIG_VALUE = 99999999;
|
|
6
|
+
|
|
7
|
+
export class Text extends Module {
|
|
8
|
+
name = "text";
|
|
9
|
+
|
|
10
|
+
subscribe = () => {
|
|
11
|
+
this.on(this.EVENTS.TEXT.EMIT, this.onTextEmit);
|
|
12
|
+
this.on(this.EVENTS.TEXT.EMIT_END, this.onTextEmitEnd);
|
|
13
|
+
|
|
14
|
+
this.on(this.EVENTS.TEXT.RECALC, this.onTextRecalc);
|
|
15
|
+
|
|
16
|
+
this.on(this.EVENTS.STATE.CLEAR, this.onStateClear);
|
|
17
|
+
this.on(this.EVENTS.STATE.SET, this.onStateSet);
|
|
18
|
+
};
|
|
19
|
+
init = async () => {
|
|
20
|
+
await this.emit(this.EVENTS.SCENARIO.LINE_EXEC_REG, { module: this.name, handler: this.onLineHandler });
|
|
21
|
+
|
|
22
|
+
this.shared.textNoAnimationSources = [];
|
|
23
|
+
};
|
|
24
|
+
onLineHandler = async (line, args) => {
|
|
25
|
+
const { text } = line.match(this.CONST.TEXT.LINE_REG).groups;
|
|
26
|
+
|
|
27
|
+
await this.emit(this.EVENTS.TEXT.EMIT_BEFORE);
|
|
28
|
+
await this.emit(this.EVENTS.TEXT.EMIT, { text, ...args });
|
|
29
|
+
};
|
|
30
|
+
onTextEmit = async ({ text, ...args }) => {
|
|
31
|
+
const [uid, tokens] = [await this.emitOne(this.EVENTS.VENDORS.UID), textToTokens(await this.getRealText(text))];
|
|
32
|
+
const tokensVisible = this.shared.textNoAnimationSources.length ? BIG_VALUE : 0;
|
|
33
|
+
|
|
34
|
+
this.updateState({ text, tokensVisible, uid, tokens, args });
|
|
35
|
+
|
|
36
|
+
await this.emitTextChanged();
|
|
37
|
+
|
|
38
|
+
if (this.shared.textNoAnimationSources.length) return setImmediate(this.emitAnimationEnd);
|
|
39
|
+
|
|
40
|
+
this.shared.isTextAnimationInProcess = true;
|
|
41
|
+
|
|
42
|
+
await this.emit(this.EVENTS.TEXT.INTERACT, { state: this.CONST.TEXT.INTERACT_STATES.RENDERING });
|
|
43
|
+
await this.waitRender();
|
|
44
|
+
await this.emit(this.EVENTS.TEXT.CICLE, { tokens });
|
|
45
|
+
};
|
|
46
|
+
onTextEmitEnd = async ({ isForced = true } = {}) => {
|
|
47
|
+
await this.emit(this.EVENTS.TEXT.INTERACT, { state: this.CONST.TEXT.INTERACT_STATES.SHOWED });
|
|
48
|
+
|
|
49
|
+
this.updateState({ tokensVisible: isForced ? BIG_VALUE : this.state.tokens.length });
|
|
50
|
+
|
|
51
|
+
if (!this.shared.textNoAnimationSources.length) await this.emitTextChanged();
|
|
52
|
+
this.shared.isTextAnimationInProcess = false;
|
|
53
|
+
};
|
|
54
|
+
onTextRecalc = async () => {
|
|
55
|
+
const [uid, tokens] = [await this.emitOne(this.EVENTS.VENDORS.UID), textToTokens(await this.getRealText(this.state.text))];
|
|
56
|
+
|
|
57
|
+
this.updateState({ tokens, tokensVisible: tokens.length, uid });
|
|
58
|
+
|
|
59
|
+
return this.emitTextChanged();
|
|
60
|
+
};
|
|
61
|
+
onStateSet = ({ [this.name]: state }) => this.setNewState(state);
|
|
62
|
+
onStateClear = () => this.setNewState(this.getDefaultState());
|
|
63
|
+
|
|
64
|
+
setNewState = (state) => this.emit(this.EVENTS.TEXT.EMIT, { text: state.text, ...state.args });
|
|
65
|
+
getRealText = (text) => this.emitOne(this.EVENTS.TEXT.REPLACE, { text, label: this.globalState.scenario.label, state: this.globalState });
|
|
66
|
+
emitAnimationEnd = () => this.emit(this.EVENTS.TEXT.EMIT_END, { isForced: true });
|
|
67
|
+
emitTextChanged = () => this.emit(this.EVENTS.TEXT.CHANGED, this.state);
|
|
68
|
+
|
|
69
|
+
getDefaultState = () => ({ text: "", tokensVisible: 0, uid: "", tokens: [], args: {} });
|
|
70
|
+
}
|
package/modules/utils.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export const textToTokens = (str) => {
|
|
2
|
+
const marks = {};
|
|
3
|
+
const tokens = [];
|
|
4
|
+
|
|
5
|
+
for (let i = 0; i < str.length; ) {
|
|
6
|
+
const curChar = str[i];
|
|
7
|
+
|
|
8
|
+
if (curChar === "{") {
|
|
9
|
+
let j = 0;
|
|
10
|
+
while (true) {
|
|
11
|
+
j++;
|
|
12
|
+
if (str[i + j] === "}") {
|
|
13
|
+
if (str[i + 1] === "/") {
|
|
14
|
+
let mark = str.slice(i + 2, i + j);
|
|
15
|
+
if (mark.includes(":")) {
|
|
16
|
+
mark = mark.split(":")[0];
|
|
17
|
+
}
|
|
18
|
+
delete marks[mark];
|
|
19
|
+
} else {
|
|
20
|
+
let [mark, value] = [str.slice(i + 1, i + j), true];
|
|
21
|
+
if (mark.includes(":")) {
|
|
22
|
+
[mark, value] = mark.split(":");
|
|
23
|
+
}
|
|
24
|
+
marks[mark] = value;
|
|
25
|
+
}
|
|
26
|
+
break;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
i += j + 1;
|
|
30
|
+
} else {
|
|
31
|
+
i++;
|
|
32
|
+
tokens.push({ char: curChar, marks: { ...marks } });
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return tokens;
|
|
37
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vnejs/plugins.text",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"main": "index.js",
|
|
5
|
+
"types": "index.d.ts",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
8
|
+
"publish:major:plugin": "npm run publish:major",
|
|
9
|
+
"publish:minor:plugin": "npm run publish:minor",
|
|
10
|
+
"publish:patch:plugin": "npm run publish:patch",
|
|
11
|
+
"publish:major": "npm version major && npm publish --access public",
|
|
12
|
+
"publish:minor": "npm version minor && npm publish --access public",
|
|
13
|
+
"publish:patch": "npm version patch && npm publish --access public"
|
|
14
|
+
},
|
|
15
|
+
"author": "",
|
|
16
|
+
"license": "ISC",
|
|
17
|
+
"description": ""
|
|
18
|
+
}
|