@ykurilov/agent-chat-flows 0.0.0-stage → 0.55.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/CHANGELOG.md +252 -0
- package/LICENSE +21 -0
- package/README.md +124 -2
- package/agent-flow.schema.json +1020 -0
- package/dist/chunk-3PTJI2XY.js +100 -0
- package/dist/chunk-3PTJI2XY.js.map +1 -0
- package/dist/chunk-BNMQUNSI.js +9 -0
- package/dist/chunk-BNMQUNSI.js.map +1 -0
- package/dist/chunk-BOV6FJGU.js +1288 -0
- package/dist/chunk-BOV6FJGU.js.map +1 -0
- package/dist/chunk-CKY5KG7N.js +165 -0
- package/dist/chunk-CKY5KG7N.js.map +1 -0
- package/dist/chunk-DCTRMCGV.js +34 -0
- package/dist/chunk-DCTRMCGV.js.map +1 -0
- package/dist/format.js +147 -0
- package/dist/format.js.map +1 -0
- package/dist/index.js +3197 -0
- package/dist/index.js.map +1 -0
- package/dist/postgres.js +387 -0
- package/dist/postgres.js.map +1 -0
- package/dist/react.js +2650 -0
- package/dist/react.js.map +1 -0
- package/dist/sqlite.js +319 -0
- package/dist/sqlite.js.map +1 -0
- package/dist/types/check/app-checks.d.ts +16 -0
- package/dist/types/check/check-flow.d.ts +8 -0
- package/dist/types/check/index.d.ts +2 -0
- package/dist/types/engine/data-conditions.d.ts +10 -0
- package/dist/types/engine/day-frame.d.ts +17 -0
- package/dist/types/engine/first-messages.d.ts +21 -0
- package/dist/types/engine/flow-days.d.ts +39 -0
- package/dist/types/engine/flow-graph.d.ts +26 -0
- package/dist/types/engine/flow-run.d.ts +130 -0
- package/dist/types/engine/index.d.ts +14 -0
- package/dist/types/engine/migrate-run.d.ts +11 -0
- package/dist/types/engine/next-transition.d.ts +40 -0
- package/dist/types/engine/nudges.d.ts +78 -0
- package/dist/types/engine/run-updates.d.ts +37 -0
- package/dist/types/engine/side-questions.d.ts +26 -0
- package/dist/types/engine/side-tasks.d.ts +44 -0
- package/dist/types/engine/targets.d.ts +9 -0
- package/dist/types/engine/timers.d.ts +61 -0
- package/dist/types/engine/zoned-time.d.ts +27 -0
- package/dist/types/format/catalog-events.d.ts +5 -0
- package/dist/types/format/compact-flow.d.ts +8 -0
- package/dist/types/format/flow-format.d.ts +286 -0
- package/dist/types/format/flow-json-schema.d.ts +2 -0
- package/dist/types/format/flow-schema.d.ts +605 -0
- package/dist/types/format/index.d.ts +7 -0
- package/dist/types/format/migrate-flow.d.ts +4 -0
- package/dist/types/format/parse-flow.d.ts +6 -0
- package/dist/types/format.d.ts +3 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/labels/client-card.d.ts +20 -0
- package/dist/types/labels/editor.d.ts +261 -0
- package/dist/types/labels/index.d.ts +14 -0
- package/dist/types/labels/migrate.d.ts +19 -0
- package/dist/types/labels/test-chat.d.ts +28 -0
- package/dist/types/not-yet.d.ts +2 -0
- package/dist/types/postgres.d.ts +10 -0
- package/dist/types/prompt/client-card-block.d.ts +16 -0
- package/dist/types/prompt/flow-rules.d.ts +9 -0
- package/dist/types/prompt/rules/after-end.d.ts +3 -0
- package/dist/types/prompt/rules/client-facts.d.ts +2 -0
- package/dist/types/prompt/rules/day-frame.d.ts +2 -0
- package/dist/types/prompt/rules/link-views.d.ts +4 -0
- package/dist/types/prompt/rules/rule-part.d.ts +3 -0
- package/dist/types/prompt/rules/side-tasks.d.ts +2 -0
- package/dist/types/prompt/rules/time-zone.d.ts +2 -0
- package/dist/types/prompt/state-card.d.ts +14 -0
- package/dist/types/react/client-card/client-card-panel.d.ts +8 -0
- package/dist/types/react/client-card/index.d.ts +2 -0
- package/dist/types/react/client-card/use-client-card.d.ts +19 -0
- package/dist/types/react/client-card-skin.d.ts +27 -0
- package/dist/types/react/editor/board-space.d.ts +51 -0
- package/dist/types/react/editor/board-styles.d.ts +1 -0
- package/dist/types/react/editor/editor-model.d.ts +60 -0
- package/dist/types/react/editor/exit-text.d.ts +11 -0
- package/dist/types/react/editor/flow-board.d.ts +11 -0
- package/dist/types/react/editor/flow-editor.d.ts +13 -0
- package/dist/types/react/editor/flow-simulation.d.ts +44 -0
- package/dist/types/react/editor/index.d.ts +6 -0
- package/dist/types/react/editor/schedule-model.d.ts +47 -0
- package/dist/types/react/editor/side-task-model.d.ts +18 -0
- package/dist/types/react/editor/use-flow-editor.d.ts +120 -0
- package/dist/types/react/flow-editor-skin.d.ts +453 -0
- package/dist/types/react/flow-labels.d.ts +8 -0
- package/dist/types/react/index.d.ts +6 -0
- package/dist/types/react/test-chat/flow-test-chat.d.ts +22 -0
- package/dist/types/react/test-chat/index.d.ts +2 -0
- package/dist/types/react/test-chat/use-flow-test-chat.d.ts +50 -0
- package/dist/types/react.d.ts +3 -0
- package/dist/types/runtime/admit.d.ts +15 -0
- package/dist/types/runtime/agent-flow.d.ts +20 -0
- package/dist/types/runtime/client-card.d.ts +9 -0
- package/dist/types/runtime/client-facts.d.ts +22 -0
- package/dist/types/runtime/close-day-tool.d.ts +3 -0
- package/dist/types/runtime/decisions-client.d.ts +33 -0
- package/dist/types/runtime/extra-tool.d.ts +16 -0
- package/dist/types/runtime/first-message-retry.d.ts +4 -0
- package/dist/types/runtime/first-message-rules.d.ts +55 -0
- package/dist/types/runtime/first-reasons-notice.d.ts +9 -0
- package/dist/types/runtime/flow-delivery.d.ts +53 -0
- package/dist/types/runtime/flow-events.d.ts +126 -0
- package/dist/types/runtime/flow-finish.d.ts +68 -0
- package/dist/types/runtime/flow-known-texts.d.ts +11 -0
- package/dist/types/runtime/flow-signals.d.ts +12 -0
- package/dist/types/runtime/flow-timers.d.ts +55 -0
- package/dist/types/runtime/flow-tools.d.ts +23 -0
- package/dist/types/runtime/flow-types.d.ts +547 -0
- package/dist/types/runtime/held-messages.d.ts +25 -0
- package/dist/types/runtime/index.d.ts +11 -0
- package/dist/types/runtime/judge-context.d.ts +36 -0
- package/dist/types/runtime/judge.d.ts +21 -0
- package/dist/types/runtime/migrate-chats.d.ts +20 -0
- package/dist/types/runtime/note-tools.d.ts +15 -0
- package/dist/types/runtime/pause.d.ts +21 -0
- package/dist/types/runtime/prepare-turn.d.ts +12 -0
- package/dist/types/runtime/side-task-tool.d.ts +5 -0
- package/dist/types/runtime/step-tail.d.ts +11 -0
- package/dist/types/runtime/tail/day-line.d.ts +2 -0
- package/dist/types/runtime/tail/frame-line.d.ts +2 -0
- package/dist/types/runtime/tail/side-tasks-line.d.ts +2 -0
- package/dist/types/runtime/tail/tail-part.d.ts +21 -0
- package/dist/types/runtime/test-clock.d.ts +26 -0
- package/dist/types/runtime/time-zone-tool.d.ts +3 -0
- package/dist/types/runtime/turn-gate.d.ts +8 -0
- package/dist/types/sqlite.d.ts +9 -0
- package/dist/types/store/flow-store-shared.d.ts +20 -0
- package/dist/types/store/flow-store.d.ts +240 -0
- package/dist/types/store/memory.d.ts +3 -0
- package/dist/types/store/migration-settings.d.ts +7 -0
- package/dist/types/store/postgres-schema.d.ts +775 -0
- package/dist/types/store/postgres.d.ts +15 -0
- package/dist/types/store/sqlite-schema.d.ts +824 -0
- package/dist/types/store/sqlite.d.ts +9 -0
- package/drizzle/postgres/0000_init.sql +45 -0
- package/drizzle/postgres/0001_days_pause_side_tasks.sql +21 -0
- package/drizzle/postgres/0002_chat_placement.sql +1 -0
- package/drizzle/postgres/meta/0000_snapshot.json +317 -0
- package/drizzle/postgres/meta/0001_snapshot.json +439 -0
- package/drizzle/postgres/meta/0002_snapshot.json +445 -0
- package/drizzle/postgres/meta/_journal.json +27 -0
- package/drizzle/sqlite/0000_init.sql +44 -0
- package/drizzle/sqlite/0001_days_pause_side_tasks.sql +21 -0
- package/drizzle/sqlite/0002_chat_placement.sql +1 -0
- package/drizzle/sqlite/meta/0000_snapshot.json +297 -0
- package/drizzle/sqlite/meta/0001_snapshot.json +427 -0
- package/drizzle/sqlite/meta/0002_snapshot.json +434 -0
- package/drizzle/sqlite/meta/_journal.json +27 -0
- package/package.json +98 -3
package/dist/react.js
ADDED
|
@@ -0,0 +1,2650 @@
|
|
|
1
|
+
import {
|
|
2
|
+
FACT_PREFIX,
|
|
3
|
+
fieldValueText
|
|
4
|
+
} from "./chunk-3PTJI2XY.js";
|
|
5
|
+
import {
|
|
6
|
+
ANY_STATE_LAYOUT_KEY,
|
|
7
|
+
DEFAULT_LATE_START_HOURS,
|
|
8
|
+
DEFAULT_SIDE_TASK_MAX_ATTEMPTS,
|
|
9
|
+
FLOW_DATA_OPS,
|
|
10
|
+
FLOW_OPS_BY_FIELD_TYPE,
|
|
11
|
+
RETURN_TARGET,
|
|
12
|
+
activeExits,
|
|
13
|
+
appIssues,
|
|
14
|
+
applyTransition,
|
|
15
|
+
catalogEventLabel,
|
|
16
|
+
catalogEventNames,
|
|
17
|
+
checkFlow,
|
|
18
|
+
compactFlow,
|
|
19
|
+
fieldById,
|
|
20
|
+
isFieldStale,
|
|
21
|
+
isFinal,
|
|
22
|
+
nextTransition,
|
|
23
|
+
noteAgentAnswer,
|
|
24
|
+
noteHumanMessage,
|
|
25
|
+
noteSideQuestion,
|
|
26
|
+
parseFlow,
|
|
27
|
+
sideQuestion,
|
|
28
|
+
startRun,
|
|
29
|
+
stateById,
|
|
30
|
+
timerDueAt
|
|
31
|
+
} from "./chunk-BOV6FJGU.js";
|
|
32
|
+
|
|
33
|
+
// src/react/flow-labels.ts
|
|
34
|
+
import { createContext, createElement, useContext } from "react";
|
|
35
|
+
|
|
36
|
+
// src/labels/client-card.ts
|
|
37
|
+
var russianClientCardLabels = {
|
|
38
|
+
title: "\u0427\u0442\u043E \u0430\u0433\u0435\u043D\u0442 \u0437\u043D\u0430\u0435\u0442 \u043E \u0432\u0430\u0441",
|
|
39
|
+
empty: "\u0410\u0433\u0435\u043D\u0442 \u043F\u043E\u043A\u0430 \u043D\u0438\u0447\u0435\u0433\u043E \u043E \u0432\u0430\u0441 \u043D\u0435 \u0437\u0430\u043F\u043E\u043C\u043D\u0438\u043B.",
|
|
40
|
+
updatedAt: (date) => `\u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u043E ${date}`,
|
|
41
|
+
stale: "\u043C\u043E\u0436\u0435\u0442 \u0431\u044B\u0442\u044C \u0443\u0436\u0435 \u043D\u0435 \u0442\u0430\u043A",
|
|
42
|
+
fact: "\u0424\u0430\u043A\u0442",
|
|
43
|
+
forgetField: "\u0421\u0442\u0435\u0440\u0435\u0442\u044C",
|
|
44
|
+
forgetAll: "\u0421\u0442\u0435\u0440\u0435\u0442\u044C \u0432\u0441\u0451",
|
|
45
|
+
forgetAllConfirm: "\u0421\u0442\u0435\u0440\u0435\u0442\u044C \u0432\u0441\u0451, \u0447\u0442\u043E \u0430\u0433\u0435\u043D\u0442 \u0437\u043D\u0430\u0435\u0442 \u043E \u0432\u0430\u0441? \u0412\u0435\u0440\u043D\u0443\u0442\u044C \u043D\u0435 \u043F\u043E\u043B\u0443\u0447\u0438\u0442\u0441\u044F.",
|
|
46
|
+
confirm: "\u0421\u0442\u0435\u0440\u0435\u0442\u044C",
|
|
47
|
+
cancel: "\u041E\u0442\u043C\u0435\u043D\u0430",
|
|
48
|
+
loading: "\u0417\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u043C\u2026",
|
|
49
|
+
failed: "\u041D\u0435 \u043F\u043E\u043B\u0443\u0447\u0438\u043B\u043E\u0441\u044C \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C. \u041F\u043E\u043F\u0440\u043E\u0431\u0443\u0439\u0442\u0435 \u0435\u0449\u0451 \u0440\u0430\u0437",
|
|
50
|
+
retry: "\u041F\u043E\u0432\u0442\u043E\u0440\u0438\u0442\u044C"
|
|
51
|
+
};
|
|
52
|
+
var englishClientCardLabels = {
|
|
53
|
+
title: "What the agent knows about you",
|
|
54
|
+
empty: "The agent has not remembered anything about you yet.",
|
|
55
|
+
updatedAt: (date) => `updated ${date}`,
|
|
56
|
+
stale: "may be out of date",
|
|
57
|
+
fact: "Fact",
|
|
58
|
+
forgetField: "Erase",
|
|
59
|
+
forgetAll: "Erase everything",
|
|
60
|
+
forgetAllConfirm: "Erase everything the agent knows about you? This cannot be undone.",
|
|
61
|
+
confirm: "Erase",
|
|
62
|
+
cancel: "Cancel",
|
|
63
|
+
loading: "Loading\u2026",
|
|
64
|
+
failed: "Could not load. Try again",
|
|
65
|
+
retry: "Retry"
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
// src/labels/editor.ts
|
|
69
|
+
var russianFlowEditorLabels = {
|
|
70
|
+
conditionKinds: { judge: "\u0421\u043B\u043E\u0432\u0430\u043C\u0438", data: "\u0414\u0430\u043D\u043D\u044B\u0435", event: "\u0421\u043E\u0431\u044B\u0442\u0438\u0435", timer: "\u0422\u0430\u0439\u043C\u0435\u0440" },
|
|
71
|
+
conditionKindHints: {
|
|
72
|
+
judge: "\u0421\u043B\u043E\u0432\u0430\u043C\u0438 \u2014 \u0440\u0435\u0448\u0430\u0435\u0442 \u0430\u0433\u0435\u043D\u0442, \u043F\u0440\u043E\u0432\u0435\u0440\u044F\u0435\u0442 \u0441\u0443\u0434\u044C\u044F",
|
|
73
|
+
data: "\u041F\u043E \u0434\u0430\u043D\u043D\u044B\u043C \u2014 \u043F\u043E\u043B\u0435 \u0437\u0430\u043F\u043E\u043B\u043D\u0435\u043D\u043E, \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u0440\u0430\u0432\u043D\u043E\u2026",
|
|
74
|
+
event: "\u0421\u043E\u0431\u044B\u0442\u0438\u0435 \u2014 \u043A\u043D\u043E\u043F\u043A\u0430, \u0438\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442, \u0441\u0438\u0433\u043D\u0430\u043B \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044F",
|
|
75
|
+
timer: "\u0422\u0430\u0439\u043C\u0435\u0440 \u2014 \u043F\u043E \u0447\u0430\u0441\u0430\u043C"
|
|
76
|
+
},
|
|
77
|
+
returnKind: "\u041D\u0430\u0437\u0430\u0434",
|
|
78
|
+
returnTarget: "\u043D\u0430\u0437\u0430\u0434, \u0433\u0434\u0435 \u0431\u044B\u043B",
|
|
79
|
+
ops: { filled: "\u0437\u0430\u043F\u043E\u043B\u043D\u0435\u043D\u043E", eq: "=", ne: "\u2260", gt: ">", lt: "<", contains: "\u0441\u043E\u0434\u0435\u0440\u0436\u0438\u0442" },
|
|
80
|
+
events: { message: "\u041F\u0440\u0438\u0448\u043B\u043E \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435", button: "\u041D\u0430\u0436\u0430\u0442\u0430 \u043A\u043D\u043E\u043F\u043A\u0430", tool: "\u0412\u044B\u0437\u0432\u0430\u043D \u0438\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442", app: "\u0421\u043E\u0431\u044B\u0442\u0438\u0435 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044F" },
|
|
81
|
+
timerSpans: {
|
|
82
|
+
in_state: "\u0412\u0440\u0435\u043C\u044F \u0432 \u044D\u0442\u043E\u043C \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0438",
|
|
83
|
+
silence: "\u0422\u0438\u0448\u0438\u043D\u0430 \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0430",
|
|
84
|
+
since_start: "\u041F\u0440\u043E\u0448\u043B\u043E \u0441 \u043D\u0430\u0447\u0430\u043B\u0430 \u0447\u0430\u0442\u0430",
|
|
85
|
+
time_of_day: "\u041D\u0430\u0441\u0442\u0443\u043F\u0438\u043B\u043E \u0432\u0440\u0435\u043C\u044F \u0441\u0443\u0442\u043E\u043A"
|
|
86
|
+
},
|
|
87
|
+
units: { min: "\u043C\u0438\u043D", h: "\u0447", d: "\u0434\u043D." },
|
|
88
|
+
fieldTypes: { text: "\u0422\u0435\u043A\u0441\u0442", number: "\u0427\u0438\u0441\u043B\u043E", choice: "\u0412\u044B\u0431\u043E\u0440", boolean: "\u0414\u0430/\u043D\u0435\u0442" },
|
|
89
|
+
yes: "\u0434\u0430",
|
|
90
|
+
no: "\u043D\u0435\u0442",
|
|
91
|
+
days: (count) => {
|
|
92
|
+
const tens = count % 100;
|
|
93
|
+
const ones = count % 10;
|
|
94
|
+
const word = tens >= 11 && tens <= 14 ? "\u0434\u043D\u0435\u0439" : ones === 1 ? "\u0434\u0435\u043D\u044C" : ones >= 2 && ones <= 4 ? "\u0434\u043D\u044F" : "\u0434\u043D\u0435\u0439";
|
|
95
|
+
return `${count} ${word}`;
|
|
96
|
+
},
|
|
97
|
+
programDay: (day) => `\u0441 \u0434\u043D\u044F ${day}`,
|
|
98
|
+
header: {
|
|
99
|
+
state: "\u0421\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0435",
|
|
100
|
+
card: "\u041A\u0430\u0440\u0442\u043E\u0447\u043A\u0430 \u0438 \u0437\u0430\u043C\u0435\u0442\u043A\u0438",
|
|
101
|
+
check: "\u041F\u0440\u043E\u0432\u0435\u0440\u0438\u0442\u044C \u0433\u0440\u0430\u0444",
|
|
102
|
+
run: "\u041F\u0440\u043E\u0433\u043E\u043D",
|
|
103
|
+
json: "JSON",
|
|
104
|
+
save: "\u0421\u043E\u0445\u0440\u0430\u043D\u0438\u0442\u044C",
|
|
105
|
+
saved: "\u0421\u043E\u0445\u0440\u0430\u043D\u0435\u043D\u043E",
|
|
106
|
+
unsaved: "\u0415\u0441\u0442\u044C \u043D\u0435\u0441\u043E\u0445\u0440\u0430\u043D\u0451\u043D\u043D\u044B\u0435 \u043F\u0440\u0430\u0432\u043A\u0438",
|
|
107
|
+
saveFailed: "\u041D\u0435 \u0441\u043E\u0445\u0440\u0430\u043D\u0435\u043D\u043E",
|
|
108
|
+
saveFailedHint: "\u0421\u043E\u0445\u0440\u0430\u043D\u0435\u043D\u0438\u0435 \u043E\u0442\u043A\u043B\u043E\u043D\u0435\u043D\u043E. \u0427\u0442\u043E \u043D\u0435 \u0442\u0430\u043A \u2014 \u0432 \xAB\u041F\u0440\u043E\u0432\u0435\u0440\u0438\u0442\u044C \u0433\u0440\u0430\u0444\xBB \u0438 \u043D\u0430 \u043D\u043E\u0434\u0430\u0445.",
|
|
109
|
+
readOnly: "\u0422\u043E\u043B\u044C\u043A\u043E \u043F\u0440\u043E\u0441\u043C\u043E\u0442\u0440",
|
|
110
|
+
readOnlyHint: "\u0421\u0446\u0435\u043D\u0430\u0440\u0438\u0439 \u0441\u0435\u0439\u0447\u0430\u0441 \u043F\u0440\u0430\u0432\u0438\u0442 \u0430\u0433\u0435\u043D\u0442. \u0421\u043C\u043E\u0442\u0440\u0435\u0442\u044C \u0438 \u043F\u0440\u043E\u0433\u043E\u043D\u044F\u0442\u044C \u043C\u043E\u0436\u043D\u043E, \u043F\u0440\u0430\u0432\u0438\u0442\u044C \u043D\u0435\u043B\u044C\u0437\u044F.",
|
|
111
|
+
migrate: "\u041F\u0435\u0440\u0435\u0432\u0435\u0441\u0442\u0438 \u0447\u0430\u0442\u044B"
|
|
112
|
+
},
|
|
113
|
+
board: {
|
|
114
|
+
anyState: "\u0418\u0437 \u043B\u044E\u0431\u043E\u0433\u043E \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u044F",
|
|
115
|
+
start: "\u0421\u0446\u0435\u043D\u0430\u0440\u0438\u0439 \u043D\u0430\u0447\u0438\u043D\u0430\u0435\u0442\u0441\u044F \u0437\u0434\u0435\u0441\u044C",
|
|
116
|
+
final: "\u041A\u043E\u043D\u0435\u0446",
|
|
117
|
+
noSideQuestions: "\u0411\u0435\u0437 \u0432\u043E\u043F\u0440\u043E\u0441\u043E\u0432 \u043E \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0435",
|
|
118
|
+
noTarget: "\u043A\u0443\u0434\u0430 \u2014 \u043D\u0435 \u0432\u044B\u0431\u0440\u0430\u043D\u043E",
|
|
119
|
+
emptyCondition: "\u0443\u0441\u043B\u043E\u0432\u0438\u0435 \u043D\u0435 \u043D\u0430\u043F\u0438\u0441\u0430\u043D\u043E",
|
|
120
|
+
newState: "\u041D\u043E\u0432\u043E\u0435 \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0435",
|
|
121
|
+
zoomIn: "\u041A\u0440\u0443\u043F\u043D\u0435\u0435",
|
|
122
|
+
zoomOut: "\u041C\u0435\u043B\u044C\u0447\u0435",
|
|
123
|
+
fit: "\u0412\u0435\u0441\u044C \u0433\u0440\u0430\u0444",
|
|
124
|
+
errors: (count) => `\u041E\u0448\u0438\u0431\u043E\u043A: ${count}`,
|
|
125
|
+
warnings: (count) => `\u0417\u0430\u043C\u0435\u0447\u0430\u043D\u0438\u0439: ${count}`,
|
|
126
|
+
nudges: "\u043A\u0430\u0441\u0430\u043D\u0438\u044F",
|
|
127
|
+
noNudges: "\u0431\u0435\u0437 \u043A\u0430\u0441\u0430\u043D\u0438\u0439",
|
|
128
|
+
nudgesDays: (days) => days === 1 ? "\u0432 \u0434\u0435\u043D\u044C \u0432\u0445\u043E\u0434\u0430" : `\u043F\u0435\u0440\u0432\u044B\u0435 ${days} \u0434\u043D.`,
|
|
129
|
+
space: {
|
|
130
|
+
legend: "\u041B\u0435\u0433\u0435\u043D\u0434\u0430",
|
|
131
|
+
showMinimap: "\u041F\u043E\u043A\u0430\u0437\u0430\u0442\u044C \u043C\u0438\u043D\u0438-\u043A\u0430\u0440\u0442\u0443",
|
|
132
|
+
hideMinimap: "\u0421\u043A\u0440\u044B\u0442\u044C \u043C\u0438\u043D\u0438-\u043A\u0430\u0440\u0442\u0443",
|
|
133
|
+
showInspector: "\u041F\u043E\u043A\u0430\u0437\u0430\u0442\u044C \u043F\u0430\u043D\u0435\u043B\u044C \xAB\u0421\u0446\u0435\u043D\u0430\u0440\u0438\u0439\xBB",
|
|
134
|
+
hideInspector: "\u0421\u043A\u0440\u044B\u0442\u044C \u043F\u0430\u043D\u0435\u043B\u044C \xAB\u0421\u0446\u0435\u043D\u0430\u0440\u0438\u0439\xBB",
|
|
135
|
+
enterFullscreen: "\u041D\u0430 \u0432\u0435\u0441\u044C \u044D\u043A\u0440\u0430\u043D",
|
|
136
|
+
exitFullscreen: "\u0412\u044B\u0439\u0442\u0438 \u0438\u0437 \u043F\u043E\u043B\u043D\u043E\u0433\u043E \u044D\u043A\u0440\u0430\u043D\u0430",
|
|
137
|
+
fullscreenRegion: "\u0420\u0435\u0434\u0430\u043A\u0442\u043E\u0440 \u0441\u0446\u0435\u043D\u0430\u0440\u0438\u044F \u043D\u0430 \u0432\u0435\u0441\u044C \u044D\u043A\u0440\u0430\u043D"
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
overview: {
|
|
141
|
+
title: "\u0421\u0446\u0435\u043D\u0430\u0440\u0438\u0439",
|
|
142
|
+
common: "\u041E\u0431\u0449\u0435\u0435 \u0434\u043B\u044F \u0432\u0441\u0435\u0445 \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0439",
|
|
143
|
+
commonHint: "\u0420\u043E\u043B\u044C, \u0442\u043E\u043D, \u0446\u0435\u043D\u044B, \u0443\u0441\u043B\u043E\u0432\u0438\u044F, \u0441\u0441\u044B\u043B\u043A\u0438 \u2014 \u0442\u043E, \u0447\u0442\u043E \u0432\u0435\u0440\u043D\u043E \u0432 \u043B\u044E\u0431\u043E\u043C \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0438.",
|
|
144
|
+
states: "\u0421\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0439",
|
|
145
|
+
exits: "\u0423\u0441\u043B\u043E\u0432\u0438\u0439 \u0432\u044B\u0445\u043E\u0434\u0430",
|
|
146
|
+
finals: "\u041A\u043E\u043D\u0435\u0447\u043D\u044B\u0445",
|
|
147
|
+
lists: "\u0421\u043F\u0438\u0441\u043A\u0438",
|
|
148
|
+
help: "\u041A\u0430\u043A \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u044C\u0441\u044F",
|
|
149
|
+
flowIssues: "\u0412\u0435\u0441\u044C \u0441\u0446\u0435\u043D\u0430\u0440\u0438\u0439",
|
|
150
|
+
afterEnd: "\u041F\u043E\u0441\u043B\u0435 \u043A\u043E\u043D\u0446\u0430 \u0440\u0430\u0437\u0433\u043E\u0432\u043E\u0440\u0430",
|
|
151
|
+
afterEndModes: { reply: "\u041E\u0442\u0432\u0435\u0447\u0430\u0442\u044C", silent: "\u041D\u0435 \u043E\u0442\u0432\u0435\u0447\u0430\u0442\u044C" },
|
|
152
|
+
afterEndHints: {
|
|
153
|
+
reply: "\u0410\u0433\u0435\u043D\u0442 \u043E\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u043F\u043E \u043E\u0431\u0449\u0435\u0439 \u0447\u0430\u0441\u0442\u0438: \u0437\u043D\u0430\u0435\u0442, \u0447\u0442\u043E \u043F\u0440\u043E\u0433\u0440\u0430\u043C\u043C\u0430 \u0437\u0430\u043A\u043E\u043D\u0447\u0438\u043B\u0430\u0441\u044C, \u043D\u0435 \u0432\u0435\u0434\u0451\u0442 \u043F\u043E \u0448\u0430\u0433\u0430\u043C \u0438 \u043D\u0435 \u043F\u0440\u043E\u0434\u0430\u0451\u0442 \u043F\u043E\u0432\u0442\u043E\u0440\u043D\u043E.",
|
|
154
|
+
silent: "\u0410\u0433\u0435\u043D\u0442 \u043C\u043E\u043B\u0447\u0438\u0442, \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435 \u043F\u043E\u043B\u0443\u0447\u0430\u0435\u0442 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0435."
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
schedule: {
|
|
158
|
+
title: "\u0414\u043D\u0438 \u0438 \u0432\u0440\u0435\u043C\u044F",
|
|
159
|
+
on: "\u0421\u0446\u0435\u043D\u0430\u0440\u0438\u0439 \u043F\u043E \u0434\u043D\u044F\u043C",
|
|
160
|
+
hint: "\u0414\u043D\u0438, \u043D\u043E\u0447\u044C, \u043E\u0431\u0449\u0438\u0439 \u0441\u0440\u043E\u043A \u0438 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u044F \u043F\u0435\u0440\u0432\u044B\u043C. \u041D\u043E\u043C\u0435\u0440 \u0434\u043D\u044F \u0430\u0433\u0435\u043D\u0442 \u0432\u0438\u0434\u0438\u0442 \u0432 \u043A\u0430\u0436\u0434\u043E\u043C \u0445\u043E\u0434\u0435.",
|
|
161
|
+
dayStart: "\u041D\u0430\u0447\u0430\u043B\u043E \u0434\u043D\u044F",
|
|
162
|
+
dayStartHint: "\u0412 \u044D\u0442\u043E \u0432\u0440\u0435\u043C\u044F \u2014 \u0443\u0442\u0440\u0435\u043D\u043D\u0435\u0435 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435.",
|
|
163
|
+
lengthDays: "\u0421\u043A\u043E\u043B\u044C\u043A\u043E \u0434\u043D\u0435\u0439",
|
|
164
|
+
lengthDaysNone: "\u0431\u0435\u0437 \u0441\u0440\u043E\u043A\u0430",
|
|
165
|
+
nightFrom: "\u041D\u043E\u0447\u044C \u0441",
|
|
166
|
+
nightFromHint: "\u041D\u043E\u0447\u044C\u044E \u0430\u0433\u0435\u043D\u0442 \u043F\u0435\u0440\u0432\u044B\u043C \u043D\u0435 \u043F\u0438\u0448\u0435\u0442. \u041F\u0443\u0441\u0442\u043E \u2014 \u043A\u0430\u043A \u0432 \u043D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0430\u0445 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044F.",
|
|
167
|
+
lateStart: "\u041F\u043E\u0437\u0434\u043D\u0438\u0439 \u0441\u0442\u0430\u0440\u0442, \u0447",
|
|
168
|
+
lateStartHint: "\u041F\u0440\u0438\u0448\u0451\u043B \u043C\u0435\u043D\u044C\u0448\u0435 \u0447\u0435\u043C \u0437\u0430 \u0441\u0442\u043E\u043B\u044C\u043A\u043E \u0447\u0430\u0441\u043E\u0432 \u0434\u043E \u043D\u043E\u0447\u0438 \u2014 \u043F\u0435\u0440\u0432\u044B\u0439 \u0434\u0435\u043D\u044C \u0437\u0430\u0432\u0442\u0440\u0430. 0 \u2014 \u0434\u0435\u043D\u044C \u043F\u0440\u0438\u0445\u043E\u0434\u0430 \u043F\u0435\u0440\u0432\u044B\u0439.",
|
|
169
|
+
deadlineState: "\u0421\u0440\u043E\u043A \u0432\u044B\u0448\u0435\u043B \u2014 \u043F\u0435\u0440\u0435\u0439\u0442\u0438 \u0432",
|
|
170
|
+
deadlineNone: "\u0441\u0440\u0430\u0437\u0443 \u0437\u0430\u043A\u043E\u043D\u0447\u0438\u0442\u044C",
|
|
171
|
+
farewell: "\u041F\u043E\u043F\u0440\u043E\u0449\u0430\u0442\u044C\u0441\u044F, \u043A\u043E\u0433\u0434\u0430 \u0441\u0440\u043E\u043A \u0432\u044B\u0448\u0435\u043B",
|
|
172
|
+
farewellText: "\u0413\u043E\u0442\u043E\u0432\u044B\u0439 \u0442\u0435\u043A\u0441\u0442 \u043F\u0440\u043E\u0449\u0430\u043D\u0438\u044F",
|
|
173
|
+
farewellTextHint: "\u041F\u0443\u0441\u0442\u043E \u2014 \u043F\u0440\u043E\u0449\u0430\u043D\u0438\u0435 \u043D\u0430\u043F\u0438\u0448\u0435\u0442 \u0430\u0433\u0435\u043D\u0442.",
|
|
174
|
+
firstMessages: "\u0421\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u044F \u043F\u0435\u0440\u0432\u044B\u043C",
|
|
175
|
+
maxPerDay: "\u041D\u0435 \u0431\u043E\u043B\u044C\u0448\u0435 \u0432 \u0434\u0435\u043D\u044C",
|
|
176
|
+
maxPerDayHint: "\u041F\u0443\u0441\u0442\u043E \u2014 \u043A\u0430\u043A \u0432 \u043D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0430\u0445 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044F.",
|
|
177
|
+
morning: "\u0423\u0442\u0440\u0435\u043D\u043D\u0435\u0435 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435",
|
|
178
|
+
skipMorning: "\u041D\u0435 \u043F\u0438\u0441\u0430\u0442\u044C \u0443\u0442\u0440\u043E\u043C, \u0435\u0441\u043B\u0438 \u0447\u0435\u043B\u043E\u0432\u0435\u043A \u043F\u0438\u0441\u0430\u043B \u0437\u0430, \u043C\u0438\u043D",
|
|
179
|
+
nudges: "\u041A\u0430\u0441\u0430\u043D\u0438\u044F \u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E",
|
|
180
|
+
nudgesHint: "\u0410\u0433\u0435\u043D\u0442 \u043F\u0438\u0448\u0435\u0442 \u0432 \u0442\u043E\u0439 \u0436\u0435 \u043D\u043E\u0434\u0435, \u043A\u043E\u0433\u0434\u0430 \u0447\u0435\u043B\u043E\u0432\u0435\u043A \u043C\u043E\u043B\u0447\u0438\u0442. \u0423 \u043D\u043E\u0434\u044B \u043C\u043E\u0436\u043D\u043E \u0437\u0430\u0434\u0430\u0442\u044C \u0441\u0432\u043E\u0438.",
|
|
181
|
+
dayFrame: "\u0420\u0430\u043C\u043A\u0438 \u0434\u043D\u044F",
|
|
182
|
+
dayFrameHint: "\u041F\u043E \u0447\u0438\u0441\u043B\u0443 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0439 \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0430 \u0437\u0430 \u0434\u0435\u043D\u044C.",
|
|
183
|
+
softFrom: "\u0421\u0432\u043E\u0440\u0430\u0447\u0438\u0432\u0430\u0442\u044C \u0441 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u044F \u2116",
|
|
184
|
+
hardAt: "\u0417\u0430\u043A\u043E\u043D\u0447\u0438\u0442\u044C \u0434\u0435\u043D\u044C \u043D\u0430 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0438 \u2116",
|
|
185
|
+
hardText: "\u041E\u0442\u0432\u0435\u0442, \u043A\u043E\u0433\u0434\u0430 \u0434\u0435\u043D\u044C \u0437\u0430\u043A\u043E\u043D\u0447\u0435\u043D",
|
|
186
|
+
hardTextHint: "\u0423\u0445\u043E\u0434\u0438\u0442 \u0441\u043B\u043E\u0432\u043E \u0432 \u0441\u043B\u043E\u0432\u043E, \u0431\u0435\u0437 \u043C\u043E\u0434\u0435\u043B\u0438.",
|
|
187
|
+
more: "\u0414\u043E\u043F\u043E\u043B\u043D\u0438\u0442\u0435\u043B\u044C\u043D\u043E"
|
|
188
|
+
},
|
|
189
|
+
nudges: {
|
|
190
|
+
afterSilence: "\u041C\u043E\u043B\u0447\u0438\u0442 \u2014 \u043D\u0430\u043F\u0438\u0441\u0430\u0442\u044C \u0447\u0435\u0440\u0435\u0437",
|
|
191
|
+
addStep: "\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0441\u0440\u043E\u043A",
|
|
192
|
+
dailyAt: "\u041F\u043E\u0442\u043E\u043C \u043A\u0430\u0436\u0434\u044B\u0439 \u0434\u0435\u043D\u044C \u0432",
|
|
193
|
+
addTime: "\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0432\u0440\u0435\u043C\u044F",
|
|
194
|
+
giveUpAfter: "\u041C\u043E\u043B\u0447\u0438\u0442 \u0441\u0442\u043E\u043B\u044C\u043A\u043E \u2014 \u0440\u0430\u0437\u0433\u043E\u0432\u043E\u0440 \u043E\u043A\u043E\u043D\u0447\u0435\u043D",
|
|
195
|
+
never: "\u043D\u0438\u043A\u043E\u0433\u0434\u0430",
|
|
196
|
+
remove: "\u0423\u0431\u0440\u0430\u0442\u044C",
|
|
197
|
+
on: "\u041A\u0430\u0441\u0430\u043D\u0438\u044F \u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E"
|
|
198
|
+
},
|
|
199
|
+
sideTasks: {
|
|
200
|
+
title: "\u041F\u043E\u043F\u0443\u0442\u043D\u044B\u0435 \u0437\u0430\u0434\u0430\u0447\u0438",
|
|
201
|
+
hint: "\u0410\u0433\u0435\u043D\u0442 \u0434\u0435\u043B\u0430\u0435\u0442 \u0438\u0445 \u043C\u0435\u0436\u0434\u0443 \u0434\u0435\u043B\u043E\u043C, \u0432 \u0442\u0435\u043C\u043F\u0435 \u0432\u043E\u043F\u0440\u043E\u0441\u043E\u0432 \u043E \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0435. \u041D\u0435 \u0431\u043E\u043B\u044C\u0448\u0435 \u0437\u0430\u0434\u0430\u043D\u043D\u044B\u0445 \u043F\u043E\u043F\u044B\u0442\u043E\u043A.",
|
|
202
|
+
builtIn: "\xAB\u0423\u0437\u043D\u0430\u0442\u044C \u0433\u043E\u0440\u043E\u0434\xBB \u2014 \u0432\u0441\u0442\u0440\u043E\u0435\u043D\u043D\u0430\u044F \u0437\u0430\u0434\u0430\u0447\u0430: \u0435\u0441\u0442\u044C \u0432\u0441\u0435\u0433\u0434\u0430, \u043A\u043E\u0433\u0434\u0430 \u0437\u0430\u0434\u0430\u043D\u044B \xAB\u0414\u043D\u0438 \u0438 \u0432\u0440\u0435\u043C\u044F\xBB.",
|
|
203
|
+
text: "\u0427\u0442\u043E \u0441\u0434\u0435\u043B\u0430\u0442\u044C",
|
|
204
|
+
when: "\u041A\u043E\u0433\u0434\u0430 \u0443\u043C\u0435\u0441\u0442\u043D\u043E",
|
|
205
|
+
field: "\u0420\u0435\u0448\u0435\u043D\u0430, \u043A\u043E\u0433\u0434\u0430 \u0438\u0437\u0432\u0435\u0441\u0442\u043D\u043E \u043F\u043E\u043B\u0435",
|
|
206
|
+
noField: "\u2014",
|
|
207
|
+
fromApp: "\u041F\u043E\u043B\u044F \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044F",
|
|
208
|
+
maxAttempts: "\u041F\u043E\u043F\u044B\u0442\u043E\u043A",
|
|
209
|
+
perConversation: "\u0417\u0430\u043D\u043E\u0432\u043E \u0432 \u043A\u0430\u0436\u0434\u043E\u043C \u0440\u0430\u0437\u0433\u043E\u0432\u043E\u0440\u0435",
|
|
210
|
+
add: "\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0437\u0430\u0434\u0430\u0447\u0443",
|
|
211
|
+
newTask: "",
|
|
212
|
+
delete: "\u0423\u0434\u0430\u043B\u0438\u0442\u044C \u0437\u0430\u0434\u0430\u0447\u0443"
|
|
213
|
+
},
|
|
214
|
+
statePanel: {
|
|
215
|
+
title: "\u0421\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0435",
|
|
216
|
+
name: "\u041D\u0430\u0437\u0432\u0430\u043D\u0438\u0435",
|
|
217
|
+
instructions: "\u0427\u0442\u043E \u0430\u0433\u0435\u043D\u0442 \u0437\u0434\u0435\u0441\u044C \u0434\u0435\u043B\u0430\u0435\u0442",
|
|
218
|
+
instructionsHint: "\u041F\u0435\u0440\u0432\u0430\u044F \u0441\u0442\u0440\u043E\u043A\u0430 \u2014 \u0446\u0435\u043B\u044C, \u0435\u0451 \u0432\u0438\u0434\u043D\u043E \u043D\u0430 \u0434\u043E\u0441\u043A\u0435. \u0414\u0430\u043B\u044C\u0448\u0435 \u2014 \u043A\u0430\u043A \u0441\u0435\u0431\u044F \u0432\u0435\u0441\u0442\u0438.",
|
|
219
|
+
firstMessage: "\u041F\u0435\u0440\u0432\u043E\u0435 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435",
|
|
220
|
+
learn: "\u0423\u0437\u043D\u0430\u0442\u044C \u0432 \u044D\u0442\u043E\u043C \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0438",
|
|
221
|
+
tools: "\u0418\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442\u044B",
|
|
222
|
+
toolsHint: "\u0410\u0433\u0435\u043D\u0442\u0443 \u0437\u0434\u0435\u0441\u044C \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u044B \u0442\u043E\u043B\u044C\u043A\u043E \u043E\u0442\u043C\u0435\u0447\u0435\u043D\u043D\u044B\u0435.",
|
|
223
|
+
noTools: "\u0412 \u0441\u0446\u0435\u043D\u0430\u0440\u0438\u0438 \u043F\u043E\u043A\u0430 \u043D\u0435\u0442 \u0438\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442\u043E\u0432.",
|
|
224
|
+
tags: "\u041C\u0435\u0442\u043A\u0438",
|
|
225
|
+
makeStart: "\u041D\u0430\u0447\u0438\u043D\u0430\u0442\u044C \u043E\u0442\u0441\u044E\u0434\u0430",
|
|
226
|
+
isStart: "\u0421\u0446\u0435\u043D\u0430\u0440\u0438\u0439 \u043D\u0430\u0447\u0438\u043D\u0430\u0435\u0442\u0441\u044F \u0437\u0434\u0435\u0441\u044C",
|
|
227
|
+
noSideQuestions: "\u041D\u0435 \u0441\u043F\u0440\u0430\u0448\u0438\u0432\u0430\u0442\u044C \u0437\u0434\u0435\u0441\u044C \u043E \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0435 \u043C\u0435\u0436\u0434\u0443 \u0434\u0435\u043B\u043E\u043C",
|
|
228
|
+
exits: "\u041A\u043E\u0433\u0434\u0430 \u0432\u044B\u0445\u043E\u0434\u0438\u0442\u044C \u0438 \u043A\u0443\u0434\u0430",
|
|
229
|
+
addExit: "\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0443\u0441\u043B\u043E\u0432\u0438\u0435",
|
|
230
|
+
more: "\u0414\u043E\u043F\u043E\u043B\u043D\u0438\u0442\u0435\u043B\u044C\u043D\u043E",
|
|
231
|
+
deleteState: "\u0423\u0434\u0430\u043B\u0438\u0442\u044C \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0435",
|
|
232
|
+
duration: "\u0414\u043B\u0438\u0442\u0441\u044F",
|
|
233
|
+
durationHint: "\u041F\u043E\u0442\u043E\u043C \u043F\u0435\u0440\u0435\u0445\u043E\u0434 \xAB\u041A\u0443\u0434\u0430\xBB. \u0414\u043D\u0438 \u2014 \u0434\u043D\u0438 \u0441\u0446\u0435\u043D\u0430\u0440\u0438\u044F: \u043D\u043E\u0432\u044B\u0439 \u0434\u0435\u043D\u044C \u043D\u0430\u0447\u0438\u043D\u0430\u0435\u0442\u0441\u044F \u0432 \u043D\u0430\u0447\u0430\u043B\u0435 \u0434\u043D\u044F. \xAB\u0414\u043E \u0434\u043D\u044F \u043F\u0440\u043E\u0433\u0440\u0430\u043C\u043C\u044B\xBB \u2014 \u0434\u043E \u043D\u0430\u0447\u0430\u043B\u0430 \u044D\u0442\u043E\u0433\u043E \u0434\u043D\u044F, \u043A\u043E\u0433\u0434\u0430 \u0431\u044B \u0447\u0435\u043B\u043E\u0432\u0435\u043A \u043D\u0438 \u043F\u0440\u0438\u0448\u0451\u043B \u0432 \u043D\u043E\u0434\u0443.",
|
|
234
|
+
durationNone: "\u043D\u0435 \u043E\u0433\u0440\u0430\u043D\u0438\u0447\u0435\u043D\u043E",
|
|
235
|
+
durationUnits: { h: "\u0447\u0430\u0441\u043E\u0432", d: "\u0434\u043D\u0435\u0439", program_day: "\u0434\u043E \u0434\u043D\u044F \u043F\u0440\u043E\u0433\u0440\u0430\u043C\u043C\u044B" },
|
|
236
|
+
durationTo: "\u041F\u043E\u0442\u043E\u043C \u2014 \u043A\u0443\u0434\u0430",
|
|
237
|
+
nudges: "\u041A\u0430\u0441\u0430\u043D\u0438\u044F",
|
|
238
|
+
nudgesModes: { inherit: "\u041A\u0430\u043A \u0443 \u0441\u0446\u0435\u043D\u0430\u0440\u0438\u044F", own: "\u0421\u0432\u043E\u0438", off: "\u0412\u044B\u043A\u043B" },
|
|
239
|
+
nudgesHint: "\u0410\u0433\u0435\u043D\u0442 \u043F\u0438\u0448\u0435\u0442 \u0437\u0434\u0435\u0441\u044C \u0441\u0430\u043C, \u043A\u043E\u0433\u0434\u0430 \u0447\u0435\u043B\u043E\u0432\u0435\u043A \u043C\u043E\u043B\u0447\u0438\u0442.",
|
|
240
|
+
nudgesDays: "\u0421\u0432\u043E\u0438 \u043A\u0430\u0441\u0430\u043D\u0438\u044F \u0434\u0435\u0439\u0441\u0442\u0432\u0443\u044E\u0442",
|
|
241
|
+
nudgesDaysModes: { always: "\u0432\u0441\u0451 \u0432\u0440\u0435\u043C\u044F \u0432 \u043D\u043E\u0434\u0435", days: "\u0434\u043D., \u0441\u0447\u0438\u0442\u0430\u044F \u0434\u0435\u043D\u044C \u0432\u0445\u043E\u0434\u0430" },
|
|
242
|
+
nudgesDaysHint: "\u041F\u043E\u0442\u043E\u043C \u2014 \u043A\u0430\u0441\u0430\u043D\u0438\u044F \u0441\u0446\u0435\u043D\u0430\u0440\u0438\u044F: \u0443\u0442\u0440\u0435\u043D\u043D\u0435\u0435 \u0438 \u043E\u0431\u0449\u0438\u0439 \u0440\u044F\u0434 \u0441 \u043D\u0430\u0447\u0430\u043B\u0430."
|
|
243
|
+
},
|
|
244
|
+
exitCard: {
|
|
245
|
+
when: "\u041A\u043E\u0433\u0434\u0430",
|
|
246
|
+
field: "\u041F\u043E\u043B\u0435",
|
|
247
|
+
op: "\u041A\u0430\u043A \u0441\u0440\u0430\u0432\u043D\u0438\u0442\u044C",
|
|
248
|
+
value: "\u0417\u043D\u0430\u0447\u0435\u043D\u0438\u0435",
|
|
249
|
+
event: "\u0421\u043E\u0431\u044B\u0442\u0438\u0435",
|
|
250
|
+
eventName: "\u0427\u0442\u043E \u0438\u043C\u0435\u043D\u043D\u043E",
|
|
251
|
+
timer: "\u041E\u0442\u043A\u0443\u0434\u0430 \u0441\u0447\u0438\u0442\u0430\u0442\u044C",
|
|
252
|
+
amount: "\u0421\u043A\u043E\u043B\u044C\u043A\u043E",
|
|
253
|
+
at: "\u0412\u043E \u0441\u043A\u043E\u043B\u044C\u043A\u043E",
|
|
254
|
+
to: "\u041A\u0443\u0434\u0430",
|
|
255
|
+
moveUp: "\u0412\u044B\u0448\u0435",
|
|
256
|
+
moveDown: "\u041D\u0438\u0436\u0435",
|
|
257
|
+
delete: "\u0423\u0434\u0430\u043B\u0438\u0442\u044C \u0443\u0441\u043B\u043E\u0432\u0438\u0435"
|
|
258
|
+
},
|
|
259
|
+
anyStatePanel: {
|
|
260
|
+
title: "\u041E\u0431\u0449\u0438\u0435 \u043F\u0435\u0440\u0435\u0445\u043E\u0434\u044B",
|
|
261
|
+
hint: "\u0420\u0430\u0431\u043E\u0442\u0430\u044E\u0442 \u0438\u0437 \u043B\u044E\u0431\u043E\u0433\u043E \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u044F. \u0421\u0432\u043E\u0438 \u0443\u0441\u043B\u043E\u0432\u0438\u044F \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u044F \u043F\u0440\u043E\u0432\u0435\u0440\u044F\u044E\u0442\u0441\u044F \u0440\u0430\u043D\u044C\u0448\u0435.",
|
|
262
|
+
except: "\u041D\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0443\u044E\u0442 \u0435\u0449\u0451 \u0438 \u0437\u0434\u0435\u0441\u044C"
|
|
263
|
+
},
|
|
264
|
+
cardPanel: {
|
|
265
|
+
title: "\u041A\u0430\u0440\u0442\u043E\u0447\u043A\u0430 \u0438 \u0437\u0430\u043C\u0435\u0442\u043A\u0438",
|
|
266
|
+
clientCard: "\u041A\u0430\u0440\u0442\u043E\u0447\u043A\u0430 \u043A\u043B\u0438\u0435\u043D\u0442\u0430",
|
|
267
|
+
chatNotes: "\u0417\u0430\u043C\u0435\u0442\u043A\u0438 \u044D\u0442\u043E\u0433\u043E \u0447\u0430\u0442\u0430",
|
|
268
|
+
chatNotesHint: "\u0416\u0438\u0432\u0443\u0442 \u0432\u0435\u0441\u044C \u0447\u0430\u0442, \u0432 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0435\u043C \u0447\u0430\u0442\u0435 \u043D\u0435 \u043F\u043E\u043C\u043D\u044F\u0442\u0441\u044F.",
|
|
269
|
+
empty: "\u041A\u0430\u0440\u0442\u043E\u0447\u043A\u0430 \u043A\u043B\u0438\u0435\u043D\u0442\u0430 \u0438 \u0437\u0430\u043C\u0435\u0442\u043A\u0438 \u043F\u043E\u043A\u0430 \u043F\u0443\u0441\u0442\u044B.",
|
|
270
|
+
name: "\u041D\u0430\u0437\u0432\u0430\u043D\u0438\u0435",
|
|
271
|
+
about: "\u0427\u0442\u043E \u044D\u0442\u043E \u0438 \u043A\u043E\u0433\u0434\u0430 \u0443\u043C\u0435\u0441\u0442\u043D\u043E \u0441\u043F\u0440\u043E\u0441\u0438\u0442\u044C",
|
|
272
|
+
type: "\u0422\u0438\u043F",
|
|
273
|
+
options: "\u0412\u0430\u0440\u0438\u0430\u043D\u0442\u044B",
|
|
274
|
+
optionsHint: "\u0412\u0430\u0440\u0438\u0430\u043D\u0442\u044B \u0447\u0435\u0440\u0435\u0437 \u0437\u0430\u043F\u044F\u0442\u0443\u044E",
|
|
275
|
+
refreshAfter: "\u041C\u043E\u0436\u0435\u0442 \u0443\u0441\u0442\u0430\u0440\u0435\u0442\u044C",
|
|
276
|
+
refreshAfterMonths: (months) => `\u0447\u0435\u0440\u0435\u0437 ${months} \u043C\u0435\u0441.`,
|
|
277
|
+
pace: "\u0412\u043E\u043F\u0440\u043E\u0441 \u043E \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0435 \u2014 \u0440\u0430\u0437 \u0432",
|
|
278
|
+
paceEvery: (messages) => `\u0440\u0430\u0437 \u0432 ${messages} \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0439`,
|
|
279
|
+
paceHint: "\u0421\u043D\u0430\u0447\u0430\u043B\u0430 \u043F\u044B\u0442\u0430\u0435\u0442\u0441\u044F \u043F\u043E\u043D\u044F\u0442\u044C \u0438\u0437 \u0441\u043B\u043E\u0432, \u0441\u043F\u0440\u0430\u0448\u0438\u0432\u0430\u0435\u0442 \u2014 \u0435\u0441\u043B\u0438 \u043D\u0435 \u0432\u044B\u0448\u043B\u043E. \u041D\u0435 \u0441\u043F\u0440\u0430\u0448\u0438\u0432\u0430\u0435\u0442, \u0435\u0441\u043B\u0438 \u0447\u0435\u043B\u043E\u0432\u0435\u043A \u0440\u0430\u0437\u0434\u0440\u0430\u0436\u0451\u043D \u0438\u043B\u0438 \u0442\u043E\u0440\u043E\u043F\u0438\u0442\u0441\u044F.",
|
|
280
|
+
addField: "\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u043F\u043E\u043B\u0435",
|
|
281
|
+
newField: "\u041D\u043E\u0432\u043E\u0435 \u043F\u043E\u043B\u0435",
|
|
282
|
+
fromApp: "\u041F\u043E\u043B\u0435 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044F\u2026"
|
|
283
|
+
},
|
|
284
|
+
checkPanel: {
|
|
285
|
+
title: "\u041F\u0440\u043E\u0432\u0435\u0440\u043A\u0430",
|
|
286
|
+
clean: "\u041F\u0440\u043E\u0431\u043B\u0435\u043C \u043D\u0435 \u043D\u0430\u0448\u043B\u043E\u0441\u044C.",
|
|
287
|
+
errors: "\u041E\u0448\u0438\u0431\u043A\u0438",
|
|
288
|
+
warnings: "\u0417\u0430\u043C\u0435\u0447\u0430\u043D\u0438\u044F",
|
|
289
|
+
goTo: "\u041E\u0442\u043A\u0440\u044B\u0442\u044C",
|
|
290
|
+
appCheckFailed: "\u041F\u0440\u043E\u0432\u0435\u0440\u043A\u0438 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044F \u043D\u0435 \u0441\u0440\u0430\u0431\u043E\u0442\u0430\u043B\u0438 \u2014 \u0438\u0445 \u043D\u0430\u0445\u043E\u0434\u043E\u043A \u0437\u0434\u0435\u0441\u044C \u043D\u0435\u0442"
|
|
291
|
+
},
|
|
292
|
+
runPanel: {
|
|
293
|
+
title: "\u041F\u0440\u043E\u0433\u043E\u043D",
|
|
294
|
+
hint: "\u041F\u0440\u043E\u0433\u043E\u043D: \u0432\u044B\u0431\u0438\u0440\u0430\u0439\u0442\u0435 \u0441\u043F\u0440\u0430\u0432\u0430, \u0447\u0442\u043E \u043F\u0440\u043E\u0438\u0437\u043E\u0448\u043B\u043E",
|
|
295
|
+
current: "\u0413\u0434\u0435 \u0441\u0435\u0439\u0447\u0430\u0441 \u0430\u0433\u0435\u043D\u0442",
|
|
296
|
+
whatHappened: "\u0427\u0442\u043E \u043F\u0440\u043E\u0438\u0437\u043E\u0448\u043B\u043E?",
|
|
297
|
+
nothing: "\u041D\u0438\u0447\u0435\u0433\u043E \u0438\u0437 \u044D\u0442\u043E\u0433\u043E",
|
|
298
|
+
known: "\u0427\u0442\u043E \u0430\u0433\u0435\u043D\u0442 \u0437\u043D\u0430\u0435\u0442",
|
|
299
|
+
notYet: "\u041F\u043E\u043A\u0430 \u043D\u0435\u0442",
|
|
300
|
+
nextQuestion: "\u0421\u043B\u0435\u0434\u0443\u044E\u0449\u0438\u0439 \u0432\u043E\u043F\u0440\u043E\u0441 \u043E \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0435",
|
|
301
|
+
path: "\u041F\u0440\u043E\u0439\u0434\u0435\u043D\u043D\u044B\u0439 \u043F\u0443\u0442\u044C",
|
|
302
|
+
back: "\u0428\u0430\u0433 \u043D\u0430\u0437\u0430\u0434",
|
|
303
|
+
restart: "\u0421\u043D\u0430\u0447\u0430\u043B\u0430",
|
|
304
|
+
finish: "\u0417\u0430\u043A\u043E\u043D\u0447\u0438\u0442\u044C \u043F\u0440\u043E\u0433\u043E\u043D",
|
|
305
|
+
step: (step2) => `\u041F\u0440\u043E\u0433\u043E\u043D \xB7 \u0448\u0430\u0433 ${step2}`,
|
|
306
|
+
stay: "\u043E\u0441\u0442\u0430\u0442\u044C\u0441\u044F \u0437\u0434\u0435\u0441\u044C",
|
|
307
|
+
wontFire: "\u0441\u0435\u0439\u0447\u0430\u0441 \u043D\u0435 \u0441\u0440\u0430\u0431\u043E\u0442\u0430\u0435\u0442",
|
|
308
|
+
nothingHappened: "\u043D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u0441\u0440\u0430\u0431\u043E\u0442\u0430\u043B\u043E",
|
|
309
|
+
noStart: "\u041D\u0435\u0442 \u0441\u0442\u0430\u0440\u0442\u043E\u0432\u043E\u0433\u043E \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u044F: \u043E\u0442\u043C\u0435\u0442\u044C\u0442\u0435, \u0433\u0434\u0435 \u043D\u0430\u0447\u0438\u043D\u0430\u0435\u0442\u0441\u044F \u0441\u0446\u0435\u043D\u0430\u0440\u0438\u0439"
|
|
310
|
+
},
|
|
311
|
+
jsonDialog: {
|
|
312
|
+
title: "\u0413\u0440\u0430\u0444 \u0432 \u0444\u043E\u0440\u043C\u0430\u0442\u0435 JSON",
|
|
313
|
+
copy: "\u041A\u043E\u043F\u0438\u0440\u043E\u0432\u0430\u0442\u044C",
|
|
314
|
+
copied: "\u0421\u043A\u043E\u043F\u0438\u0440\u043E\u0432\u0430\u043D\u043E",
|
|
315
|
+
download: "\u0421\u043A\u0430\u0447\u0430\u0442\u044C",
|
|
316
|
+
load: "\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C",
|
|
317
|
+
close: "\u0417\u0430\u043A\u0440\u044B\u0442\u044C"
|
|
318
|
+
}
|
|
319
|
+
};
|
|
320
|
+
var englishFlowEditorLabels = {
|
|
321
|
+
conditionKinds: { judge: "In words", data: "Data", event: "Event", timer: "Timer" },
|
|
322
|
+
conditionKindHints: {
|
|
323
|
+
judge: "In words \u2014 the agent decides, the judge checks",
|
|
324
|
+
data: "By data \u2014 a field is filled, a value equals\u2026",
|
|
325
|
+
event: "Event \u2014 a button, a tool, an app signal",
|
|
326
|
+
timer: "Timer \u2014 by the clock"
|
|
327
|
+
},
|
|
328
|
+
returnKind: "Back",
|
|
329
|
+
returnTarget: "back where it was",
|
|
330
|
+
ops: { filled: "is filled", eq: "=", ne: "\u2260", gt: ">", lt: "<", contains: "contains" },
|
|
331
|
+
events: { message: "Message arrived", button: "Button pressed", tool: "Tool called", app: "App event" },
|
|
332
|
+
timerSpans: {
|
|
333
|
+
in_state: "Time in this state",
|
|
334
|
+
silence: "Person is silent",
|
|
335
|
+
since_start: "Since the chat started",
|
|
336
|
+
time_of_day: "Time of day reached"
|
|
337
|
+
},
|
|
338
|
+
units: { min: "min", h: "h", d: "d" },
|
|
339
|
+
fieldTypes: { text: "Text", number: "Number", choice: "Choice", boolean: "Yes/no" },
|
|
340
|
+
yes: "yes",
|
|
341
|
+
no: "no",
|
|
342
|
+
days: (count) => `${count} ${count === 1 ? "day" : "days"}`,
|
|
343
|
+
programDay: (day) => `from day ${day}`,
|
|
344
|
+
header: {
|
|
345
|
+
state: "State",
|
|
346
|
+
card: "Card and notes",
|
|
347
|
+
check: "Check the graph",
|
|
348
|
+
run: "Dry run",
|
|
349
|
+
json: "JSON",
|
|
350
|
+
save: "Save",
|
|
351
|
+
saved: "Saved",
|
|
352
|
+
unsaved: "Unsaved changes",
|
|
353
|
+
saveFailed: "Not saved",
|
|
354
|
+
saveFailedHint: "Saving was rejected. See what is wrong in \u201CCheck flow\u201D and on the nodes.",
|
|
355
|
+
readOnly: "View only",
|
|
356
|
+
readOnlyHint: "An agent is editing this flow right now. You can look and run it, but not edit.",
|
|
357
|
+
migrate: "Move chats"
|
|
358
|
+
},
|
|
359
|
+
board: {
|
|
360
|
+
anyState: "From any state",
|
|
361
|
+
start: "The flow starts here",
|
|
362
|
+
final: "End",
|
|
363
|
+
noSideQuestions: "No questions about the person",
|
|
364
|
+
noTarget: "target not chosen",
|
|
365
|
+
emptyCondition: "condition not written",
|
|
366
|
+
newState: "New state",
|
|
367
|
+
zoomIn: "Zoom in",
|
|
368
|
+
zoomOut: "Zoom out",
|
|
369
|
+
fit: "Whole graph",
|
|
370
|
+
errors: (count) => `Errors: ${count}`,
|
|
371
|
+
warnings: (count) => `Warnings: ${count}`,
|
|
372
|
+
nudges: "nudges",
|
|
373
|
+
noNudges: "no nudges",
|
|
374
|
+
nudgesDays: (days) => days === 1 ? "on the entry day" : `for the first ${days} days`,
|
|
375
|
+
space: {
|
|
376
|
+
legend: "Legend",
|
|
377
|
+
showMinimap: "Show the minimap",
|
|
378
|
+
hideMinimap: "Hide the minimap",
|
|
379
|
+
showInspector: "Show the flow panel",
|
|
380
|
+
hideInspector: "Hide the flow panel",
|
|
381
|
+
enterFullscreen: "Full screen",
|
|
382
|
+
exitFullscreen: "Exit full screen",
|
|
383
|
+
fullscreenRegion: "Flow editor, full screen"
|
|
384
|
+
}
|
|
385
|
+
},
|
|
386
|
+
overview: {
|
|
387
|
+
title: "Flow",
|
|
388
|
+
common: "Common to all states",
|
|
389
|
+
commonHint: "Role, tone, prices, terms, links \u2014 whatever holds in every state.",
|
|
390
|
+
states: "States",
|
|
391
|
+
exits: "Exit conditions",
|
|
392
|
+
finals: "Final",
|
|
393
|
+
lists: "Lists",
|
|
394
|
+
help: "How to use",
|
|
395
|
+
flowIssues: "Whole flow",
|
|
396
|
+
afterEnd: "After the conversation ends",
|
|
397
|
+
afterEndModes: { reply: "Reply", silent: "Stay silent" },
|
|
398
|
+
afterEndHints: {
|
|
399
|
+
reply: "The agent replies from the common part: it knows the program is over, does not lead through steps or sell again.",
|
|
400
|
+
silent: "The agent stays silent; the app receives the message."
|
|
401
|
+
}
|
|
402
|
+
},
|
|
403
|
+
schedule: {
|
|
404
|
+
title: "Days and time",
|
|
405
|
+
on: "Day-by-day flow",
|
|
406
|
+
hint: "Days, night, total length and first messages. The agent sees the day number on every turn.",
|
|
407
|
+
dayStart: "Day starts at",
|
|
408
|
+
dayStartHint: "The morning message goes out at this time.",
|
|
409
|
+
lengthDays: "How many days",
|
|
410
|
+
lengthDaysNone: "no limit",
|
|
411
|
+
nightFrom: "Night from",
|
|
412
|
+
nightFromHint: "At night the agent never writes first. Empty \u2014 as in the app settings.",
|
|
413
|
+
lateStart: "Late start, h",
|
|
414
|
+
lateStartHint: "Arrived less than this many hours before night \u2014 day one is tomorrow. 0 \u2014 the arrival day is day one.",
|
|
415
|
+
deadlineState: "Time is up \u2014 go to",
|
|
416
|
+
deadlineNone: "end right away",
|
|
417
|
+
farewell: "Say goodbye when time is up",
|
|
418
|
+
farewellText: "Ready-made goodbye",
|
|
419
|
+
farewellTextHint: "Empty \u2014 the agent writes the goodbye.",
|
|
420
|
+
firstMessages: "Writing first",
|
|
421
|
+
maxPerDay: "At most per day",
|
|
422
|
+
maxPerDayHint: "Empty \u2014 as in the app settings.",
|
|
423
|
+
morning: "Morning message",
|
|
424
|
+
skipMorning: "Skip the morning if the person wrote within, min",
|
|
425
|
+
nudges: "Default nudges",
|
|
426
|
+
nudgesHint: "The agent writes in the same state while the person is silent. A state can have its own.",
|
|
427
|
+
dayFrame: "Daily limits",
|
|
428
|
+
dayFrameHint: "By the number of the person's messages per day.",
|
|
429
|
+
softFrom: "Wrap up from message #",
|
|
430
|
+
hardAt: "End the day at message #",
|
|
431
|
+
hardText: "Reply when the day is over",
|
|
432
|
+
hardTextHint: "Sent word for word, without the model.",
|
|
433
|
+
more: "More"
|
|
434
|
+
},
|
|
435
|
+
nudges: {
|
|
436
|
+
afterSilence: "Silent \u2014 write after",
|
|
437
|
+
addStep: "Add a delay",
|
|
438
|
+
dailyAt: "Then every day at",
|
|
439
|
+
addTime: "Add a time",
|
|
440
|
+
giveUpAfter: "Silent this long \u2014 conversation over",
|
|
441
|
+
never: "never",
|
|
442
|
+
remove: "Remove",
|
|
443
|
+
on: "Default nudges"
|
|
444
|
+
},
|
|
445
|
+
sideTasks: {
|
|
446
|
+
title: "Side tasks",
|
|
447
|
+
hint: "The agent does them in passing, at the pace of questions about the person. No more than the given attempts.",
|
|
448
|
+
builtIn: "\u201CFind out the city\u201D is built in: always on when \u201CDays and time\u201D are set.",
|
|
449
|
+
text: "What to do",
|
|
450
|
+
when: "When it fits",
|
|
451
|
+
field: "Done when this field is known",
|
|
452
|
+
noField: "\u2014",
|
|
453
|
+
fromApp: "App fields",
|
|
454
|
+
maxAttempts: "Attempts",
|
|
455
|
+
perConversation: "Anew in every conversation",
|
|
456
|
+
add: "Add a task",
|
|
457
|
+
newTask: "",
|
|
458
|
+
delete: "Delete the task"
|
|
459
|
+
},
|
|
460
|
+
statePanel: {
|
|
461
|
+
title: "State",
|
|
462
|
+
name: "Name",
|
|
463
|
+
instructions: "What the agent does here",
|
|
464
|
+
instructionsHint: "The first line is the goal, shown on the board. Then \u2014 how to behave.",
|
|
465
|
+
firstMessage: "First message",
|
|
466
|
+
learn: "Learn in this state",
|
|
467
|
+
tools: "Tools",
|
|
468
|
+
toolsHint: "Only the checked tools are available here.",
|
|
469
|
+
noTools: "The flow has no tools yet.",
|
|
470
|
+
tags: "Tags",
|
|
471
|
+
makeStart: "Start here",
|
|
472
|
+
isStart: "The flow starts here",
|
|
473
|
+
noSideQuestions: "No side questions about the person here",
|
|
474
|
+
exits: "When to leave and where to",
|
|
475
|
+
addExit: "Add a condition",
|
|
476
|
+
more: "More",
|
|
477
|
+
deleteState: "Delete the state",
|
|
478
|
+
duration: "Lasts",
|
|
479
|
+
durationHint: "Then goes to \u201CGo to\u201D. Days are flow days: a new day begins at the day start. \u201CUntil program day\u201D lasts until that day starts, whenever the person entered the node.",
|
|
480
|
+
durationNone: "no limit",
|
|
481
|
+
durationUnits: { h: "hours", d: "days", program_day: "until program day" },
|
|
482
|
+
durationTo: "Then go to",
|
|
483
|
+
nudges: "Nudges",
|
|
484
|
+
nudgesModes: { inherit: "As in the flow", own: "Own", off: "Off" },
|
|
485
|
+
nudgesHint: "The agent writes here by itself while the person is silent.",
|
|
486
|
+
nudgesDays: "Own nudges apply",
|
|
487
|
+
nudgesDaysModes: { always: "the whole time in the state", days: "days, counting the entry day" },
|
|
488
|
+
nudgesDaysHint: "Then the flow's nudges: the morning message and the common series from the start."
|
|
489
|
+
},
|
|
490
|
+
exitCard: {
|
|
491
|
+
when: "When",
|
|
492
|
+
field: "Field",
|
|
493
|
+
op: "Compare",
|
|
494
|
+
value: "Value",
|
|
495
|
+
event: "Event",
|
|
496
|
+
eventName: "Which one",
|
|
497
|
+
timer: "Count from",
|
|
498
|
+
amount: "How long",
|
|
499
|
+
at: "At",
|
|
500
|
+
to: "Go to",
|
|
501
|
+
moveUp: "Up",
|
|
502
|
+
moveDown: "Down",
|
|
503
|
+
delete: "Delete the condition"
|
|
504
|
+
},
|
|
505
|
+
anyStatePanel: {
|
|
506
|
+
title: "Common transitions",
|
|
507
|
+
hint: "Work from any state. The state's own conditions are checked first.",
|
|
508
|
+
except: "Also not active here"
|
|
509
|
+
},
|
|
510
|
+
cardPanel: {
|
|
511
|
+
title: "Card and notes",
|
|
512
|
+
clientCard: "Client card",
|
|
513
|
+
chatNotes: "Notes of this chat",
|
|
514
|
+
chatNotesHint: "Live for the whole chat and are not remembered in the next one.",
|
|
515
|
+
empty: "The client card and notes are empty so far.",
|
|
516
|
+
name: "Name",
|
|
517
|
+
about: "What it is and when to ask",
|
|
518
|
+
type: "Type",
|
|
519
|
+
options: "Options",
|
|
520
|
+
optionsHint: "Options separated by commas",
|
|
521
|
+
refreshAfter: "May go stale",
|
|
522
|
+
refreshAfterMonths: (months) => `in ${months} mo.`,
|
|
523
|
+
pace: "A question about the person \u2014 once per",
|
|
524
|
+
paceEvery: (messages) => `once per ${messages} messages`,
|
|
525
|
+
paceHint: "First tries to understand from what was said and asks only if that fails. Does not ask when the person is annoyed or in a hurry.",
|
|
526
|
+
addField: "Add a field",
|
|
527
|
+
newField: "New field",
|
|
528
|
+
fromApp: "App field\u2026"
|
|
529
|
+
},
|
|
530
|
+
checkPanel: {
|
|
531
|
+
title: "Check",
|
|
532
|
+
clean: "No problems found.",
|
|
533
|
+
errors: "Errors",
|
|
534
|
+
warnings: "Warnings",
|
|
535
|
+
goTo: "Open",
|
|
536
|
+
appCheckFailed: "The app's checks failed \u2014 their findings are missing here"
|
|
537
|
+
},
|
|
538
|
+
runPanel: {
|
|
539
|
+
title: "Dry run",
|
|
540
|
+
hint: "Dry run: choose on the right what happened",
|
|
541
|
+
current: "Where the agent is",
|
|
542
|
+
whatHappened: "What happened?",
|
|
543
|
+
nothing: "None of these",
|
|
544
|
+
known: "What the agent knows",
|
|
545
|
+
notYet: "Not yet",
|
|
546
|
+
nextQuestion: "Next question about the person",
|
|
547
|
+
path: "Path so far",
|
|
548
|
+
back: "Step back",
|
|
549
|
+
restart: "Restart",
|
|
550
|
+
finish: "Finish the dry run",
|
|
551
|
+
step: (step2) => `Dry run \xB7 step ${step2}`,
|
|
552
|
+
stay: "stay here",
|
|
553
|
+
wontFire: "won't fire now",
|
|
554
|
+
nothingHappened: "nothing fired",
|
|
555
|
+
noStart: "No start state: mark where the flow begins"
|
|
556
|
+
},
|
|
557
|
+
jsonDialog: {
|
|
558
|
+
title: "The graph as JSON",
|
|
559
|
+
copy: "Copy",
|
|
560
|
+
copied: "Copied",
|
|
561
|
+
download: "Download",
|
|
562
|
+
load: "Load",
|
|
563
|
+
close: "Close"
|
|
564
|
+
}
|
|
565
|
+
};
|
|
566
|
+
|
|
567
|
+
// src/labels/migrate.ts
|
|
568
|
+
var russianFlowMigrateLabels = {
|
|
569
|
+
title: "\u041F\u0435\u0440\u0435\u0432\u0435\u0441\u0442\u0438 \u0447\u0430\u0442\u044B \u043D\u0430 \u043D\u043E\u0432\u0443\u044E \u0432\u0435\u0440\u0441\u0438\u044E",
|
|
570
|
+
hint: "\u0427\u0430\u0442\u044B \u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0430\u0442 \u0441 \u0442\u043E\u0433\u043E \u0436\u0435 \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u044F. \u0414\u043B\u044F \u0443\u0434\u0430\u043B\u0451\u043D\u043D\u044B\u0445 \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0439 \u0432\u044B\u0431\u0435\u0440\u0438\u0442\u0435, \u043A\u0443\u0434\u0430 \u043F\u0435\u0440\u0435\u0432\u0435\u0441\u0442\u0438.",
|
|
571
|
+
version: "\u0412\u0435\u0440\u0441\u0438\u044F",
|
|
572
|
+
state: "\u0421\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0435",
|
|
573
|
+
chats: "\u0427\u0430\u0442\u043E\u0432",
|
|
574
|
+
removed: "\u0443\u0434\u0430\u043B\u0435\u043D\u043E \u0432 \u043D\u043E\u0432\u043E\u0439 \u0432\u0435\u0440\u0441\u0438\u0438",
|
|
575
|
+
target: "\u041A\u0443\u0434\u0430",
|
|
576
|
+
chooseTarget: "\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0435",
|
|
577
|
+
migrate: "\u041F\u0435\u0440\u0435\u0432\u0435\u0441\u0442\u0438",
|
|
578
|
+
moved: (count) => `\u041F\u0435\u0440\u0435\u0432\u0435\u0434\u0435\u043D\u043E \u0447\u0430\u0442\u043E\u0432: ${count}`,
|
|
579
|
+
nothingToMigrate: "\u0412\u0441\u0435 \u0447\u0430\u0442\u044B \u0443\u0436\u0435 \u043D\u0430 \u044D\u0442\u043E\u0439 \u0432\u0435\u0440\u0441\u0438\u0438.",
|
|
580
|
+
close: "\u0417\u0430\u043A\u0440\u044B\u0442\u044C",
|
|
581
|
+
failed: "\u041D\u0435 \u043F\u043E\u043B\u0443\u0447\u0438\u043B\u043E\u0441\u044C \u043F\u0435\u0440\u0435\u0432\u0435\u0441\u0442\u0438. \u041F\u043E\u043F\u0440\u043E\u0431\u0443\u0439\u0442\u0435 \u0435\u0449\u0451 \u0440\u0430\u0437"
|
|
582
|
+
};
|
|
583
|
+
var englishFlowMigrateLabels = {
|
|
584
|
+
title: "Move chats to the new version",
|
|
585
|
+
hint: "Chats continue from the same state. For removed states choose where to move them.",
|
|
586
|
+
version: "Version",
|
|
587
|
+
state: "State",
|
|
588
|
+
chats: "Chats",
|
|
589
|
+
removed: "removed in the new version",
|
|
590
|
+
target: "Move to",
|
|
591
|
+
chooseTarget: "Choose a state",
|
|
592
|
+
migrate: "Move",
|
|
593
|
+
moved: (count) => `Chats moved: ${count}`,
|
|
594
|
+
nothingToMigrate: "All chats are already on this version.",
|
|
595
|
+
close: "Close",
|
|
596
|
+
failed: "Could not move the chats. Try again"
|
|
597
|
+
};
|
|
598
|
+
|
|
599
|
+
// src/labels/test-chat.ts
|
|
600
|
+
var russianFlowTestChatLabels = {
|
|
601
|
+
title: "\u0422\u0435\u0441\u0442\u043E\u0432\u044B\u0439 \u0447\u0430\u0442",
|
|
602
|
+
state: "\u0421\u0435\u0439\u0447\u0430\u0441 \u0432 \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0438",
|
|
603
|
+
advance: (label) => `+${label}`,
|
|
604
|
+
clock: "\u0427\u0430\u0441\u044B \u0447\u0430\u0442\u0430",
|
|
605
|
+
noOffset: "\u043A\u0430\u043A \u0441\u0435\u0439\u0447\u0430\u0441",
|
|
606
|
+
units: { day: "\u0434", hour: "\u0447", minute: "\u043C\u0438\u043D" },
|
|
607
|
+
reset: "\u041D\u0430\u0447\u0430\u0442\u044C \u0437\u0430\u043D\u043E\u0432\u043E",
|
|
608
|
+
close: "\u0417\u0430\u043A\u0440\u044B\u0442\u044C",
|
|
609
|
+
failed: "\u041D\u0435 \u043F\u043E\u043B\u0443\u0447\u0438\u043B\u043E\u0441\u044C \u2014 \u043F\u043E\u043F\u0440\u043E\u0431\u0443\u0439\u0442\u0435 \u0435\u0449\u0451 \u0440\u0430\u0437",
|
|
610
|
+
nextDay: "\u041A \u043D\u0430\u0447\u0430\u043B\u0443 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0435\u0433\u043E \u0434\u043D\u044F",
|
|
611
|
+
day: (day, totalDays) => totalDays === void 0 ? `\u0414\u0435\u043D\u044C ${day}` : `\u0414\u0435\u043D\u044C ${day} \u0438\u0437 ${totalDays}`,
|
|
612
|
+
beforeFirstDay: "\u0414\u043E \u043F\u0435\u0440\u0432\u043E\u0433\u043E \u0434\u043D\u044F"
|
|
613
|
+
};
|
|
614
|
+
var englishFlowTestChatLabels = {
|
|
615
|
+
title: "Test chat",
|
|
616
|
+
state: "Current state",
|
|
617
|
+
advance: (label) => `+${label}`,
|
|
618
|
+
clock: "Chat clock",
|
|
619
|
+
noOffset: "now",
|
|
620
|
+
units: { day: "d", hour: "h", minute: "min" },
|
|
621
|
+
reset: "Start over",
|
|
622
|
+
close: "Close",
|
|
623
|
+
failed: "Something went wrong \u2014 try again",
|
|
624
|
+
nextDay: "To the start of the next day",
|
|
625
|
+
day: (day, totalDays) => totalDays === void 0 ? `Day ${day}` : `Day ${day} of ${totalDays}`,
|
|
626
|
+
beforeFirstDay: "Before day one"
|
|
627
|
+
};
|
|
628
|
+
|
|
629
|
+
// src/labels/index.ts
|
|
630
|
+
var russianFlowLabels = {
|
|
631
|
+
editor: russianFlowEditorLabels,
|
|
632
|
+
clientCard: russianClientCardLabels,
|
|
633
|
+
migrate: russianFlowMigrateLabels,
|
|
634
|
+
testChat: russianFlowTestChatLabels
|
|
635
|
+
};
|
|
636
|
+
var englishFlowLabels = {
|
|
637
|
+
editor: englishFlowEditorLabels,
|
|
638
|
+
clientCard: englishClientCardLabels,
|
|
639
|
+
migrate: englishFlowMigrateLabels,
|
|
640
|
+
testChat: englishFlowTestChatLabels
|
|
641
|
+
};
|
|
642
|
+
|
|
643
|
+
// src/react/flow-labels.ts
|
|
644
|
+
var FlowLabelsContext = createContext(russianFlowLabels);
|
|
645
|
+
function FlowLabelsProvider({ labels, children }) {
|
|
646
|
+
return createElement(FlowLabelsContext.Provider, { value: labels }, children);
|
|
647
|
+
}
|
|
648
|
+
function useFlowLabels() {
|
|
649
|
+
return useContext(FlowLabelsContext);
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
// src/react/editor/flow-editor.tsx
|
|
653
|
+
import { useMemo as useMemo4, useState as useState4 } from "react";
|
|
654
|
+
|
|
655
|
+
// src/react/editor/editor-model.ts
|
|
656
|
+
function freeId(prefix, taken) {
|
|
657
|
+
const used = new Set(taken);
|
|
658
|
+
let index = 1;
|
|
659
|
+
while (used.has(`${prefix}_${index}`)) index += 1;
|
|
660
|
+
return `${prefix}_${index}`;
|
|
661
|
+
}
|
|
662
|
+
function allExits(flow) {
|
|
663
|
+
return [...flow.anyState.exits, ...flow.states.flatMap((state) => state.exits)];
|
|
664
|
+
}
|
|
665
|
+
function allFields(flow) {
|
|
666
|
+
return [...flow.clientCard, ...flow.chatNotes];
|
|
667
|
+
}
|
|
668
|
+
function editState(flow, stateId, edit) {
|
|
669
|
+
return { ...flow, states: flow.states.map((state) => state.id === stateId ? edit(state) : state) };
|
|
670
|
+
}
|
|
671
|
+
function editExits(flow, owner, edit) {
|
|
672
|
+
if (owner === null) return { ...flow, anyState: { ...flow.anyState, exits: edit(flow.anyState.exits) } };
|
|
673
|
+
return editState(flow, owner, (state) => ({ ...state, exits: edit(state.exits) }));
|
|
674
|
+
}
|
|
675
|
+
function exitsOf(flow, owner) {
|
|
676
|
+
if (owner === null) return flow.anyState.exits;
|
|
677
|
+
return flow.states.find((state) => state.id === owner)?.exits ?? [];
|
|
678
|
+
}
|
|
679
|
+
function shift(items, index, delta) {
|
|
680
|
+
const target = index + delta;
|
|
681
|
+
if (index < 0 || target < 0 || target >= items.length) return items;
|
|
682
|
+
const next = [...items];
|
|
683
|
+
const [item] = next.splice(index, 1);
|
|
684
|
+
next.splice(target, 0, item);
|
|
685
|
+
return next;
|
|
686
|
+
}
|
|
687
|
+
function addState(flow, input) {
|
|
688
|
+
const stateId = freeId("state", flow.states.map((state2) => state2.id));
|
|
689
|
+
const state = {
|
|
690
|
+
id: stateId,
|
|
691
|
+
kind: "state",
|
|
692
|
+
title: input.title,
|
|
693
|
+
instructions: "",
|
|
694
|
+
firstMessage: null,
|
|
695
|
+
learn: [],
|
|
696
|
+
noSideQuestions: false,
|
|
697
|
+
tools: [],
|
|
698
|
+
tags: [],
|
|
699
|
+
exits: []
|
|
700
|
+
};
|
|
701
|
+
return {
|
|
702
|
+
stateId,
|
|
703
|
+
flow: {
|
|
704
|
+
...flow,
|
|
705
|
+
start: flow.states.length === 0 ? stateId : flow.start,
|
|
706
|
+
states: [...flow.states, state],
|
|
707
|
+
layout: input.at ? { ...flow.layout, [stateId]: input.at } : flow.layout
|
|
708
|
+
}
|
|
709
|
+
};
|
|
710
|
+
}
|
|
711
|
+
function removeState(flow, stateId) {
|
|
712
|
+
const unlink = (exits) => exits.map((exit) => exit.to === stateId ? { ...exit, to: null } : exit);
|
|
713
|
+
const { [stateId]: _removed, ...layout } = flow.layout;
|
|
714
|
+
return {
|
|
715
|
+
...flow,
|
|
716
|
+
start: flow.start === stateId ? null : flow.start,
|
|
717
|
+
states: flow.states.filter((state) => state.id !== stateId).map((state) => ({ ...state, exits: unlink(state.exits) })),
|
|
718
|
+
anyState: { except: flow.anyState.except.filter((id) => id !== stateId), exits: unlink(flow.anyState.exits) },
|
|
719
|
+
layout
|
|
720
|
+
};
|
|
721
|
+
}
|
|
722
|
+
function updateState(flow, stateId, patch) {
|
|
723
|
+
return editState(flow, stateId, (state) => ({ ...state, ...patch }));
|
|
724
|
+
}
|
|
725
|
+
function setStart(flow, stateId) {
|
|
726
|
+
return { ...flow, start: stateId };
|
|
727
|
+
}
|
|
728
|
+
function setExcept(flow, except) {
|
|
729
|
+
return { ...flow, anyState: { ...flow.anyState, except } };
|
|
730
|
+
}
|
|
731
|
+
function defaultCondition(kind, flow) {
|
|
732
|
+
switch (kind) {
|
|
733
|
+
case "judge":
|
|
734
|
+
return { type: "judge", text: "" };
|
|
735
|
+
case "data":
|
|
736
|
+
return { type: "data", field: allFields(flow)[0]?.id ?? "field", op: "filled" };
|
|
737
|
+
case "event":
|
|
738
|
+
return { type: "event", event: "message" };
|
|
739
|
+
case "timer":
|
|
740
|
+
return { type: "timer", from: "in_state", amount: 1, unit: "h" };
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
function normalizeCondition(flow, previous, next) {
|
|
744
|
+
if (next.type !== previous.type) return defaultCondition(next.type, flow);
|
|
745
|
+
if (next.type === "data") {
|
|
746
|
+
const type = fieldById(flow, next.field)?.field.type;
|
|
747
|
+
if (type === void 0) return next;
|
|
748
|
+
if (!FLOW_OPS_BY_FIELD_TYPE[type].includes(next.op)) return { type: "data", field: next.field, op: "filled" };
|
|
749
|
+
if (next.op === "filled") return { type: "data", field: next.field, op: "filled" };
|
|
750
|
+
if (type === "boolean") return { type: "data", field: next.field, op: next.op, value: next.value !== false && next.value !== "false" };
|
|
751
|
+
if (type === "number" && typeof next.value === "string" && next.value.trim() !== "" && Number.isFinite(Number(next.value))) {
|
|
752
|
+
return { ...next, value: Number(next.value) };
|
|
753
|
+
}
|
|
754
|
+
return { ...next, value: next.value ?? "" };
|
|
755
|
+
}
|
|
756
|
+
if (next.type === "timer") {
|
|
757
|
+
if (next.from === "time_of_day") return { type: "timer", from: "time_of_day", at: "at" in next && next.at ? next.at : "10:00" };
|
|
758
|
+
const days = "days" in next && next.days ? next.days : "day" in next && next.day ? next.day : 1;
|
|
759
|
+
if (next.from === "state_days") return { type: "timer", from: "state_days", days };
|
|
760
|
+
if (next.from === "program_day") return { type: "timer", from: "program_day", day: days };
|
|
761
|
+
const span = next;
|
|
762
|
+
return { type: "timer", from: next.from, amount: span.amount ?? 1, unit: span.unit ?? "h" };
|
|
763
|
+
}
|
|
764
|
+
if (next.type === "event" && next.event === "message") return { type: "event", event: "message" };
|
|
765
|
+
return next;
|
|
766
|
+
}
|
|
767
|
+
function addExit(flow, owner, kind) {
|
|
768
|
+
const exitId = freeId(owner ?? "any", allExits(flow).map((exit2) => exit2.id));
|
|
769
|
+
const exit = { id: exitId, when: defaultCondition(kind, flow), to: null };
|
|
770
|
+
return { exitId, flow: editExits(flow, owner, (exits) => [...exits, exit]) };
|
|
771
|
+
}
|
|
772
|
+
function updateExit(flow, owner, exitId, next) {
|
|
773
|
+
return editExits(flow, owner, (exits) => exits.map((exit) => exit.id === exitId ? { ...exit, ...next } : exit));
|
|
774
|
+
}
|
|
775
|
+
function connectExit(flow, owner, exitId, to) {
|
|
776
|
+
return editExits(flow, owner, (exits) => exits.map((exit) => exit.id === exitId ? { ...exit, to } : exit));
|
|
777
|
+
}
|
|
778
|
+
function moveExit(flow, owner, exitId, delta) {
|
|
779
|
+
const exits = exitsOf(flow, owner);
|
|
780
|
+
const moved = shift(exits, exits.findIndex((exit) => exit.id === exitId), delta);
|
|
781
|
+
return moved === exits ? flow : editExits(flow, owner, () => moved);
|
|
782
|
+
}
|
|
783
|
+
function removeExit(flow, owner, exitId) {
|
|
784
|
+
return editExits(flow, owner, (exits) => exits.filter((exit) => exit.id !== exitId));
|
|
785
|
+
}
|
|
786
|
+
function addField(flow, list, name) {
|
|
787
|
+
const fieldId = freeId("field", allFields(flow).map((field) => field.id));
|
|
788
|
+
const base = { id: fieldId, name, about: "", type: "text", options: [] };
|
|
789
|
+
if (list === "clientCard") {
|
|
790
|
+
return { fieldId, flow: { ...flow, clientCard: [...flow.clientCard, { ...base, refreshAfterMonths: null }] } };
|
|
791
|
+
}
|
|
792
|
+
return { fieldId, flow: { ...flow, chatNotes: [...flow.chatNotes, base] } };
|
|
793
|
+
}
|
|
794
|
+
function updateField(flow, list, fieldId, patch) {
|
|
795
|
+
if (list === "clientCard") {
|
|
796
|
+
return { ...flow, clientCard: flow.clientCard.map((field) => field.id === fieldId ? { ...field, ...patch } : field) };
|
|
797
|
+
}
|
|
798
|
+
const { refreshAfterMonths: _cardOnly, ...notePatch } = patch;
|
|
799
|
+
return { ...flow, chatNotes: flow.chatNotes.map((field) => field.id === fieldId ? { ...field, ...notePatch } : field) };
|
|
800
|
+
}
|
|
801
|
+
function moveField(flow, list, fieldId, delta) {
|
|
802
|
+
const fields = flow[list];
|
|
803
|
+
const moved = shift(fields, fields.findIndex((field) => field.id === fieldId), delta);
|
|
804
|
+
return moved === fields ? flow : { ...flow, [list]: moved };
|
|
805
|
+
}
|
|
806
|
+
function removeField(flow, list, fieldId) {
|
|
807
|
+
const fields = flow[list];
|
|
808
|
+
return {
|
|
809
|
+
...flow,
|
|
810
|
+
[list]: fields.filter((field) => field.id !== fieldId),
|
|
811
|
+
states: flow.states.map((state) => state.learn.includes(fieldId) ? { ...state, learn: state.learn.filter((id) => id !== fieldId) } : state)
|
|
812
|
+
};
|
|
813
|
+
}
|
|
814
|
+
function freeSpot(flow) {
|
|
815
|
+
const points = Object.values(flow.layout);
|
|
816
|
+
if (points.length === 0) return { x: 80, y: 80 };
|
|
817
|
+
return { x: Math.min(...points.map((point) => point.x)), y: Math.max(...points.map((point) => point.y)) + 240 };
|
|
818
|
+
}
|
|
819
|
+
function placeNode(flow, nodeId, at) {
|
|
820
|
+
return { ...flow, layout: { ...flow.layout, [nodeId]: { x: Math.round(at.x), y: Math.round(at.y) } } };
|
|
821
|
+
}
|
|
822
|
+
function flowToJson(flow) {
|
|
823
|
+
return `${JSON.stringify(keepJudgeText(compactFlow(flow)), null, 2)}
|
|
824
|
+
`;
|
|
825
|
+
}
|
|
826
|
+
function keepJudgeText(json) {
|
|
827
|
+
const fix = (exits) => exits?.forEach((exit) => {
|
|
828
|
+
if (exit.when?.type === "judge" && exit.when.text === void 0) exit.when.text = "";
|
|
829
|
+
});
|
|
830
|
+
json.states.forEach((state) => fix(state.exits));
|
|
831
|
+
fix(json.anyState?.exits);
|
|
832
|
+
return json;
|
|
833
|
+
}
|
|
834
|
+
function loadFlowJson(text) {
|
|
835
|
+
let input;
|
|
836
|
+
try {
|
|
837
|
+
input = JSON.parse(text);
|
|
838
|
+
} catch (error) {
|
|
839
|
+
return { ok: false, errors: [`JSON: ${error instanceof Error ? error.message : String(error)}`] };
|
|
840
|
+
}
|
|
841
|
+
const result = parseFlow(input);
|
|
842
|
+
if (result.ok) return result;
|
|
843
|
+
return { ok: false, errors: result.errors.map((error) => error.path ? `${error.path}: ${error.message}` : error.message) };
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
// src/react/editor/exit-text.ts
|
|
847
|
+
function conditionText(flow, when, labels, catalog) {
|
|
848
|
+
switch (when.type) {
|
|
849
|
+
case "judge":
|
|
850
|
+
return when.text.trim() || labels.board.emptyCondition;
|
|
851
|
+
case "data": {
|
|
852
|
+
const name = fieldById(flow, when.field)?.field.name || when.field;
|
|
853
|
+
const op = labels.ops[when.op];
|
|
854
|
+
if (typeof when.value === "boolean") return `${name} ${op} ${when.value ? labels.yes : labels.no}`;
|
|
855
|
+
return when.op === "filled" || when.value === void 0 ? `${name} ${op}` : `${name} ${op} ${when.value}`;
|
|
856
|
+
}
|
|
857
|
+
case "event":
|
|
858
|
+
if (when.event === "message" || !when.name) return labels.events[when.event];
|
|
859
|
+
return `${labels.events[when.event]} \xAB${when.event !== "tool" && catalogEventLabel(catalog, when.name) || when.name}\xBB`;
|
|
860
|
+
case "timer":
|
|
861
|
+
if (when.from === "time_of_day") return `${labels.timerSpans.time_of_day} ${when.at}`;
|
|
862
|
+
if (when.from === "state_days") return `${labels.statePanel.duration} ${labels.days(when.days)}`;
|
|
863
|
+
if (when.from === "program_day") return labels.programDay(when.day);
|
|
864
|
+
return `${labels.timerSpans[when.from]} ${when.amount} ${labels.units[when.unit]}`;
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
function edgeKind(exit) {
|
|
868
|
+
return exit.to === RETURN_TARGET ? "return" : exit.when.type;
|
|
869
|
+
}
|
|
870
|
+
function targetTitle(flow, to, labels) {
|
|
871
|
+
if (to === null) return null;
|
|
872
|
+
if (to === RETURN_TARGET) return labels.returnTarget;
|
|
873
|
+
const state = flow.states.find((candidate) => candidate.id === to);
|
|
874
|
+
return state ? state.title || state.id : null;
|
|
875
|
+
}
|
|
876
|
+
function stateGoal(instructions) {
|
|
877
|
+
return instructions.split("\n").find((line) => line.trim() !== "")?.trim() ?? "";
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
// src/react/editor/board-space.ts
|
|
881
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
882
|
+
var DEFAULT_SPACE_STORAGE_KEY = "agent-chat-flow-board";
|
|
883
|
+
function readRemembered(key) {
|
|
884
|
+
if (!key || typeof window === "undefined") return {};
|
|
885
|
+
try {
|
|
886
|
+
const parsed = JSON.parse(window.localStorage.getItem(key) ?? "null");
|
|
887
|
+
if (typeof parsed !== "object" || parsed === null) return {};
|
|
888
|
+
const result = {};
|
|
889
|
+
for (const name of ["legend", "minimap", "inspector"]) {
|
|
890
|
+
const value = parsed[name];
|
|
891
|
+
if (typeof value === "boolean") result[name] = value;
|
|
892
|
+
}
|
|
893
|
+
return result;
|
|
894
|
+
} catch {
|
|
895
|
+
return {};
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
function writeRemembered(key, value) {
|
|
899
|
+
if (!key || typeof window === "undefined") return;
|
|
900
|
+
try {
|
|
901
|
+
window.localStorage.setItem(key, JSON.stringify(value));
|
|
902
|
+
} catch {
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
var selectionKey = (selection) => {
|
|
906
|
+
if (selection.kind === "state") return `state:${selection.stateId}:${selection.exitId ?? ""}`;
|
|
907
|
+
if (selection.kind === "any") return `any:${selection.exitId ?? ""}`;
|
|
908
|
+
return selection.kind === "overview" ? `overview:${selection.sideTaskId ?? ""}` : selection.kind;
|
|
909
|
+
};
|
|
910
|
+
function useBoardSpace(props, editor) {
|
|
911
|
+
const { header = "full", legend = "collapsed", minimap = "open", inspector = "open", fullscreen: fullscreenAllowed = true } = props;
|
|
912
|
+
const storageKey = props.storageKey === void 0 ? DEFAULT_SPACE_STORAGE_KEY : props.storageKey;
|
|
913
|
+
const [remembered, setRemembered] = useState({});
|
|
914
|
+
useEffect(() => setRemembered(readRemembered(storageKey)), [storageKey]);
|
|
915
|
+
const unsaved = useRef(false);
|
|
916
|
+
useEffect(() => {
|
|
917
|
+
if (!unsaved.current) return;
|
|
918
|
+
unsaved.current = false;
|
|
919
|
+
writeRemembered(storageKey, remembered);
|
|
920
|
+
}, [remembered, storageKey]);
|
|
921
|
+
const remember = useCallback((name, value) => {
|
|
922
|
+
unsaved.current = true;
|
|
923
|
+
setRemembered((current) => ({ ...current, [name]: value }));
|
|
924
|
+
}, []);
|
|
925
|
+
const legendOpen = legend === "hidden" ? false : remembered.legend ?? legend === "open";
|
|
926
|
+
const minimapOpen = minimap === "hidden" ? false : remembered.minimap ?? minimap === "open";
|
|
927
|
+
const selected = editor.selection.kind !== "overview" || editor.selection.sideTaskId !== void 0;
|
|
928
|
+
const key = selectionKey(editor.selection);
|
|
929
|
+
const [followed, setFollowed] = useState(selected);
|
|
930
|
+
const [previousKey, setPreviousKey] = useState(key);
|
|
931
|
+
if (key !== previousKey) {
|
|
932
|
+
setPreviousKey(key);
|
|
933
|
+
setFollowed(selected);
|
|
934
|
+
}
|
|
935
|
+
const needed = editor.mode !== "edit";
|
|
936
|
+
const inspectorOpen = needed || (inspector === "collapsible" ? followed : remembered.inspector ?? true);
|
|
937
|
+
const [fullscreen, setFullscreen] = useState(false);
|
|
938
|
+
useEffect(() => {
|
|
939
|
+
if (!fullscreenAllowed) setFullscreen(false);
|
|
940
|
+
}, [fullscreenAllowed]);
|
|
941
|
+
return useMemo(
|
|
942
|
+
() => ({
|
|
943
|
+
header,
|
|
944
|
+
legendHidden: legend === "hidden",
|
|
945
|
+
legendOpen,
|
|
946
|
+
toggleLegend: () => remember("legend", !legendOpen),
|
|
947
|
+
minimapHidden: minimap === "hidden",
|
|
948
|
+
minimapOpen,
|
|
949
|
+
toggleMinimap: () => remember("minimap", !minimapOpen),
|
|
950
|
+
inspectorOpen,
|
|
951
|
+
inspectorToggleable: !needed,
|
|
952
|
+
toggleInspector: () => {
|
|
953
|
+
if (needed) return;
|
|
954
|
+
if (inspector === "collapsible") setFollowed(!inspectorOpen);
|
|
955
|
+
else remember("inspector", !inspectorOpen);
|
|
956
|
+
},
|
|
957
|
+
fullscreenAllowed,
|
|
958
|
+
fullscreen: fullscreenAllowed && fullscreen,
|
|
959
|
+
setFullscreen
|
|
960
|
+
}),
|
|
961
|
+
[header, legend, legendOpen, minimap, minimapOpen, inspector, inspectorOpen, needed, fullscreenAllowed, fullscreen, remember]
|
|
962
|
+
);
|
|
963
|
+
}
|
|
964
|
+
var FOCUSABLE = 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
|
|
965
|
+
function useFullscreenBehavior(active, onExit, blocked, root) {
|
|
966
|
+
useEffect(() => {
|
|
967
|
+
if (!active) return;
|
|
968
|
+
const previous = document.body.style.overflow;
|
|
969
|
+
document.body.style.overflow = "hidden";
|
|
970
|
+
return () => {
|
|
971
|
+
document.body.style.overflow = previous;
|
|
972
|
+
};
|
|
973
|
+
}, [active]);
|
|
974
|
+
useEffect(() => {
|
|
975
|
+
if (!active) return;
|
|
976
|
+
const onKey = (event) => {
|
|
977
|
+
if (event.key !== "Escape" || event.defaultPrevented || blocked) return;
|
|
978
|
+
const target = event.target;
|
|
979
|
+
if (target instanceof HTMLElement && root?.contains(target) && target.matches('input, textarea, select, [contenteditable]:not([contenteditable="false"])')) {
|
|
980
|
+
event.preventDefault();
|
|
981
|
+
target.blur();
|
|
982
|
+
return;
|
|
983
|
+
}
|
|
984
|
+
event.preventDefault();
|
|
985
|
+
onExit();
|
|
986
|
+
root?.querySelector("[data-flow-fullscreen-toggle]")?.focus();
|
|
987
|
+
};
|
|
988
|
+
document.addEventListener("keydown", onKey);
|
|
989
|
+
return () => document.removeEventListener("keydown", onKey);
|
|
990
|
+
}, [active, blocked, onExit, root]);
|
|
991
|
+
return useCallback(
|
|
992
|
+
(event) => {
|
|
993
|
+
if (!active || event.key !== "Tab" || !root || !(event.target instanceof Node) || !root.contains(event.target)) return;
|
|
994
|
+
const items = [...root.querySelectorAll(FOCUSABLE)].filter((item) => item.getClientRects().length > 0);
|
|
995
|
+
const first = items[0];
|
|
996
|
+
const last = items[items.length - 1];
|
|
997
|
+
if (!first || !last) return;
|
|
998
|
+
const current = document.activeElement;
|
|
999
|
+
if (event.shiftKey && (current === first || !root.contains(current))) {
|
|
1000
|
+
event.preventDefault();
|
|
1001
|
+
last.focus();
|
|
1002
|
+
} else if (!event.shiftKey && (current === last || !root.contains(current))) {
|
|
1003
|
+
event.preventDefault();
|
|
1004
|
+
first.focus();
|
|
1005
|
+
}
|
|
1006
|
+
},
|
|
1007
|
+
[active, root]
|
|
1008
|
+
);
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
// src/react/editor/flow-board.tsx
|
|
1012
|
+
import {
|
|
1013
|
+
Background,
|
|
1014
|
+
BackgroundVariant,
|
|
1015
|
+
getBezierPath,
|
|
1016
|
+
Handle,
|
|
1017
|
+
MiniMap,
|
|
1018
|
+
Panel,
|
|
1019
|
+
Position,
|
|
1020
|
+
ReactFlow,
|
|
1021
|
+
ReactFlowProvider,
|
|
1022
|
+
applyNodeChanges,
|
|
1023
|
+
useReactFlow,
|
|
1024
|
+
useStore
|
|
1025
|
+
} from "@xyflow/react";
|
|
1026
|
+
import { createContext as createContext2, useCallback as useCallback2, useContext as useContext2, useEffect as useEffect2, useMemo as useMemo2, useState as useState2 } from "react";
|
|
1027
|
+
|
|
1028
|
+
// src/react/editor/board-styles.ts
|
|
1029
|
+
var FLOW_BOARD_CSS = `
|
|
1030
|
+
.react-flow { direction: ltr; position: relative; width: 100%; height: 100%; overflow: hidden; z-index: 0; }
|
|
1031
|
+
.react-flow__container, .react-flow__edgelabel-renderer, .react-flow__viewport-portal {
|
|
1032
|
+
position: absolute; width: 100%; height: 100%; top: 0; left: 0;
|
|
1033
|
+
}
|
|
1034
|
+
.react-flow__pane { z-index: 1; touch-action: none; }
|
|
1035
|
+
.react-flow__pane.draggable { cursor: grab; }
|
|
1036
|
+
.react-flow__pane.dragging { cursor: grabbing; }
|
|
1037
|
+
.react-flow__viewport { transform-origin: 0 0; z-index: 2; pointer-events: none; }
|
|
1038
|
+
.react-flow__renderer { z-index: 4; }
|
|
1039
|
+
.react-flow__background { pointer-events: none; z-index: -1; }
|
|
1040
|
+
.react-flow__background-pattern.dots { fill: var(--flow-board-dots, rgba(128, 128, 128, 0.35)); }
|
|
1041
|
+
.react-flow .react-flow__edges { position: absolute; }
|
|
1042
|
+
.react-flow .react-flow__edges svg { overflow: visible; position: absolute; pointer-events: none; }
|
|
1043
|
+
.react-flow__edge { pointer-events: visibleStroke; }
|
|
1044
|
+
.react-flow__edge.inactive { pointer-events: none; }
|
|
1045
|
+
.react-flow__edge:focus, .react-flow__edge:focus-visible { outline: none; }
|
|
1046
|
+
.react-flow__connection { pointer-events: none; }
|
|
1047
|
+
.react-flow__connection-path { stroke: var(--flow-board-connection, currentColor); stroke-width: 2; fill: none; stroke-dasharray: 4 4; }
|
|
1048
|
+
svg.react-flow__connectionline { z-index: 1001; overflow: visible; position: absolute; }
|
|
1049
|
+
.react-flow__nodes { pointer-events: none; transform-origin: 0 0; }
|
|
1050
|
+
.react-flow__node {
|
|
1051
|
+
position: absolute; user-select: none; pointer-events: all; transform-origin: 0 0; box-sizing: border-box; cursor: default;
|
|
1052
|
+
}
|
|
1053
|
+
.react-flow__node.draggable { cursor: grab; }
|
|
1054
|
+
.react-flow__node.draggable.dragging { cursor: grabbing; }
|
|
1055
|
+
.react-flow__node:focus, .react-flow__node:focus-visible { outline: none; }
|
|
1056
|
+
.react-flow__handle {
|
|
1057
|
+
position: absolute; pointer-events: none; width: 10px; height: 10px; min-width: 5px; min-height: 5px; border-radius: 999px;
|
|
1058
|
+
background: var(--flow-board-handle-fill, #fff); border: 1.5px solid var(--flow-board-handle, currentColor);
|
|
1059
|
+
}
|
|
1060
|
+
.react-flow__handle.connectingfrom, .react-flow__handle.connectionindicator { pointer-events: all; cursor: crosshair; }
|
|
1061
|
+
.react-flow__handle-left { top: 50%; left: 0; transform: translate(-50%, -50%); }
|
|
1062
|
+
.react-flow__handle-right { top: 50%; right: 0; transform: translate(50%, -50%); }
|
|
1063
|
+
.react-flow__handle-top { top: 0; left: 50%; transform: translate(-50%, -50%); }
|
|
1064
|
+
.react-flow__handle-bottom { top: auto; bottom: 0; left: 50%; transform: translate(-50%, 50%); }
|
|
1065
|
+
.react-flow__panel { position: absolute; z-index: 5; margin: 16px; }
|
|
1066
|
+
.react-flow__panel.top { top: 0; }
|
|
1067
|
+
.react-flow__panel.bottom { bottom: 0; }
|
|
1068
|
+
.react-flow__panel.left { left: 0; }
|
|
1069
|
+
.react-flow__panel.right { right: 0; }
|
|
1070
|
+
.react-flow__panel.top.center, .react-flow__panel.bottom.center { left: 50%; transform: translateX(-16px) translateX(-50%); }
|
|
1071
|
+
.react-flow__minimap { background: var(--flow-board-minimap, #fff); border-radius: 12px; overflow: hidden; }
|
|
1072
|
+
.react-flow__minimap-svg { display: block; }
|
|
1073
|
+
.react-flow__minimap-mask { fill: var(--flow-board-minimap-mask, rgba(128, 128, 128, 0.15)); }
|
|
1074
|
+
.react-flow__minimap-node { fill: var(--flow-board-minimap-node, rgba(128, 128, 128, 0.35)); }
|
|
1075
|
+
.flow-board__new-exit.react-flow__handle {
|
|
1076
|
+
top: auto; bottom: 10px; right: 0; transform: translate(50%, 0); opacity: 0.55;
|
|
1077
|
+
}
|
|
1078
|
+
.flow-board__new-exit.react-flow__handle:hover { opacity: 1; }
|
|
1079
|
+
@media (max-width: 860px) {
|
|
1080
|
+
.flow-board__legend, .flow-board__minimap, .flow-board__minimap-toggle { display: none !important; }
|
|
1081
|
+
}
|
|
1082
|
+
`;
|
|
1083
|
+
|
|
1084
|
+
// src/react/editor/schedule-model.ts
|
|
1085
|
+
var DEFAULT_SCHEDULE = { dayStart: "09:00", lateStartHours: DEFAULT_LATE_START_HOURS, farewellOnDeadline: false };
|
|
1086
|
+
var DEFAULT_NUDGES = {
|
|
1087
|
+
afterSilence: [
|
|
1088
|
+
{ amount: 15, unit: "min" },
|
|
1089
|
+
{ amount: 1, unit: "h" },
|
|
1090
|
+
{ amount: 4, unit: "h" }
|
|
1091
|
+
]
|
|
1092
|
+
};
|
|
1093
|
+
function compact(value) {
|
|
1094
|
+
const entries = Object.entries(value).filter(([, item]) => item !== void 0 && item !== "");
|
|
1095
|
+
return entries.length === 0 ? void 0 : Object.fromEntries(entries);
|
|
1096
|
+
}
|
|
1097
|
+
function withOptional(target, key, value) {
|
|
1098
|
+
const { [key]: _old, ...rest } = target;
|
|
1099
|
+
return value === void 0 ? rest : { ...rest, [key]: value };
|
|
1100
|
+
}
|
|
1101
|
+
function setScheduleOn(flow, on) {
|
|
1102
|
+
if (on) return flow.schedule ? flow : { ...flow, schedule: { ...DEFAULT_SCHEDULE } };
|
|
1103
|
+
return withOptional(flow, "schedule", void 0);
|
|
1104
|
+
}
|
|
1105
|
+
function updateSchedule(flow, patch) {
|
|
1106
|
+
let schedule = flow.schedule ?? { ...DEFAULT_SCHEDULE };
|
|
1107
|
+
for (const [key, raw] of Object.entries(patch)) {
|
|
1108
|
+
const value = raw !== null && typeof raw === "object" && !Array.isArray(raw) && key !== "nudges" ? compact(raw) : raw === "" ? void 0 : raw;
|
|
1109
|
+
schedule = withOptional(schedule, key, value);
|
|
1110
|
+
}
|
|
1111
|
+
return { ...flow, schedule };
|
|
1112
|
+
}
|
|
1113
|
+
function setAfterEnd(flow, mode) {
|
|
1114
|
+
return { ...flow, afterEnd: mode };
|
|
1115
|
+
}
|
|
1116
|
+
function durationExit(exit) {
|
|
1117
|
+
const { when } = exit;
|
|
1118
|
+
if (when.type !== "timer") return null;
|
|
1119
|
+
if (when.from === "state_days") return { exitId: exit.id, amount: when.days, unit: "d", to: exit.to };
|
|
1120
|
+
if (when.from === "program_day") return { exitId: exit.id, amount: when.day, unit: "program_day", to: exit.to };
|
|
1121
|
+
if (when.from === "in_state" && when.unit === "h") return { exitId: exit.id, amount: when.amount, unit: "h", to: exit.to };
|
|
1122
|
+
return null;
|
|
1123
|
+
}
|
|
1124
|
+
function durationOf(state) {
|
|
1125
|
+
for (const exit of state.exits) {
|
|
1126
|
+
const found = durationExit(exit);
|
|
1127
|
+
if (found) return found;
|
|
1128
|
+
}
|
|
1129
|
+
return null;
|
|
1130
|
+
}
|
|
1131
|
+
function setDuration(flow, stateId, value) {
|
|
1132
|
+
const state = flow.states.find((candidate) => candidate.id === stateId);
|
|
1133
|
+
if (!state) return flow;
|
|
1134
|
+
const current = durationOf(state);
|
|
1135
|
+
if (value === null) {
|
|
1136
|
+
if (!current) return flow;
|
|
1137
|
+
return { ...flow, states: flow.states.map((candidate) => candidate.id === stateId ? { ...candidate, exits: candidate.exits.filter((exit) => exit.id !== current.exitId) } : candidate) };
|
|
1138
|
+
}
|
|
1139
|
+
const amount = Math.max(1, Math.round(value.amount) || 1);
|
|
1140
|
+
const when = value.unit === "d" ? { type: "timer", from: "state_days", days: amount } : value.unit === "program_day" ? { type: "timer", from: "program_day", day: amount } : { type: "timer", from: "in_state", amount, unit: "h" };
|
|
1141
|
+
if (current) return updateExit(flow, stateId, current.exitId, { when, to: current.to });
|
|
1142
|
+
const made = addExit(flow, stateId, "timer");
|
|
1143
|
+
return updateExit(made.flow, stateId, made.exitId, { when, to: null });
|
|
1144
|
+
}
|
|
1145
|
+
function nudgesMode(state) {
|
|
1146
|
+
if (state.nudges === "off") return "off";
|
|
1147
|
+
return state.nudges ? "own" : "inherit";
|
|
1148
|
+
}
|
|
1149
|
+
function setStateNudges(flow, stateId, mode) {
|
|
1150
|
+
return {
|
|
1151
|
+
...flow,
|
|
1152
|
+
states: flow.states.map((state) => {
|
|
1153
|
+
if (state.id !== stateId || nudgesMode(state) === mode) return state;
|
|
1154
|
+
if (mode === "inherit") return withOptional(state, "nudges", void 0);
|
|
1155
|
+
if (mode === "off") return { ...state, nudges: "off" };
|
|
1156
|
+
return { ...state, nudges: structuredClone(flow.schedule?.nudges ?? DEFAULT_NUDGES) };
|
|
1157
|
+
})
|
|
1158
|
+
};
|
|
1159
|
+
}
|
|
1160
|
+
function updateStateNudges(flow, stateId, nudges) {
|
|
1161
|
+
return {
|
|
1162
|
+
...flow,
|
|
1163
|
+
states: flow.states.map((state) => {
|
|
1164
|
+
if (state.id !== stateId) return state;
|
|
1165
|
+
const days = state.nudges && state.nudges !== "off" ? state.nudges.days : void 0;
|
|
1166
|
+
return { ...state, nudges: { ...cleanNudges(nudges), ...days !== void 0 && { days } } };
|
|
1167
|
+
})
|
|
1168
|
+
};
|
|
1169
|
+
}
|
|
1170
|
+
function setStateNudgesDays(flow, stateId, days) {
|
|
1171
|
+
return {
|
|
1172
|
+
...flow,
|
|
1173
|
+
states: flow.states.map((state) => {
|
|
1174
|
+
if (state.id !== stateId || !state.nudges || state.nudges === "off") return state;
|
|
1175
|
+
const { days: _days, ...rest } = state.nudges;
|
|
1176
|
+
return { ...state, nudges: days === null ? rest : { ...rest, days: Math.max(1, Math.round(days) || 1) } };
|
|
1177
|
+
})
|
|
1178
|
+
};
|
|
1179
|
+
}
|
|
1180
|
+
function stateNudgesDays(state) {
|
|
1181
|
+
return state.nudges && state.nudges !== "off" ? state.nudges.days ?? null : null;
|
|
1182
|
+
}
|
|
1183
|
+
function updateScheduleNudges(flow, nudges) {
|
|
1184
|
+
return updateSchedule(flow, { nudges: nudges === null ? void 0 : cleanNudges(nudges) });
|
|
1185
|
+
}
|
|
1186
|
+
function cleanNudges(nudges) {
|
|
1187
|
+
let clean = { afterSilence: nudges.afterSilence };
|
|
1188
|
+
if (nudges.dailyAt?.length) clean = { ...clean, dailyAt: nudges.dailyAt };
|
|
1189
|
+
if (nudges.giveUpAfter) clean = { ...clean, giveUpAfter: nudges.giveUpAfter };
|
|
1190
|
+
return clean;
|
|
1191
|
+
}
|
|
1192
|
+
function durationText(duration, labels) {
|
|
1193
|
+
return `${duration.amount} ${labels.units[duration.unit]}`;
|
|
1194
|
+
}
|
|
1195
|
+
function stateTimingText(flow, state, labels) {
|
|
1196
|
+
const parts = [];
|
|
1197
|
+
const duration = durationOf(state);
|
|
1198
|
+
if (duration) {
|
|
1199
|
+
parts.push(duration.unit === "d" ? labels.days(duration.amount) : duration.unit === "program_day" ? labels.programDay(duration.amount) : `${duration.amount} ${labels.units.h}`);
|
|
1200
|
+
}
|
|
1201
|
+
if (state.nudges === "off") parts.push(labels.board.noNudges);
|
|
1202
|
+
else if (state.nudges) {
|
|
1203
|
+
const steps = state.nudges.afterSilence.map((step2) => durationText(step2, labels));
|
|
1204
|
+
const daily = state.nudges.dailyAt ?? [];
|
|
1205
|
+
const days = state.nudges.days === void 0 ? "" : ` ${labels.board.nudgesDays(state.nudges.days)}`;
|
|
1206
|
+
if (steps.length + daily.length > 0) parts.push(`${labels.board.nudges} ${[...steps, ...daily].join(" \xB7 ")}${days}`);
|
|
1207
|
+
}
|
|
1208
|
+
return parts.join(" \xB7 ");
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
// src/react/editor/flow-simulation.ts
|
|
1212
|
+
var STEP_MS = 6e4;
|
|
1213
|
+
function startSimulation(flow, options) {
|
|
1214
|
+
const run = noteAgentAnswer(startRun(flow, options.now), options.now);
|
|
1215
|
+
return { run, card: {}, now: options.now, timeZone: options.timeZone, steps: [{ stateId: run.state, exitId: null, nothing: false }], previous: null };
|
|
1216
|
+
}
|
|
1217
|
+
function simulationChoices(flow, sim) {
|
|
1218
|
+
const state = stateById(flow, sim.run.state);
|
|
1219
|
+
if (!state) return [];
|
|
1220
|
+
return activeExits(flow, state).map(({ exit, own }) => {
|
|
1221
|
+
const after = chooseExit(flow, sim, exit.id);
|
|
1222
|
+
return { exitId: exit.id, own, target: after.steps.length > sim.steps.length ? after.run.state : null };
|
|
1223
|
+
});
|
|
1224
|
+
}
|
|
1225
|
+
function findExit(flow, exitId) {
|
|
1226
|
+
return [...flow.states.flatMap((state) => state.exits), ...flow.anyState.exits].find((exit) => exit.id === exitId);
|
|
1227
|
+
}
|
|
1228
|
+
function fittingValue(condition, field) {
|
|
1229
|
+
const expected = condition.value;
|
|
1230
|
+
switch (condition.op) {
|
|
1231
|
+
case "eq":
|
|
1232
|
+
case "contains":
|
|
1233
|
+
return expected;
|
|
1234
|
+
case "gt":
|
|
1235
|
+
return typeof expected === "number" ? expected + 1 : expected;
|
|
1236
|
+
case "lt":
|
|
1237
|
+
return typeof expected === "number" ? expected - 1 : expected;
|
|
1238
|
+
case "filled":
|
|
1239
|
+
if (field.type === "number") return 1;
|
|
1240
|
+
return field.type === "choice" && field.options.length > 0 ? field.options[0] : "\u2713";
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
function writeValue(flow, sim, fieldId, value) {
|
|
1244
|
+
const found = fieldById(flow, fieldId);
|
|
1245
|
+
if (!found) return sim;
|
|
1246
|
+
if (found.place === "notes") return { ...sim, run: { ...sim.run, notes: { ...sim.run.notes, [fieldId]: value } } };
|
|
1247
|
+
return { ...sim, card: { ...sim.card, [fieldId]: { value, updatedAt: sim.now } } };
|
|
1248
|
+
}
|
|
1249
|
+
function cardValues(sim) {
|
|
1250
|
+
return Object.fromEntries(Object.entries(sim.card).map(([id, entry]) => [id, entry.value]));
|
|
1251
|
+
}
|
|
1252
|
+
function step(flow, before, prepare) {
|
|
1253
|
+
const asked = sideQuestion(flow, before.run, { card: before.card, now: before.now });
|
|
1254
|
+
const prepared = prepare({ ...before, now: before.now + STEP_MS });
|
|
1255
|
+
const sim = prepared.sim;
|
|
1256
|
+
let run = prepared.human ? noteHumanMessage(sim.run, sim.now) : sim.run;
|
|
1257
|
+
const transition = prepared.event ? nextTransition(flow, run, prepared.event, sim.now) : null;
|
|
1258
|
+
const steps = [...sim.steps];
|
|
1259
|
+
if (transition) {
|
|
1260
|
+
run = applyTransition(flow, run, transition);
|
|
1261
|
+
steps.push({ stateId: transition.to, exitId: transition.exitId, nothing: false });
|
|
1262
|
+
} else if (prepared.event === null) {
|
|
1263
|
+
steps.push({ stateId: run.state, exitId: null, nothing: true });
|
|
1264
|
+
} else {
|
|
1265
|
+
return before;
|
|
1266
|
+
}
|
|
1267
|
+
run = noteAgentAnswer(run, sim.now);
|
|
1268
|
+
if (asked && asked.reason !== "learn") run = noteSideQuestion(run);
|
|
1269
|
+
return { ...sim, run, steps, previous: before };
|
|
1270
|
+
}
|
|
1271
|
+
function chooseExit(flow, sim, exitId) {
|
|
1272
|
+
const exit = findExit(flow, exitId);
|
|
1273
|
+
if (!exit) return sim;
|
|
1274
|
+
const when = exit.when;
|
|
1275
|
+
return step(flow, sim, (current) => {
|
|
1276
|
+
switch (when.type) {
|
|
1277
|
+
case "judge":
|
|
1278
|
+
return { sim: current, human: true, event: { type: "verdict", exitId, probability: 1, decidedBy: "judge" } };
|
|
1279
|
+
case "event":
|
|
1280
|
+
return {
|
|
1281
|
+
sim: current,
|
|
1282
|
+
human: when.event === "message",
|
|
1283
|
+
event: { type: "event", event: when.event, ...when.name === void 0 ? {} : { name: when.name } }
|
|
1284
|
+
};
|
|
1285
|
+
case "timer": {
|
|
1286
|
+
const due = timerDueAt(when, current.run, current.timeZone);
|
|
1287
|
+
const now = due === null ? current.now : Math.max(current.now - STEP_MS, due);
|
|
1288
|
+
return { sim: { ...current, now }, human: false, event: { type: "timer", exitId, timeZone: current.timeZone } };
|
|
1289
|
+
}
|
|
1290
|
+
case "data": {
|
|
1291
|
+
const found = fieldById(flow, when.field);
|
|
1292
|
+
if (!found || !FLOW_OPS_BY_FIELD_TYPE[found.field.type].includes(when.op)) {
|
|
1293
|
+
return { sim: current, human: true, event: { type: "data", field: when.field, card: cardValues(current) } };
|
|
1294
|
+
}
|
|
1295
|
+
const filled = writeValue(flow, current, when.field, fittingValue(when, found.field));
|
|
1296
|
+
return { sim: filled, human: true, event: { type: "data", field: when.field, card: cardValues(filled) } };
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
});
|
|
1300
|
+
}
|
|
1301
|
+
function chooseNothing(flow, sim) {
|
|
1302
|
+
return step(flow, sim, (current) => ({ sim: current, human: true, event: null }));
|
|
1303
|
+
}
|
|
1304
|
+
function setKnownValue(flow, sim, fieldId, text) {
|
|
1305
|
+
const found = fieldById(flow, fieldId);
|
|
1306
|
+
if (!found) return sim;
|
|
1307
|
+
const trimmed = text.trim();
|
|
1308
|
+
const value = trimmed === "" ? void 0 : found.field.type === "number" && Number.isFinite(Number(trimmed)) ? Number(trimmed) : trimmed;
|
|
1309
|
+
let written = writeValue(flow, sim, fieldId, value);
|
|
1310
|
+
if (value === void 0) {
|
|
1311
|
+
written = found.place === "notes" ? { ...written, run: { ...written.run, notes: withoutKey(written.run.notes, fieldId) } } : { ...written, card: withoutKey(written.card, fieldId) };
|
|
1312
|
+
return written;
|
|
1313
|
+
}
|
|
1314
|
+
const transition = nextTransition(flow, written.run, { type: "data", field: fieldId, card: cardValues(written) }, written.now);
|
|
1315
|
+
if (!transition) return written;
|
|
1316
|
+
return {
|
|
1317
|
+
...written,
|
|
1318
|
+
run: noteAgentAnswer(applyTransition(flow, written.run, transition), written.now),
|
|
1319
|
+
steps: [...written.steps, { stateId: transition.to, exitId: transition.exitId, nothing: false }],
|
|
1320
|
+
previous: sim
|
|
1321
|
+
};
|
|
1322
|
+
}
|
|
1323
|
+
function withoutKey(record, key) {
|
|
1324
|
+
const { [key]: _removed, ...rest } = record;
|
|
1325
|
+
return rest;
|
|
1326
|
+
}
|
|
1327
|
+
function nextQuestionField(flow, sim) {
|
|
1328
|
+
return sideQuestion(flow, sim.run, { card: sim.card, now: sim.now })?.fieldId ?? null;
|
|
1329
|
+
}
|
|
1330
|
+
function stepBack(sim) {
|
|
1331
|
+
return sim.previous ?? sim;
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
// src/react/editor/flow-board.tsx
|
|
1335
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
1336
|
+
var NEW_EXIT_HANDLE = "@new";
|
|
1337
|
+
var INPUT_HANDLE = "in";
|
|
1338
|
+
var LEGEND_KINDS = ["judge", "data", "event", "timer", "return"];
|
|
1339
|
+
var SkinContext = createContext2(null);
|
|
1340
|
+
function useSkin() {
|
|
1341
|
+
const skin = useContext2(SkinContext);
|
|
1342
|
+
if (!skin) throw new Error("FlowBoard renders only inside FlowEditor.");
|
|
1343
|
+
return skin;
|
|
1344
|
+
}
|
|
1345
|
+
function exitHandle(exitId, connectable) {
|
|
1346
|
+
return /* @__PURE__ */ jsx(Handle, { type: "source", position: Position.Right, id: exitId, isConnectable: connectable });
|
|
1347
|
+
}
|
|
1348
|
+
function StateNodeView({ data }) {
|
|
1349
|
+
const skin = useSkin();
|
|
1350
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1351
|
+
/* @__PURE__ */ jsx(
|
|
1352
|
+
skin.FlowStateNode,
|
|
1353
|
+
{
|
|
1354
|
+
...data.view,
|
|
1355
|
+
renderInputHandle: () => /* @__PURE__ */ jsx(Handle, { type: "target", position: Position.Left, id: INPUT_HANDLE, isConnectable: data.draggable }),
|
|
1356
|
+
renderExitHandle: (exitId) => exitHandle(exitId, data.draggable)
|
|
1357
|
+
}
|
|
1358
|
+
),
|
|
1359
|
+
data.draggable ? /* @__PURE__ */ jsx(Handle, { type: "source", position: Position.Right, id: NEW_EXIT_HANDLE, className: "flow-board__new-exit" }) : null
|
|
1360
|
+
] });
|
|
1361
|
+
}
|
|
1362
|
+
function AnyNodeView({ data }) {
|
|
1363
|
+
const skin = useSkin();
|
|
1364
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1365
|
+
/* @__PURE__ */ jsx(skin.FlowAnyStateNode, { ...data.view, renderExitHandle: (exitId) => exitHandle(exitId, data.draggable) }),
|
|
1366
|
+
data.draggable ? /* @__PURE__ */ jsx(Handle, { type: "source", position: Position.Right, id: NEW_EXIT_HANDLE, className: "flow-board__new-exit" }) : null
|
|
1367
|
+
] });
|
|
1368
|
+
}
|
|
1369
|
+
function EdgeView({ id, sourceX, sourceY, targetX, targetY, sourcePosition, targetPosition, data }) {
|
|
1370
|
+
const skin = useSkin();
|
|
1371
|
+
const [path, labelX, labelY] = getBezierPath({ sourceX, sourceY, sourcePosition, targetX, targetY, targetPosition });
|
|
1372
|
+
if (!data) return null;
|
|
1373
|
+
return /* @__PURE__ */ jsx(skin.FlowEdge, { ...data, edgeId: id, path, labelX, labelY });
|
|
1374
|
+
}
|
|
1375
|
+
var nodeTypes = { state: StateNodeView, any: AnyNodeView };
|
|
1376
|
+
var edgeTypes = { flow: EdgeView };
|
|
1377
|
+
function nodePosition(flow, nodeId, index) {
|
|
1378
|
+
const saved = flow.layout[nodeId];
|
|
1379
|
+
if (saved) return saved;
|
|
1380
|
+
if (nodeId === ANY_STATE_LAYOUT_KEY) return { x: 40, y: -220 };
|
|
1381
|
+
return { x: 40 + index % 4 * 330, y: 40 + Math.floor(index / 4) * 260 };
|
|
1382
|
+
}
|
|
1383
|
+
function countFor(issues, match) {
|
|
1384
|
+
const found = issues.filter(match);
|
|
1385
|
+
return { errors: found.filter((issue) => issue.level === "error").length, warnings: found.filter((issue) => issue.level === "warning").length };
|
|
1386
|
+
}
|
|
1387
|
+
function exitRows(flow, catalog, exits, issues, labels) {
|
|
1388
|
+
return exits.map((exit) => ({
|
|
1389
|
+
exitId: exit.id,
|
|
1390
|
+
kind: edgeKind(exit),
|
|
1391
|
+
text: conditionText(flow, exit.when, labels, catalog),
|
|
1392
|
+
target: targetTitle(flow, exit.to, labels),
|
|
1393
|
+
issues: countFor(issues, (issue) => issue.place.exitId === exit.id)
|
|
1394
|
+
}));
|
|
1395
|
+
}
|
|
1396
|
+
var ownerOf = (nodeId) => nodeId === ANY_STATE_LAYOUT_KEY ? null : nodeId;
|
|
1397
|
+
function runMarks(editor) {
|
|
1398
|
+
const sim = editor.simulation;
|
|
1399
|
+
if (editor.mode !== "run" || !sim) return null;
|
|
1400
|
+
const visited = new Set(sim.steps.map((step2) => step2.stateId));
|
|
1401
|
+
const next = new Set(simulationChoices(editor.flow, sim).flatMap((choice) => choice.target ? [choice.target] : []));
|
|
1402
|
+
const traversed = new Set(sim.steps.flatMap((step2) => step2.exitId ? [step2.exitId] : []));
|
|
1403
|
+
const state = stateById(editor.flow, sim.run.state);
|
|
1404
|
+
const live = new Set(state ? activeExits(editor.flow, state).map(({ exit }) => exit.id) : []);
|
|
1405
|
+
return { current: sim.run.state, visited, next, traversed, live };
|
|
1406
|
+
}
|
|
1407
|
+
function useBoardModel(editor) {
|
|
1408
|
+
const labels = useFlowLabels().editor;
|
|
1409
|
+
const { flow, catalog, issues, selection, mode, select, readOnly } = editor;
|
|
1410
|
+
return useMemo2(() => {
|
|
1411
|
+
const marks = runMarks(editor);
|
|
1412
|
+
const selectable = mode !== "run";
|
|
1413
|
+
const editable = selectable && !readOnly;
|
|
1414
|
+
const selectedExit = selection.kind === "state" || selection.kind === "any" ? selection.exitId : void 0;
|
|
1415
|
+
const nodes = flow.states.map((state, index) => ({
|
|
1416
|
+
id: state.id,
|
|
1417
|
+
type: "state",
|
|
1418
|
+
position: nodePosition(flow, state.id, index),
|
|
1419
|
+
draggable: editable,
|
|
1420
|
+
data: {
|
|
1421
|
+
draggable: editable,
|
|
1422
|
+
view: {
|
|
1423
|
+
stateId: state.id,
|
|
1424
|
+
title: state.title || state.id,
|
|
1425
|
+
goal: stateGoal(state.instructions),
|
|
1426
|
+
exits: exitRows(flow, catalog, state.exits, issues, labels),
|
|
1427
|
+
tags: state.tags,
|
|
1428
|
+
start: flow.start === state.id,
|
|
1429
|
+
final: isFinal(state),
|
|
1430
|
+
noSideQuestions: state.noSideQuestions,
|
|
1431
|
+
timing: stateTimingText(flow, state, labels),
|
|
1432
|
+
selected: selection.kind === "state" && selection.stateId === state.id,
|
|
1433
|
+
dimmed: marks !== null && !marks.visited.has(state.id) && !marks.next.has(state.id),
|
|
1434
|
+
run: marks === null ? null : marks.current === state.id ? "current" : marks.visited.has(state.id) ? "visited" : null,
|
|
1435
|
+
issues: countFor(issues, (issue) => issue.place.stateId === state.id),
|
|
1436
|
+
onSelect: () => selectable && select({ kind: "state", stateId: state.id })
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
}));
|
|
1440
|
+
if (flow.anyState.exits.length > 0 || flow.anyState.except.length > 0 || flow.layout[ANY_STATE_LAYOUT_KEY]) {
|
|
1441
|
+
nodes.push({
|
|
1442
|
+
id: ANY_STATE_LAYOUT_KEY,
|
|
1443
|
+
type: "any",
|
|
1444
|
+
position: nodePosition(flow, ANY_STATE_LAYOUT_KEY, 0),
|
|
1445
|
+
draggable: editable,
|
|
1446
|
+
data: {
|
|
1447
|
+
draggable: editable,
|
|
1448
|
+
view: {
|
|
1449
|
+
except: flow.anyState.except.map((id) => stateById(flow, id)?.title ?? id),
|
|
1450
|
+
exits: exitRows(flow, catalog, flow.anyState.exits, issues, labels),
|
|
1451
|
+
selected: selection.kind === "any",
|
|
1452
|
+
dimmed: marks !== null,
|
|
1453
|
+
issues: countFor(issues, (issue) => !issue.place.stateId && flow.anyState.exits.some((exit) => exit.id === issue.place.exitId)),
|
|
1454
|
+
onSelect: () => selectable && select({ kind: "any" })
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1457
|
+
});
|
|
1458
|
+
}
|
|
1459
|
+
const owners = [
|
|
1460
|
+
{ nodeId: ANY_STATE_LAYOUT_KEY, exits: flow.anyState.exits },
|
|
1461
|
+
...flow.states.map((state) => ({ nodeId: state.id, exits: state.exits }))
|
|
1462
|
+
];
|
|
1463
|
+
const edges = owners.flatMap(
|
|
1464
|
+
({ nodeId, exits }) => exits.filter((exit) => exit.to !== null && exit.to !== RETURN_TARGET && stateById(flow, exit.to)).map((exit) => ({
|
|
1465
|
+
id: `${nodeId}:${exit.id}`,
|
|
1466
|
+
type: "flow",
|
|
1467
|
+
source: nodeId,
|
|
1468
|
+
sourceHandle: exit.id,
|
|
1469
|
+
target: exit.to,
|
|
1470
|
+
targetHandle: INPUT_HANDLE,
|
|
1471
|
+
data: {
|
|
1472
|
+
kind: edgeKind(exit),
|
|
1473
|
+
label: conditionText(flow, exit.when, labels, catalog),
|
|
1474
|
+
selected: selectedExit === exit.id,
|
|
1475
|
+
dimmed: marks !== null && !marks.traversed.has(exit.id) && !(marks.live.has(exit.id) && nodeId === marks.current),
|
|
1476
|
+
traversed: marks?.traversed.has(exit.id) ?? false,
|
|
1477
|
+
onSelect: () => selectable && select(nodeId === ANY_STATE_LAYOUT_KEY ? { kind: "any", exitId: exit.id } : { kind: "state", stateId: nodeId, exitId: exit.id })
|
|
1478
|
+
}
|
|
1479
|
+
}))
|
|
1480
|
+
);
|
|
1481
|
+
return { nodes, edges };
|
|
1482
|
+
}, [flow, catalog, issues, selection, mode, readOnly, editor.simulation, labels, select]);
|
|
1483
|
+
}
|
|
1484
|
+
function BoardToggle(props) {
|
|
1485
|
+
const skin = useSkin();
|
|
1486
|
+
if (skin.FlowBoardToggle) return /* @__PURE__ */ jsx(skin.FlowBoardToggle, { ...props });
|
|
1487
|
+
return /* @__PURE__ */ jsx(
|
|
1488
|
+
"button",
|
|
1489
|
+
{
|
|
1490
|
+
type: "button",
|
|
1491
|
+
"aria-label": props.label,
|
|
1492
|
+
title: props.label,
|
|
1493
|
+
"aria-expanded": props.expanded,
|
|
1494
|
+
"data-flow-board-toggle": props.kind,
|
|
1495
|
+
onClick: props.onClick,
|
|
1496
|
+
style: {
|
|
1497
|
+
font: "inherit",
|
|
1498
|
+
fontSize: 12,
|
|
1499
|
+
padding: "6px 10px",
|
|
1500
|
+
borderRadius: 8,
|
|
1501
|
+
cursor: "pointer",
|
|
1502
|
+
color: "inherit",
|
|
1503
|
+
background: "var(--flow-board-handle-fill, #fff)",
|
|
1504
|
+
border: "1px solid var(--flow-board-handle, currentColor)"
|
|
1505
|
+
},
|
|
1506
|
+
children: props.showText ? props.label : props.kind === "legend" ? "?" : props.expanded ? "\xD7" : "+"
|
|
1507
|
+
}
|
|
1508
|
+
);
|
|
1509
|
+
}
|
|
1510
|
+
function ZoomControls() {
|
|
1511
|
+
const skin = useSkin();
|
|
1512
|
+
const { zoomIn, zoomOut, fitView } = useReactFlow();
|
|
1513
|
+
const zoom = useStore((store) => store.transform[2]);
|
|
1514
|
+
return /* @__PURE__ */ jsx(
|
|
1515
|
+
skin.FlowZoomControls,
|
|
1516
|
+
{
|
|
1517
|
+
zoom,
|
|
1518
|
+
onZoomIn: () => void zoomIn({ duration: 150 }),
|
|
1519
|
+
onZoomOut: () => void zoomOut({ duration: 150 }),
|
|
1520
|
+
onFit: () => void fitView({ duration: 200, padding: 0.15 })
|
|
1521
|
+
}
|
|
1522
|
+
);
|
|
1523
|
+
}
|
|
1524
|
+
function clientPoint(event) {
|
|
1525
|
+
if ("changedTouches" in event) {
|
|
1526
|
+
const touch = event.changedTouches[0];
|
|
1527
|
+
return touch ? { x: touch.clientX, y: touch.clientY } : null;
|
|
1528
|
+
}
|
|
1529
|
+
return { x: event.clientX, y: event.clientY };
|
|
1530
|
+
}
|
|
1531
|
+
var ignoreNodeClick = () => {
|
|
1532
|
+
};
|
|
1533
|
+
function Board({ editor, space }) {
|
|
1534
|
+
const spaceLabels = useFlowLabels().editor.board.space;
|
|
1535
|
+
const skin = useSkin();
|
|
1536
|
+
const model = useBoardModel(editor);
|
|
1537
|
+
const { screenToFlowPosition } = useReactFlow();
|
|
1538
|
+
const [nodes, setNodes] = useState2(model.nodes);
|
|
1539
|
+
useEffect2(() => {
|
|
1540
|
+
setNodes((previous) => {
|
|
1541
|
+
const byId = new Map(previous.map((node) => [node.id, node]));
|
|
1542
|
+
return model.nodes.map((node) => {
|
|
1543
|
+
const old = byId.get(node.id);
|
|
1544
|
+
if (!old) return node;
|
|
1545
|
+
return { ...node, measured: old.measured, ...old.dragging ? { position: old.position, dragging: true } : {} };
|
|
1546
|
+
});
|
|
1547
|
+
});
|
|
1548
|
+
}, [model.nodes]);
|
|
1549
|
+
const onNodesChange = useCallback2((changes) => setNodes((current) => applyNodeChanges(changes, current)), []);
|
|
1550
|
+
const { edit, connect, connectToNewState, select } = editor;
|
|
1551
|
+
const onConnect = useCallback2(
|
|
1552
|
+
(connection) => {
|
|
1553
|
+
const owner = ownerOf(connection.source);
|
|
1554
|
+
if (connection.target === ANY_STATE_LAYOUT_KEY) return;
|
|
1555
|
+
if (connection.sourceHandle === NEW_EXIT_HANDLE || !connection.sourceHandle) {
|
|
1556
|
+
edit((flow) => {
|
|
1557
|
+
const made = addExit(flow, owner, "judge");
|
|
1558
|
+
return connectExit(made.flow, owner, made.exitId, connection.target);
|
|
1559
|
+
});
|
|
1560
|
+
return;
|
|
1561
|
+
}
|
|
1562
|
+
connect(owner, connection.sourceHandle, connection.target);
|
|
1563
|
+
},
|
|
1564
|
+
[connect, edit]
|
|
1565
|
+
);
|
|
1566
|
+
const onConnectEnd = useCallback2(
|
|
1567
|
+
(event, state) => {
|
|
1568
|
+
if (state.isValid || !state.fromNode || !state.fromHandle) return;
|
|
1569
|
+
const owner = ownerOf(state.fromNode.id);
|
|
1570
|
+
const exitId = state.fromHandle.id === NEW_EXIT_HANDLE ? null : state.fromHandle.id ?? null;
|
|
1571
|
+
if (state.toNode && state.toNode.id !== ANY_STATE_LAYOUT_KEY) {
|
|
1572
|
+
onConnect({ source: state.fromNode.id, sourceHandle: exitId ?? NEW_EXIT_HANDLE, target: state.toNode.id, targetHandle: INPUT_HANDLE });
|
|
1573
|
+
return;
|
|
1574
|
+
}
|
|
1575
|
+
const point = clientPoint(event);
|
|
1576
|
+
if (!point) return;
|
|
1577
|
+
const at = screenToFlowPosition(point);
|
|
1578
|
+
connectToNewState(owner, exitId, { x: Math.round(at.x), y: Math.round(at.y - 40) });
|
|
1579
|
+
},
|
|
1580
|
+
[connectToNewState, onConnect, screenToFlowPosition]
|
|
1581
|
+
);
|
|
1582
|
+
const onNodeDragStop = useCallback2(
|
|
1583
|
+
(_event, node) => edit((flow) => placeNode(flow, node.id, node.position)),
|
|
1584
|
+
[edit]
|
|
1585
|
+
);
|
|
1586
|
+
const selectable = editor.mode !== "run";
|
|
1587
|
+
const editable = selectable && !editor.readOnly;
|
|
1588
|
+
return /* @__PURE__ */ jsxs(
|
|
1589
|
+
ReactFlow,
|
|
1590
|
+
{
|
|
1591
|
+
nodes,
|
|
1592
|
+
edges: model.edges,
|
|
1593
|
+
nodeTypes,
|
|
1594
|
+
edgeTypes,
|
|
1595
|
+
onNodesChange,
|
|
1596
|
+
onConnect,
|
|
1597
|
+
onConnectEnd,
|
|
1598
|
+
onNodeDragStop,
|
|
1599
|
+
onNodeClick: ignoreNodeClick,
|
|
1600
|
+
onPaneClick: () => selectable && select({ kind: "overview" }),
|
|
1601
|
+
nodesConnectable: editable,
|
|
1602
|
+
elementsSelectable: false,
|
|
1603
|
+
deleteKeyCode: null,
|
|
1604
|
+
selectionKeyCode: null,
|
|
1605
|
+
multiSelectionKeyCode: null,
|
|
1606
|
+
fitView: true,
|
|
1607
|
+
fitViewOptions: { padding: 0.15 },
|
|
1608
|
+
minZoom: 0.2,
|
|
1609
|
+
maxZoom: 2,
|
|
1610
|
+
proOptions: { hideAttribution: true },
|
|
1611
|
+
children: [
|
|
1612
|
+
/* @__PURE__ */ jsx(Background, { variant: BackgroundVariant.Dots, gap: 22, size: 1.2 }),
|
|
1613
|
+
!space.legendHidden && /* @__PURE__ */ jsx(Panel, { position: "bottom-left", className: "flow-board__legend", children: /* @__PURE__ */ jsxs("div", { className: "flow-board__legend-stack", style: { display: "flex", flexDirection: "column", alignItems: "flex-start", gap: 8 }, children: [
|
|
1614
|
+
space.legendOpen && /* @__PURE__ */ jsx(skin.FlowLegend, { kinds: LEGEND_KINDS }),
|
|
1615
|
+
/* @__PURE__ */ jsx(BoardToggle, { kind: "legend", label: spaceLabels.legend, expanded: space.legendOpen, showText: true, onClick: space.toggleLegend })
|
|
1616
|
+
] }) }),
|
|
1617
|
+
space.inspectorToggleable && /* @__PURE__ */ jsx(Panel, { position: "top-right", children: /* @__PURE__ */ jsx(
|
|
1618
|
+
BoardToggle,
|
|
1619
|
+
{
|
|
1620
|
+
kind: "inspector",
|
|
1621
|
+
label: space.inspectorOpen ? spaceLabels.hideInspector : spaceLabels.showInspector,
|
|
1622
|
+
expanded: space.inspectorOpen,
|
|
1623
|
+
onClick: space.toggleInspector
|
|
1624
|
+
}
|
|
1625
|
+
) }),
|
|
1626
|
+
/* @__PURE__ */ jsx(Panel, { position: "bottom-right", children: /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [
|
|
1627
|
+
!space.minimapHidden && /* @__PURE__ */ jsx("span", { className: "flow-board__minimap-toggle", style: { display: "contents" }, children: /* @__PURE__ */ jsx(
|
|
1628
|
+
BoardToggle,
|
|
1629
|
+
{
|
|
1630
|
+
kind: "minimap",
|
|
1631
|
+
label: space.minimapOpen ? spaceLabels.hideMinimap : spaceLabels.showMinimap,
|
|
1632
|
+
expanded: space.minimapOpen,
|
|
1633
|
+
onClick: space.toggleMinimap
|
|
1634
|
+
}
|
|
1635
|
+
) }),
|
|
1636
|
+
/* @__PURE__ */ jsx(ZoomControls, {})
|
|
1637
|
+
] }) }),
|
|
1638
|
+
space.minimapOpen && /* @__PURE__ */ jsx(MiniMap, { className: "flow-board__minimap", pannable: true, zoomable: true, position: "bottom-right", style: { marginBottom: 76 } })
|
|
1639
|
+
]
|
|
1640
|
+
}
|
|
1641
|
+
);
|
|
1642
|
+
}
|
|
1643
|
+
function FlowBoard({ editor, skin, space }) {
|
|
1644
|
+
return /* @__PURE__ */ jsxs(SkinContext.Provider, { value: skin, children: [
|
|
1645
|
+
/* @__PURE__ */ jsx("style", { children: FLOW_BOARD_CSS }),
|
|
1646
|
+
/* @__PURE__ */ jsx(ReactFlowProvider, { children: /* @__PURE__ */ jsx(Board, { editor, space }) }, editor.flow.id)
|
|
1647
|
+
] });
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1650
|
+
// src/react/editor/side-task-model.ts
|
|
1651
|
+
function freeTaskId(flow) {
|
|
1652
|
+
const used = new Set((flow.sideTasks ?? []).map((task) => task.id));
|
|
1653
|
+
let index = 1;
|
|
1654
|
+
while (used.has(`task_${index}`)) index += 1;
|
|
1655
|
+
return `task_${index}`;
|
|
1656
|
+
}
|
|
1657
|
+
function withTasks(flow, tasks) {
|
|
1658
|
+
if (tasks.length > 0) return { ...flow, sideTasks: tasks };
|
|
1659
|
+
const { sideTasks: _empty, ...rest } = flow;
|
|
1660
|
+
return rest;
|
|
1661
|
+
}
|
|
1662
|
+
function addSideTask(flow, text) {
|
|
1663
|
+
const sideTaskId = freeTaskId(flow);
|
|
1664
|
+
const task = { id: sideTaskId, text, maxAttempts: DEFAULT_SIDE_TASK_MAX_ATTEMPTS, perConversation: false };
|
|
1665
|
+
return { sideTaskId, flow: withTasks(flow, [...flow.sideTasks ?? [], task]) };
|
|
1666
|
+
}
|
|
1667
|
+
function updateSideTask(flow, sideTaskId, patch) {
|
|
1668
|
+
return withTasks(
|
|
1669
|
+
flow,
|
|
1670
|
+
(flow.sideTasks ?? []).map((task) => {
|
|
1671
|
+
if (task.id !== sideTaskId) return task;
|
|
1672
|
+
const next = { ...task, ...patch };
|
|
1673
|
+
for (const key of ["when", "field"]) if (next[key] === void 0 || next[key] === "") delete next[key];
|
|
1674
|
+
if (patch.maxAttempts !== void 0) next.maxAttempts = Math.max(1, Math.round(patch.maxAttempts) || 1);
|
|
1675
|
+
return next;
|
|
1676
|
+
})
|
|
1677
|
+
);
|
|
1678
|
+
}
|
|
1679
|
+
function removeSideTask(flow, sideTaskId) {
|
|
1680
|
+
return withTasks(flow, (flow.sideTasks ?? []).filter((task) => task.id !== sideTaskId));
|
|
1681
|
+
}
|
|
1682
|
+
function catalogFieldsToAdd(flow, catalog) {
|
|
1683
|
+
return (catalog.fields ?? []).filter((field) => !fieldById(flow, field.id));
|
|
1684
|
+
}
|
|
1685
|
+
function addCatalogField(flow, list, field) {
|
|
1686
|
+
if (fieldById(flow, field.id)) return flow;
|
|
1687
|
+
const base = { id: field.id, name: field.title, about: "", type: field.type, options: field.type === "choice" ? [...field.options ?? []] : [] };
|
|
1688
|
+
if (list === "clientCard") {
|
|
1689
|
+
const card = { ...base, refreshAfterMonths: null };
|
|
1690
|
+
return { ...flow, clientCard: [...flow.clientCard, card] };
|
|
1691
|
+
}
|
|
1692
|
+
return { ...flow, chatNotes: [...flow.chatNotes, base] };
|
|
1693
|
+
}
|
|
1694
|
+
function setSideTaskField(flow, sideTaskId, fieldId, catalog) {
|
|
1695
|
+
const fromApp = fieldId === null ? void 0 : catalog.fields?.find((field) => field.id === fieldId);
|
|
1696
|
+
const withField = fromApp ? addCatalogField(flow, "clientCard", fromApp) : flow;
|
|
1697
|
+
return updateSideTask(withField, sideTaskId, { field: fieldId ?? void 0 });
|
|
1698
|
+
}
|
|
1699
|
+
|
|
1700
|
+
// src/react/editor/use-flow-editor.ts
|
|
1701
|
+
import { useCallback as useCallback3, useMemo as useMemo3, useRef as useRef2, useState as useState3 } from "react";
|
|
1702
|
+
function countIssues(issues) {
|
|
1703
|
+
return {
|
|
1704
|
+
errors: issues.filter((issue) => issue.level === "error").length,
|
|
1705
|
+
warnings: issues.filter((issue) => issue.level === "warning").length
|
|
1706
|
+
};
|
|
1707
|
+
}
|
|
1708
|
+
function runAppChecks(flow, checks, failed) {
|
|
1709
|
+
if (!checks) return [];
|
|
1710
|
+
try {
|
|
1711
|
+
return appIssues(flow, checks(flow));
|
|
1712
|
+
} catch (error) {
|
|
1713
|
+
console.error("[flow] app checks failed", error);
|
|
1714
|
+
return [{ level: "warning", code: "app_check_failed", message: failed, place: {} }];
|
|
1715
|
+
}
|
|
1716
|
+
}
|
|
1717
|
+
var NO_ISSUES = [];
|
|
1718
|
+
var browserTimeZone = () => Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
|
|
1719
|
+
function useFlowEditor(options) {
|
|
1720
|
+
const { value: flow, onChange, catalog } = options;
|
|
1721
|
+
const labels = useFlowLabels().editor;
|
|
1722
|
+
const latest = useRef2(options);
|
|
1723
|
+
latest.current = options;
|
|
1724
|
+
const [selection, setSelection] = useState3({ kind: "overview" });
|
|
1725
|
+
const [mode, setModeState] = useState3("edit");
|
|
1726
|
+
const [dialog, setDialog] = useState3(null);
|
|
1727
|
+
const [jsonErrors, setJsonErrors] = useState3([]);
|
|
1728
|
+
const [simulation, setSimulation] = useState3(null);
|
|
1729
|
+
const [saving, setSaving] = useState3(false);
|
|
1730
|
+
const [savedJson, setSavedJson] = useState3(() => flowToJson(flow));
|
|
1731
|
+
const [rejection, setRejection] = useState3(null);
|
|
1732
|
+
const readOnly = options.readOnly === true;
|
|
1733
|
+
const checks = options.checks;
|
|
1734
|
+
const saveFailed = rejection !== null && rejection.flow === flow;
|
|
1735
|
+
const rejectedIssues = saveFailed ? rejection.issues : NO_ISSUES;
|
|
1736
|
+
const issues = useMemo3(
|
|
1737
|
+
() => [...checkFlow(flow, catalog), ...runAppChecks(flow, checks, labels.checkPanel.appCheckFailed), ...rejectedIssues],
|
|
1738
|
+
[flow, catalog, checks, labels, rejectedIssues]
|
|
1739
|
+
);
|
|
1740
|
+
const issueCount = useMemo3(() => countIssues(issues), [issues]);
|
|
1741
|
+
const json = useMemo3(() => flowToJson(flow), [flow]);
|
|
1742
|
+
const dirty = json !== savedJson;
|
|
1743
|
+
const visibleSelection = selection.kind === "state" && !flow.states.some((state) => state.id === selection.stateId) ? { kind: "overview" } : selection;
|
|
1744
|
+
const edit = useCallback3((change) => {
|
|
1745
|
+
if (latest.current.readOnly) return;
|
|
1746
|
+
onChange(change(latest.current.value));
|
|
1747
|
+
}, [onChange]);
|
|
1748
|
+
const select = useCallback3((next) => {
|
|
1749
|
+
setSelection(next);
|
|
1750
|
+
setModeState((current) => current === "check" ? "edit" : current);
|
|
1751
|
+
}, []);
|
|
1752
|
+
const addState2 = useCallback3(
|
|
1753
|
+
(at) => {
|
|
1754
|
+
if (latest.current.readOnly) return "";
|
|
1755
|
+
const made = addState(latest.current.value, { title: labels.board.newState, ...at ? { at } : {} });
|
|
1756
|
+
onChange(made.flow);
|
|
1757
|
+
setSelection({ kind: "state", stateId: made.stateId });
|
|
1758
|
+
return made.stateId;
|
|
1759
|
+
},
|
|
1760
|
+
[labels, onChange]
|
|
1761
|
+
);
|
|
1762
|
+
const connect = useCallback3(
|
|
1763
|
+
(owner, exitId, to) => {
|
|
1764
|
+
if (!latest.current.readOnly) onChange(connectExit(latest.current.value, owner, exitId, to));
|
|
1765
|
+
},
|
|
1766
|
+
[onChange]
|
|
1767
|
+
);
|
|
1768
|
+
const connectToNewState = useCallback3(
|
|
1769
|
+
(owner, exitId, at) => {
|
|
1770
|
+
if (latest.current.readOnly) return;
|
|
1771
|
+
const made = addState(latest.current.value, { title: labels.board.newState, at });
|
|
1772
|
+
let next = made.flow;
|
|
1773
|
+
let fromExit = exitId;
|
|
1774
|
+
if (fromExit === null) {
|
|
1775
|
+
const added = addExit(next, owner, "judge");
|
|
1776
|
+
next = added.flow;
|
|
1777
|
+
fromExit = added.exitId;
|
|
1778
|
+
}
|
|
1779
|
+
onChange(connectExit(next, owner, fromExit, made.stateId));
|
|
1780
|
+
setSelection({ kind: "state", stateId: made.stateId });
|
|
1781
|
+
},
|
|
1782
|
+
[labels, onChange]
|
|
1783
|
+
);
|
|
1784
|
+
const removeState2 = useCallback3(
|
|
1785
|
+
(stateId) => {
|
|
1786
|
+
if (!latest.current.readOnly) onChange(removeState(latest.current.value, stateId));
|
|
1787
|
+
},
|
|
1788
|
+
[onChange]
|
|
1789
|
+
);
|
|
1790
|
+
const restartSimulation = useCallback3(() => {
|
|
1791
|
+
const current = latest.current;
|
|
1792
|
+
setSimulation(startSimulation(current.value, { now: current.now?.() ?? Date.now(), timeZone: current.timeZone ?? browserTimeZone() }));
|
|
1793
|
+
}, []);
|
|
1794
|
+
const setMode = useCallback3(
|
|
1795
|
+
(next) => {
|
|
1796
|
+
if (next === "run") {
|
|
1797
|
+
const current = latest.current.value;
|
|
1798
|
+
if (!current.states.some((state) => state.id === current.start)) {
|
|
1799
|
+
setModeState("check");
|
|
1800
|
+
return;
|
|
1801
|
+
}
|
|
1802
|
+
restartSimulation();
|
|
1803
|
+
} else {
|
|
1804
|
+
setSimulation(null);
|
|
1805
|
+
}
|
|
1806
|
+
setModeState(next);
|
|
1807
|
+
},
|
|
1808
|
+
[restartSimulation]
|
|
1809
|
+
);
|
|
1810
|
+
const goToIssue = useCallback3((issue) => {
|
|
1811
|
+
const { stateId, exitId, fieldId, sideTaskId } = issue.place;
|
|
1812
|
+
if (stateId && flow.states.some((state) => state.id === stateId)) setSelection({ kind: "state", stateId, ...exitId ? { exitId } : {} });
|
|
1813
|
+
else if (exitId && flow.anyState.exits.some((exit) => exit.id === exitId)) setSelection({ kind: "any", exitId });
|
|
1814
|
+
else if (sideTaskId) setSelection({ kind: "overview", sideTaskId });
|
|
1815
|
+
else if (fieldId) setSelection({ kind: "card" });
|
|
1816
|
+
else setSelection({ kind: "overview" });
|
|
1817
|
+
setModeState("edit");
|
|
1818
|
+
}, [flow]);
|
|
1819
|
+
const simulate = useMemo3(() => {
|
|
1820
|
+
const change = (step2) => setSimulation((sim) => sim ? step2(sim) : sim);
|
|
1821
|
+
return {
|
|
1822
|
+
choose: (exitId) => change((sim) => chooseExit(latest.current.value, sim, exitId)),
|
|
1823
|
+
nothing: () => change((sim) => chooseNothing(latest.current.value, sim)),
|
|
1824
|
+
back: () => change(stepBack),
|
|
1825
|
+
restart: restartSimulation,
|
|
1826
|
+
setKnown: (fieldId, text) => change((sim) => setKnownValue(latest.current.value, sim, fieldId, text))
|
|
1827
|
+
};
|
|
1828
|
+
}, [restartSimulation]);
|
|
1829
|
+
const openJson = useCallback3(() => {
|
|
1830
|
+
setJsonErrors([]);
|
|
1831
|
+
setDialog("json");
|
|
1832
|
+
}, []);
|
|
1833
|
+
const closeDialog = useCallback3(() => setDialog(null), []);
|
|
1834
|
+
const loadJson = useCallback3(
|
|
1835
|
+
(text) => {
|
|
1836
|
+
if (latest.current.readOnly) return false;
|
|
1837
|
+
const loaded = loadFlowJson(text);
|
|
1838
|
+
if (!loaded.ok) {
|
|
1839
|
+
setJsonErrors(loaded.errors);
|
|
1840
|
+
return false;
|
|
1841
|
+
}
|
|
1842
|
+
setJsonErrors([]);
|
|
1843
|
+
setSelection({ kind: "overview" });
|
|
1844
|
+
setSimulation(null);
|
|
1845
|
+
setModeState("edit");
|
|
1846
|
+
setDialog(null);
|
|
1847
|
+
onChange(loaded.flow);
|
|
1848
|
+
return true;
|
|
1849
|
+
},
|
|
1850
|
+
[onChange]
|
|
1851
|
+
);
|
|
1852
|
+
const save = useCallback3(async () => {
|
|
1853
|
+
const { onSave, value, readOnly: viewOnly } = latest.current;
|
|
1854
|
+
if (!onSave || viewOnly) return;
|
|
1855
|
+
setSaving(true);
|
|
1856
|
+
try {
|
|
1857
|
+
const result = await onSave(value);
|
|
1858
|
+
if (result && !result.ok) {
|
|
1859
|
+
setRejection({ flow: value, issues: appIssues(value, (result.issues ?? []).map((issue) => ({ ...issue, code: "save_rejected" }))) });
|
|
1860
|
+
return;
|
|
1861
|
+
}
|
|
1862
|
+
setRejection(null);
|
|
1863
|
+
setSavedJson(flowToJson(value));
|
|
1864
|
+
} catch (error) {
|
|
1865
|
+
console.warn("[flow] onSave failed", error);
|
|
1866
|
+
setRejection({ flow: value, issues: [] });
|
|
1867
|
+
} finally {
|
|
1868
|
+
setSaving(false);
|
|
1869
|
+
}
|
|
1870
|
+
}, []);
|
|
1871
|
+
const migrate = useMigrateController(flow, options.migration, () => setDialog("migrate"));
|
|
1872
|
+
return {
|
|
1873
|
+
flow,
|
|
1874
|
+
catalog,
|
|
1875
|
+
issues,
|
|
1876
|
+
issueCount,
|
|
1877
|
+
selection: visibleSelection,
|
|
1878
|
+
select,
|
|
1879
|
+
mode,
|
|
1880
|
+
setMode,
|
|
1881
|
+
goToIssue,
|
|
1882
|
+
edit,
|
|
1883
|
+
addState: addState2,
|
|
1884
|
+
connect,
|
|
1885
|
+
connectToNewState,
|
|
1886
|
+
removeState: removeState2,
|
|
1887
|
+
simulation,
|
|
1888
|
+
simulate,
|
|
1889
|
+
dialog,
|
|
1890
|
+
openJson,
|
|
1891
|
+
closeDialog,
|
|
1892
|
+
json,
|
|
1893
|
+
jsonErrors,
|
|
1894
|
+
loadJson,
|
|
1895
|
+
save,
|
|
1896
|
+
saving,
|
|
1897
|
+
saveFailed,
|
|
1898
|
+
dirty,
|
|
1899
|
+
readOnly,
|
|
1900
|
+
migrate
|
|
1901
|
+
};
|
|
1902
|
+
}
|
|
1903
|
+
function useMigrateController(flow, migration, show) {
|
|
1904
|
+
const [chats, setChats] = useState3([]);
|
|
1905
|
+
const [targets, setTargets] = useState3({});
|
|
1906
|
+
const [busy, setBusy] = useState3(false);
|
|
1907
|
+
const [failed, setFailed] = useState3(false);
|
|
1908
|
+
const [moved, setMoved] = useState3(null);
|
|
1909
|
+
const rows = useMemo3(
|
|
1910
|
+
() => chats.map((entry) => {
|
|
1911
|
+
const current = flow.states.find((state) => state.id === entry.stateId);
|
|
1912
|
+
return {
|
|
1913
|
+
...entry,
|
|
1914
|
+
stateTitle: migration?.stateTitle?.(entry.versionId, entry.stateId) ?? current?.title ?? entry.stateId,
|
|
1915
|
+
removed: !current,
|
|
1916
|
+
target: current ? null : targets[entry.stateId] ?? null
|
|
1917
|
+
};
|
|
1918
|
+
}),
|
|
1919
|
+
[chats, flow, migration, targets]
|
|
1920
|
+
);
|
|
1921
|
+
const canMigrate = !busy && rows.length > 0 && rows.every((row) => !row.removed || row.chats === 0 || row.target !== null);
|
|
1922
|
+
const open = useCallback3(() => {
|
|
1923
|
+
if (!migration) return;
|
|
1924
|
+
show();
|
|
1925
|
+
setMoved(null);
|
|
1926
|
+
setFailed(false);
|
|
1927
|
+
setBusy(true);
|
|
1928
|
+
migration.chatsByState().then(setChats, () => setFailed(true)).finally(() => setBusy(false));
|
|
1929
|
+
}, [migration, show]);
|
|
1930
|
+
const setTarget = useCallback3((stateId, target) => setTargets((current) => ({ ...current, [stateId]: target })), []);
|
|
1931
|
+
const run = useCallback3(async () => {
|
|
1932
|
+
if (!migration) return;
|
|
1933
|
+
const map = Object.fromEntries(rows.filter((row) => row.removed && row.target).map((row) => [row.stateId, row.target]));
|
|
1934
|
+
setBusy(true);
|
|
1935
|
+
setFailed(false);
|
|
1936
|
+
try {
|
|
1937
|
+
const result = await migration.migrate(map);
|
|
1938
|
+
setMoved(result.moved);
|
|
1939
|
+
setChats([]);
|
|
1940
|
+
} catch {
|
|
1941
|
+
setFailed(true);
|
|
1942
|
+
} finally {
|
|
1943
|
+
setBusy(false);
|
|
1944
|
+
}
|
|
1945
|
+
}, [migration, rows]);
|
|
1946
|
+
if (!migration) return null;
|
|
1947
|
+
return { rows, busy, failed, moved, canMigrate, open, setTarget, run };
|
|
1948
|
+
}
|
|
1949
|
+
|
|
1950
|
+
// src/react/editor/flow-editor.tsx
|
|
1951
|
+
import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
1952
|
+
function FlowEditor({ skin, className, style, header: headerMode, legend, minimap, inspector, fullscreen, storageKey, ...options }) {
|
|
1953
|
+
const editor = useFlowEditor(options);
|
|
1954
|
+
const labels = useFlowLabels().editor;
|
|
1955
|
+
const { flow } = editor;
|
|
1956
|
+
const space = useBoardSpace(
|
|
1957
|
+
{
|
|
1958
|
+
...headerMode !== void 0 ? { header: headerMode } : {},
|
|
1959
|
+
...legend !== void 0 ? { legend } : {},
|
|
1960
|
+
...minimap !== void 0 ? { minimap } : {},
|
|
1961
|
+
...inspector !== void 0 ? { inspector } : {},
|
|
1962
|
+
...fullscreen !== void 0 ? { fullscreen } : {},
|
|
1963
|
+
...storageKey !== void 0 ? { storageKey } : {}
|
|
1964
|
+
},
|
|
1965
|
+
editor
|
|
1966
|
+
);
|
|
1967
|
+
const [root, setRoot] = useState4(null);
|
|
1968
|
+
const { setFullscreen } = space;
|
|
1969
|
+
const onTab = useFullscreenBehavior(space.fullscreen, () => setFullscreen(false), editor.dialog !== null, root);
|
|
1970
|
+
const header = /* @__PURE__ */ jsx2(
|
|
1971
|
+
skin.FlowHeader,
|
|
1972
|
+
{
|
|
1973
|
+
name: flow.name,
|
|
1974
|
+
onRename: (name) => editor.edit((current) => ({ ...current, name })),
|
|
1975
|
+
mode: editor.mode,
|
|
1976
|
+
onModeChange: editor.setMode,
|
|
1977
|
+
issues: editor.issueCount,
|
|
1978
|
+
onAddState: () => editor.addState(freeSpot(flow)),
|
|
1979
|
+
onOpenCard: () => editor.select({ kind: "card" }),
|
|
1980
|
+
onOpenJson: editor.openJson,
|
|
1981
|
+
...options.onSave && !editor.readOnly ? { onSave: () => void editor.save() } : {},
|
|
1982
|
+
saving: editor.saving,
|
|
1983
|
+
dirty: editor.dirty,
|
|
1984
|
+
saveFailed: editor.saveFailed,
|
|
1985
|
+
readOnly: editor.readOnly,
|
|
1986
|
+
...editor.migrate && !editor.readOnly ? { onOpenMigrate: editor.migrate.open } : {},
|
|
1987
|
+
...space.header === "compact" ? { compact: true } : {},
|
|
1988
|
+
...space.fullscreenAllowed ? { fullscreen: { active: space.fullscreen, onToggle: () => setFullscreen(!space.fullscreen) } } : {}
|
|
1989
|
+
}
|
|
1990
|
+
);
|
|
1991
|
+
return /* @__PURE__ */ jsx2(
|
|
1992
|
+
"div",
|
|
1993
|
+
{
|
|
1994
|
+
ref: setRoot,
|
|
1995
|
+
"data-flow-fullscreen": space.fullscreen ? "" : void 0,
|
|
1996
|
+
...space.fullscreen ? { role: "dialog", "aria-modal": true, "aria-label": labels.board.space.fullscreenRegion, onKeyDown: onTab } : {},
|
|
1997
|
+
style: space.fullscreen ? FULLSCREEN_STYLE : { display: "contents" },
|
|
1998
|
+
children: /* @__PURE__ */ jsx2(
|
|
1999
|
+
skin.FlowEditorFrame,
|
|
2000
|
+
{
|
|
2001
|
+
header,
|
|
2002
|
+
board: /* @__PURE__ */ jsx2(FlowBoard, { editor, skin, space }),
|
|
2003
|
+
panel: space.inspectorOpen ? /* @__PURE__ */ jsx2(EditorPanel, { editor, skin, labels }) : null,
|
|
2004
|
+
dialog: /* @__PURE__ */ jsx2(EditorDialog, { editor, skin }),
|
|
2005
|
+
...space.header === "compact" ? { compact: true } : {},
|
|
2006
|
+
...space.fullscreen ? { style: { height: "100%", minHeight: 0 } } : { ...className !== void 0 ? { className } : {}, ...style !== void 0 ? { style } : {} }
|
|
2007
|
+
}
|
|
2008
|
+
)
|
|
2009
|
+
}
|
|
2010
|
+
);
|
|
2011
|
+
}
|
|
2012
|
+
var FULLSCREEN_STYLE = {
|
|
2013
|
+
position: "fixed",
|
|
2014
|
+
inset: 0,
|
|
2015
|
+
zIndex: 2e3,
|
|
2016
|
+
height: "100dvh",
|
|
2017
|
+
overflow: "auto",
|
|
2018
|
+
background: "var(--canvas, Canvas)",
|
|
2019
|
+
color: "var(--foreground, CanvasText)"
|
|
2020
|
+
};
|
|
2021
|
+
function stateLinks(editor) {
|
|
2022
|
+
return editor.flow.states.map((state) => ({
|
|
2023
|
+
stateId: state.id,
|
|
2024
|
+
title: state.title || state.id,
|
|
2025
|
+
start: editor.flow.start === state.id,
|
|
2026
|
+
final: isFinal(state),
|
|
2027
|
+
onSelect: () => editor.select({ kind: "state", stateId: state.id })
|
|
2028
|
+
}));
|
|
2029
|
+
}
|
|
2030
|
+
function targetOptions(flow, labels, withReturn) {
|
|
2031
|
+
const states = flow.states.map((state) => ({ value: state.id, title: state.title || state.id }));
|
|
2032
|
+
return withReturn ? [...states, { value: RETURN_TARGET, title: labels.returnTarget }] : states;
|
|
2033
|
+
}
|
|
2034
|
+
function ExitCards({ editor, skin, labels, owner }) {
|
|
2035
|
+
const { flow, catalog } = editor;
|
|
2036
|
+
const state = owner === null ? void 0 : stateById(flow, owner);
|
|
2037
|
+
const durationExitId = state && skin.FlowStateTiming ? durationOf(state)?.exitId : void 0;
|
|
2038
|
+
const exits = (owner === null ? flow.anyState.exits : state?.exits ?? []).filter((exit) => exit.id !== durationExitId);
|
|
2039
|
+
const fields = [...flow.clientCard, ...flow.chatNotes];
|
|
2040
|
+
const eventNames = {
|
|
2041
|
+
button: [.../* @__PURE__ */ new Set([...catalogEventNames(catalog), ...catalog.widgets])],
|
|
2042
|
+
tool: [...catalog.tools],
|
|
2043
|
+
app: catalogEventNames(catalog)
|
|
2044
|
+
};
|
|
2045
|
+
const eventLabels = {};
|
|
2046
|
+
for (const name of eventNames.app) {
|
|
2047
|
+
const label = catalogEventLabel(catalog, name);
|
|
2048
|
+
if (label) eventLabels[name] = label;
|
|
2049
|
+
}
|
|
2050
|
+
const targets = targetOptions(flow, labels, owner !== null);
|
|
2051
|
+
return /* @__PURE__ */ jsx2(Fragment2, { children: exits.map((exit, index) => {
|
|
2052
|
+
const fieldType = exit.when.type === "data" ? fieldById(flow, exit.when.field)?.field.type : void 0;
|
|
2053
|
+
return /* @__PURE__ */ jsx2(
|
|
2054
|
+
skin.FlowExitCard,
|
|
2055
|
+
{
|
|
2056
|
+
exitId: exit.id,
|
|
2057
|
+
when: exit.when,
|
|
2058
|
+
to: exit.to,
|
|
2059
|
+
fields,
|
|
2060
|
+
ops: fieldType ? [...FLOW_OPS_BY_FIELD_TYPE[fieldType]] : [...FLOW_DATA_OPS],
|
|
2061
|
+
eventNames,
|
|
2062
|
+
eventLabels,
|
|
2063
|
+
targets,
|
|
2064
|
+
issues: editor.issues.filter((issue) => issue.place.exitId === exit.id),
|
|
2065
|
+
canMoveUp: index > 0,
|
|
2066
|
+
canMoveDown: index < exits.length - 1,
|
|
2067
|
+
onChange: (next) => editor.edit((current) => updateExit(current, owner, exit.id, { when: normalizeCondition(current, exit.when, next.when), to: next.to })),
|
|
2068
|
+
onMoveUp: () => editor.edit((current) => moveExit(current, owner, exit.id, -1)),
|
|
2069
|
+
onMoveDown: () => editor.edit((current) => moveExit(current, owner, exit.id, 1)),
|
|
2070
|
+
onDelete: () => editor.edit((current) => removeExit(current, owner, exit.id))
|
|
2071
|
+
},
|
|
2072
|
+
exit.id
|
|
2073
|
+
);
|
|
2074
|
+
}) });
|
|
2075
|
+
}
|
|
2076
|
+
function FieldRows({ editor, skin, list }) {
|
|
2077
|
+
const fields = editor.flow[list];
|
|
2078
|
+
return /* @__PURE__ */ jsx2(Fragment2, { children: fields.map((field, index) => /* @__PURE__ */ jsx2(
|
|
2079
|
+
skin.FlowFieldRow,
|
|
2080
|
+
{
|
|
2081
|
+
field,
|
|
2082
|
+
list,
|
|
2083
|
+
canMoveUp: index > 0,
|
|
2084
|
+
canMoveDown: index < fields.length - 1,
|
|
2085
|
+
issues: editor.issues.filter((issue) => issue.place.fieldId === field.id),
|
|
2086
|
+
onChange: (patch) => editor.edit((current) => updateField(current, list, field.id, patch)),
|
|
2087
|
+
onMoveUp: () => editor.edit((current) => moveField(current, list, field.id, -1)),
|
|
2088
|
+
onMoveDown: () => editor.edit((current) => moveField(current, list, field.id, 1)),
|
|
2089
|
+
onDelete: () => editor.edit((current) => removeField(current, list, field.id))
|
|
2090
|
+
},
|
|
2091
|
+
field.id
|
|
2092
|
+
)) });
|
|
2093
|
+
}
|
|
2094
|
+
function EditorPanel({ editor, skin, labels }) {
|
|
2095
|
+
const panel = /* @__PURE__ */ jsx2(EditablePanel, { editor, skin, labels });
|
|
2096
|
+
if (!editor.readOnly || editor.mode !== "edit") return panel;
|
|
2097
|
+
return /* @__PURE__ */ jsx2("fieldset", { disabled: true, "data-flow-read-only": "", className: "m-0 flex min-h-0 min-w-0 flex-1 flex-col border-0 p-0", children: panel });
|
|
2098
|
+
}
|
|
2099
|
+
function EditablePanel({ editor, skin, labels }) {
|
|
2100
|
+
const { flow, selection } = editor;
|
|
2101
|
+
if (editor.mode === "check") {
|
|
2102
|
+
return /* @__PURE__ */ jsx2(skin.FlowCheckPanel, { issues: editor.issues.map((issue) => ({ ...issue, onGo: () => editor.goToIssue(issue) })) });
|
|
2103
|
+
}
|
|
2104
|
+
if (editor.mode === "run" && editor.simulation) return /* @__PURE__ */ jsx2(RunPanel, { editor, skin, labels });
|
|
2105
|
+
if (selection.kind === "state") {
|
|
2106
|
+
const state = stateById(flow, selection.stateId);
|
|
2107
|
+
return /* @__PURE__ */ jsx2(
|
|
2108
|
+
skin.FlowStatePanel,
|
|
2109
|
+
{
|
|
2110
|
+
stateId: state.id,
|
|
2111
|
+
title: state.title,
|
|
2112
|
+
instructions: state.instructions,
|
|
2113
|
+
firstMessage: state.firstMessage,
|
|
2114
|
+
learn: state.learn,
|
|
2115
|
+
tools: state.tools,
|
|
2116
|
+
tags: state.tags,
|
|
2117
|
+
start: flow.start === state.id,
|
|
2118
|
+
noSideQuestions: state.noSideQuestions,
|
|
2119
|
+
fields: [...flow.clientCard, ...flow.chatNotes],
|
|
2120
|
+
catalogTools: [.../* @__PURE__ */ new Set([...flow.tools, ...editor.catalog.tools])],
|
|
2121
|
+
exits: /* @__PURE__ */ jsx2(ExitCards, { editor, skin, labels, owner: state.id }),
|
|
2122
|
+
timing: /* @__PURE__ */ jsx2(StateTiming, { editor, skin, labels, state }),
|
|
2123
|
+
issues: editor.issues.filter((issue) => issue.place.stateId === state.id && !issue.place.exitId),
|
|
2124
|
+
onChange: (patch) => editor.edit((current) => {
|
|
2125
|
+
const next = updateState(current, state.id, patch);
|
|
2126
|
+
const missing = (patch.tools ?? []).filter((tool) => !next.tools.includes(tool));
|
|
2127
|
+
return missing.length ? { ...next, tools: [...next.tools, ...missing] } : next;
|
|
2128
|
+
}),
|
|
2129
|
+
onMakeStart: () => editor.edit((current) => setStart(current, state.id)),
|
|
2130
|
+
onAddExit: (kind) => {
|
|
2131
|
+
const made = addExit(flow, state.id, kind);
|
|
2132
|
+
editor.edit(() => made.flow);
|
|
2133
|
+
editor.select({ kind: "state", stateId: state.id, exitId: made.exitId });
|
|
2134
|
+
},
|
|
2135
|
+
onDelete: () => {
|
|
2136
|
+
editor.removeState(state.id);
|
|
2137
|
+
editor.select({ kind: "overview" });
|
|
2138
|
+
}
|
|
2139
|
+
},
|
|
2140
|
+
state.id
|
|
2141
|
+
);
|
|
2142
|
+
}
|
|
2143
|
+
if (selection.kind === "any") {
|
|
2144
|
+
return /* @__PURE__ */ jsx2(
|
|
2145
|
+
skin.FlowAnyStatePanel,
|
|
2146
|
+
{
|
|
2147
|
+
except: flow.anyState.except,
|
|
2148
|
+
states: stateLinks(editor),
|
|
2149
|
+
onExceptChange: (except) => editor.edit((current) => setExcept(current, except)),
|
|
2150
|
+
exits: /* @__PURE__ */ jsx2(ExitCards, { editor, skin, labels, owner: null }),
|
|
2151
|
+
onAddExit: (kind) => editor.edit((current) => addExit(current, null, kind).flow)
|
|
2152
|
+
}
|
|
2153
|
+
);
|
|
2154
|
+
}
|
|
2155
|
+
if (selection.kind === "card") {
|
|
2156
|
+
return /* @__PURE__ */ jsx2(
|
|
2157
|
+
skin.FlowCardPanel,
|
|
2158
|
+
{
|
|
2159
|
+
clientCard: /* @__PURE__ */ jsx2(FieldRows, { editor, skin, list: "clientCard" }),
|
|
2160
|
+
chatNotes: /* @__PURE__ */ jsx2(FieldRows, { editor, skin, list: "chatNotes" }),
|
|
2161
|
+
empty: flow.clientCard.length === 0 && flow.chatNotes.length === 0,
|
|
2162
|
+
sideQuestionEveryMessages: flow.pace.sideQuestionEveryMessages,
|
|
2163
|
+
onPaceChange: (messages) => editor.edit((current) => ({ ...current, pace: { sideQuestionEveryMessages: Math.max(1, Math.round(messages) || 1) } })),
|
|
2164
|
+
onAddField: (list) => editor.edit((current) => addField(current, list, labels.cardPanel.newField).flow),
|
|
2165
|
+
appFields: catalogFieldsToAdd(flow, editor.catalog).map((field) => ({ id: field.id, title: field.title })),
|
|
2166
|
+
onAddAppField: (list, fieldId) => {
|
|
2167
|
+
const field = editor.catalog.fields?.find((candidate) => candidate.id === fieldId);
|
|
2168
|
+
if (field) editor.edit((current) => addCatalogField(current, list, field));
|
|
2169
|
+
}
|
|
2170
|
+
}
|
|
2171
|
+
);
|
|
2172
|
+
}
|
|
2173
|
+
return /* @__PURE__ */ jsx2(
|
|
2174
|
+
skin.FlowOverviewPanel,
|
|
2175
|
+
{
|
|
2176
|
+
name: flow.name,
|
|
2177
|
+
common: flow.common,
|
|
2178
|
+
onCommonChange: (common) => editor.edit((current) => ({ ...current, common })),
|
|
2179
|
+
counts: {
|
|
2180
|
+
states: flow.states.length,
|
|
2181
|
+
exits: flow.anyState.exits.length + flow.states.reduce((sum, state) => sum + state.exits.length, 0),
|
|
2182
|
+
finals: flow.states.filter(isFinal).length
|
|
2183
|
+
},
|
|
2184
|
+
states: stateLinks(editor),
|
|
2185
|
+
issues: editor.issueCount,
|
|
2186
|
+
sections: /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
2187
|
+
/* @__PURE__ */ jsx2(ScheduleSection, { editor, skin, labels }),
|
|
2188
|
+
/* @__PURE__ */ jsx2(SideTasksSection, { editor, skin, focused: selection.kind === "overview" ? selection.sideTaskId : void 0 })
|
|
2189
|
+
] })
|
|
2190
|
+
}
|
|
2191
|
+
);
|
|
2192
|
+
}
|
|
2193
|
+
var SCHEDULE_ISSUES = /* @__PURE__ */ new Set(["night_before_day_start", "unknown_deadline_state", "day_frame_soft_after_hard"]);
|
|
2194
|
+
var isScheduleIssue = (issue) => SCHEDULE_ISSUES.has(issue.code) || issue.code === "empty_nudges" && !issue.place.stateId;
|
|
2195
|
+
function NudgesEditor({ skin, nudges, optional, onChange }) {
|
|
2196
|
+
if (!skin.FlowNudgesEditor) return null;
|
|
2197
|
+
return /* @__PURE__ */ jsx2(skin.FlowNudgesEditor, { nudges, optional, onChange });
|
|
2198
|
+
}
|
|
2199
|
+
function ScheduleSection({ editor, skin, labels }) {
|
|
2200
|
+
if (!skin.FlowScheduleSection) return null;
|
|
2201
|
+
const { flow } = editor;
|
|
2202
|
+
return /* @__PURE__ */ jsx2(
|
|
2203
|
+
skin.FlowScheduleSection,
|
|
2204
|
+
{
|
|
2205
|
+
schedule: flow.schedule ?? null,
|
|
2206
|
+
afterEnd: flow.afterEnd ?? "reply",
|
|
2207
|
+
states: targetOptions(flow, labels, false),
|
|
2208
|
+
issues: editor.issues.filter(isScheduleIssue),
|
|
2209
|
+
nudges: /* @__PURE__ */ jsx2(
|
|
2210
|
+
NudgesEditor,
|
|
2211
|
+
{
|
|
2212
|
+
skin,
|
|
2213
|
+
nudges: flow.schedule?.nudges ?? null,
|
|
2214
|
+
optional: true,
|
|
2215
|
+
onChange: (nudges) => editor.edit((current) => updateScheduleNudges(current, nudges))
|
|
2216
|
+
}
|
|
2217
|
+
),
|
|
2218
|
+
onToggle: (on) => editor.edit((current) => setScheduleOn(current, on)),
|
|
2219
|
+
onChange: (patch) => editor.edit((current) => updateSchedule(current, patch)),
|
|
2220
|
+
onAfterEndChange: (mode) => editor.edit((current) => setAfterEnd(current, mode))
|
|
2221
|
+
}
|
|
2222
|
+
);
|
|
2223
|
+
}
|
|
2224
|
+
function SideTasksSection({ editor, skin, focused }) {
|
|
2225
|
+
const labels = useFlowLabels().editor;
|
|
2226
|
+
if (!skin.FlowSideTasksSection) return null;
|
|
2227
|
+
const { flow, catalog } = editor;
|
|
2228
|
+
return /* @__PURE__ */ jsx2(
|
|
2229
|
+
skin.FlowSideTasksSection,
|
|
2230
|
+
{
|
|
2231
|
+
tasks: (flow.sideTasks ?? []).map((task) => ({
|
|
2232
|
+
...task,
|
|
2233
|
+
issues: editor.issues.filter((issue) => issue.place.sideTaskId === task.id),
|
|
2234
|
+
focused: task.id === focused
|
|
2235
|
+
})),
|
|
2236
|
+
fields: [
|
|
2237
|
+
...[...flow.clientCard, ...flow.chatNotes].map((field) => ({ id: field.id, title: field.name || field.id, fromApp: false })),
|
|
2238
|
+
...catalogFieldsToAdd(flow, catalog).map((field) => ({ id: field.id, title: field.title, fromApp: true }))
|
|
2239
|
+
],
|
|
2240
|
+
builtInCity: flow.schedule !== void 0,
|
|
2241
|
+
onAdd: () => editor.edit((current) => addSideTask(current, labels.sideTasks.newTask).flow),
|
|
2242
|
+
onChange: (sideTaskId, patch) => editor.edit((current) => updateSideTask(current, sideTaskId, patch)),
|
|
2243
|
+
onFieldChange: (sideTaskId, fieldId) => editor.edit((current) => setSideTaskField(current, sideTaskId, fieldId, catalog)),
|
|
2244
|
+
onDelete: (sideTaskId) => editor.edit((current) => removeSideTask(current, sideTaskId))
|
|
2245
|
+
}
|
|
2246
|
+
);
|
|
2247
|
+
}
|
|
2248
|
+
function StateTiming({ editor, skin, labels, state }) {
|
|
2249
|
+
if (!skin.FlowStateTiming) return null;
|
|
2250
|
+
const { flow } = editor;
|
|
2251
|
+
const duration = durationOf(state);
|
|
2252
|
+
const mode = nudgesMode(state);
|
|
2253
|
+
const own = state.nudges && state.nudges !== "off" ? state.nudges : null;
|
|
2254
|
+
return /* @__PURE__ */ jsx2(
|
|
2255
|
+
skin.FlowStateTiming,
|
|
2256
|
+
{
|
|
2257
|
+
duration: duration && { amount: duration.amount, unit: duration.unit, to: duration.to },
|
|
2258
|
+
daysAvailable: flow.schedule !== void 0 || duration !== null && duration.unit !== "h",
|
|
2259
|
+
targets: targetOptions(flow, labels, true),
|
|
2260
|
+
issues: duration ? editor.issues.filter((issue) => issue.place.exitId === duration.exitId) : [],
|
|
2261
|
+
onDurationChange: (value) => editor.edit((current) => setDuration(current, state.id, value)),
|
|
2262
|
+
onDurationTargetChange: (to) => {
|
|
2263
|
+
if (!duration) return;
|
|
2264
|
+
editor.edit((current) => {
|
|
2265
|
+
const exit = stateById(current, state.id)?.exits.find((candidate) => candidate.id === duration.exitId);
|
|
2266
|
+
return exit ? updateExit(current, state.id, exit.id, { when: exit.when, to }) : current;
|
|
2267
|
+
});
|
|
2268
|
+
},
|
|
2269
|
+
nudgesMode: mode,
|
|
2270
|
+
onNudgesModeChange: (next) => editor.edit((current) => setStateNudges(current, state.id, next)),
|
|
2271
|
+
nudgesDays: stateNudgesDays(state),
|
|
2272
|
+
onNudgesDaysChange: (days) => editor.edit((current) => setStateNudgesDays(current, state.id, days)),
|
|
2273
|
+
nudges: own && /* @__PURE__ */ jsx2(
|
|
2274
|
+
NudgesEditor,
|
|
2275
|
+
{
|
|
2276
|
+
skin,
|
|
2277
|
+
nudges: own,
|
|
2278
|
+
optional: false,
|
|
2279
|
+
onChange: (nudges) => editor.edit((current) => nudges ? updateStateNudges(current, state.id, nudges) : setStateNudges(current, state.id, "off"))
|
|
2280
|
+
}
|
|
2281
|
+
)
|
|
2282
|
+
}
|
|
2283
|
+
);
|
|
2284
|
+
}
|
|
2285
|
+
function RunPanel({ editor, skin, labels }) {
|
|
2286
|
+
const { flow } = editor;
|
|
2287
|
+
const sim = editor.simulation;
|
|
2288
|
+
const state = stateById(flow, sim.run.state);
|
|
2289
|
+
const choices = useMemo4(() => simulationChoices(flow, sim), [flow, sim]);
|
|
2290
|
+
const question = nextQuestionField(flow, sim);
|
|
2291
|
+
const exitById = (exitId) => [...flow.anyState.exits, ...flow.states.flatMap((candidate) => candidate.exits)].find((exit) => exit.id === exitId);
|
|
2292
|
+
const knownRow = (field, value, stale) => ({
|
|
2293
|
+
fieldId: field.id,
|
|
2294
|
+
name: field.name || field.id,
|
|
2295
|
+
value: value === void 0 || value === null || value === "" ? null : String(value),
|
|
2296
|
+
stale,
|
|
2297
|
+
onChange: (text) => editor.simulate.setKnown(field.id, text)
|
|
2298
|
+
});
|
|
2299
|
+
const known = [
|
|
2300
|
+
...flow.clientCard.map((field) => {
|
|
2301
|
+
const entry = sim.card[field.id];
|
|
2302
|
+
return knownRow(field, entry?.value, entry !== void 0 && isFieldStale(field, entry.updatedAt, sim.now));
|
|
2303
|
+
}),
|
|
2304
|
+
...flow.chatNotes.map((field) => knownRow(field, sim.run.notes[field.id], false))
|
|
2305
|
+
];
|
|
2306
|
+
return /* @__PURE__ */ jsx2(
|
|
2307
|
+
skin.FlowRunPanel,
|
|
2308
|
+
{
|
|
2309
|
+
current: { stateId: sim.run.state, title: state?.title || sim.run.state, goal: stateGoal(state?.instructions ?? "") },
|
|
2310
|
+
choices: choices.map((choice) => {
|
|
2311
|
+
const exit = exitById(choice.exitId);
|
|
2312
|
+
return {
|
|
2313
|
+
exitId: choice.exitId,
|
|
2314
|
+
kind: edgeKind(exit),
|
|
2315
|
+
text: conditionText(flow, exit.when, labels, editor.catalog),
|
|
2316
|
+
target: choice.target ? targetTitle(flow, choice.target, labels) : null,
|
|
2317
|
+
onChoose: () => editor.simulate.choose(choice.exitId)
|
|
2318
|
+
};
|
|
2319
|
+
}),
|
|
2320
|
+
onNothing: editor.simulate.nothing,
|
|
2321
|
+
known,
|
|
2322
|
+
nextQuestion: question ? fieldById(flow, question)?.field.name ?? question : null,
|
|
2323
|
+
path: sim.steps.map((step2) => {
|
|
2324
|
+
const exit = step2.exitId ? exitById(step2.exitId) : void 0;
|
|
2325
|
+
return {
|
|
2326
|
+
stateId: step2.stateId,
|
|
2327
|
+
title: stateById(flow, step2.stateId)?.title || step2.stateId,
|
|
2328
|
+
via: step2.nothing ? labels.runPanel.nothingHappened : exit ? conditionText(flow, exit.when, labels, editor.catalog) : null
|
|
2329
|
+
};
|
|
2330
|
+
}),
|
|
2331
|
+
canGoBack: sim.previous !== null,
|
|
2332
|
+
onBack: editor.simulate.back,
|
|
2333
|
+
onRestart: editor.simulate.restart,
|
|
2334
|
+
onFinish: () => editor.setMode("edit")
|
|
2335
|
+
}
|
|
2336
|
+
);
|
|
2337
|
+
}
|
|
2338
|
+
function download(name, text) {
|
|
2339
|
+
const url = URL.createObjectURL(new Blob([text], { type: "application/json" }));
|
|
2340
|
+
const link = document.createElement("a");
|
|
2341
|
+
link.href = url;
|
|
2342
|
+
link.download = name;
|
|
2343
|
+
link.click();
|
|
2344
|
+
setTimeout(() => URL.revokeObjectURL(url), 0);
|
|
2345
|
+
}
|
|
2346
|
+
function EditorDialog({ editor, skin }) {
|
|
2347
|
+
const labels = useFlowLabels().editor;
|
|
2348
|
+
if (editor.dialog === "json") {
|
|
2349
|
+
return /* @__PURE__ */ jsx2(
|
|
2350
|
+
skin.FlowJsonDialog,
|
|
2351
|
+
{
|
|
2352
|
+
json: editor.json,
|
|
2353
|
+
errors: editor.jsonErrors,
|
|
2354
|
+
onCopy: () => void navigator.clipboard?.writeText(editor.json),
|
|
2355
|
+
onDownload: () => download(`${editor.flow.id}.json`, editor.json),
|
|
2356
|
+
onLoad: (text) => void editor.loadJson(text),
|
|
2357
|
+
onClose: editor.closeDialog,
|
|
2358
|
+
...editor.readOnly ? { readOnly: true } : {}
|
|
2359
|
+
}
|
|
2360
|
+
);
|
|
2361
|
+
}
|
|
2362
|
+
if (editor.dialog === "migrate" && editor.migrate) {
|
|
2363
|
+
const migrate = editor.migrate;
|
|
2364
|
+
return /* @__PURE__ */ jsx2(
|
|
2365
|
+
skin.FlowMigrateDialog,
|
|
2366
|
+
{
|
|
2367
|
+
rows: migrate.rows.map((row) => ({ ...row, onTargetChange: (stateId) => migrate.setTarget(row.stateId, stateId) })),
|
|
2368
|
+
targets: targetOptions(editor.flow, labels, false),
|
|
2369
|
+
busy: migrate.busy,
|
|
2370
|
+
failed: migrate.failed,
|
|
2371
|
+
moved: migrate.moved,
|
|
2372
|
+
canMigrate: migrate.canMigrate,
|
|
2373
|
+
onMigrate: () => void migrate.run(),
|
|
2374
|
+
onClose: editor.closeDialog
|
|
2375
|
+
}
|
|
2376
|
+
);
|
|
2377
|
+
}
|
|
2378
|
+
return null;
|
|
2379
|
+
}
|
|
2380
|
+
|
|
2381
|
+
// src/react/client-card/use-client-card.ts
|
|
2382
|
+
import { useCallback as useCallback4, useEffect as useEffect3, useRef as useRef3, useState as useState5 } from "react";
|
|
2383
|
+
function useClientCard(options) {
|
|
2384
|
+
const latest = useRef3(options);
|
|
2385
|
+
latest.current = options;
|
|
2386
|
+
const [state, setState] = useState5("loading");
|
|
2387
|
+
const [entries, setEntries] = useState5([]);
|
|
2388
|
+
const [busy, setBusy] = useState5(/* @__PURE__ */ new Set());
|
|
2389
|
+
const [forgettingAll, setForgettingAll] = useState5(false);
|
|
2390
|
+
const alive = useRef3(true);
|
|
2391
|
+
const load = useCallback4(async () => {
|
|
2392
|
+
setState("loading");
|
|
2393
|
+
try {
|
|
2394
|
+
const loaded = await latest.current.read();
|
|
2395
|
+
if (!alive.current) return;
|
|
2396
|
+
setEntries(loaded);
|
|
2397
|
+
setState("ready");
|
|
2398
|
+
} catch {
|
|
2399
|
+
if (alive.current) setState("failed");
|
|
2400
|
+
}
|
|
2401
|
+
}, []);
|
|
2402
|
+
useEffect3(() => {
|
|
2403
|
+
alive.current = true;
|
|
2404
|
+
void load();
|
|
2405
|
+
return () => {
|
|
2406
|
+
alive.current = false;
|
|
2407
|
+
};
|
|
2408
|
+
}, [load]);
|
|
2409
|
+
const forgetField = async (fieldId) => {
|
|
2410
|
+
setBusy((current) => new Set(current).add(fieldId));
|
|
2411
|
+
try {
|
|
2412
|
+
await latest.current.forget(fieldId);
|
|
2413
|
+
if (alive.current) setEntries((current) => current.filter((entry) => entry.fieldId !== fieldId));
|
|
2414
|
+
} catch {
|
|
2415
|
+
if (alive.current) setState("failed");
|
|
2416
|
+
} finally {
|
|
2417
|
+
if (alive.current)
|
|
2418
|
+
setBusy((current) => {
|
|
2419
|
+
const next = new Set(current);
|
|
2420
|
+
next.delete(fieldId);
|
|
2421
|
+
return next;
|
|
2422
|
+
});
|
|
2423
|
+
}
|
|
2424
|
+
};
|
|
2425
|
+
const forgetAll = async () => {
|
|
2426
|
+
setForgettingAll(true);
|
|
2427
|
+
try {
|
|
2428
|
+
await latest.current.forget("all");
|
|
2429
|
+
if (alive.current) setEntries([]);
|
|
2430
|
+
} catch {
|
|
2431
|
+
if (alive.current) setState("failed");
|
|
2432
|
+
} finally {
|
|
2433
|
+
if (alive.current) setForgettingAll(false);
|
|
2434
|
+
}
|
|
2435
|
+
};
|
|
2436
|
+
const labels = useFlowLabels().clientCard;
|
|
2437
|
+
const { fields, now = Date.now, timeZone, factName = labels.fact } = options;
|
|
2438
|
+
const dateFormat = new Intl.DateTimeFormat("ru-RU", { day: "2-digit", month: "2-digit", year: "numeric", ...timeZone && { timeZone } });
|
|
2439
|
+
const position = (fieldId) => {
|
|
2440
|
+
const index = fields.findIndex((field) => field.id === fieldId);
|
|
2441
|
+
return index === -1 ? Number.MAX_SAFE_INTEGER : index;
|
|
2442
|
+
};
|
|
2443
|
+
const views = [...entries].sort((left, right) => position(left.fieldId) - position(right.fieldId) || left.fieldId.localeCompare(right.fieldId)).map((entry) => {
|
|
2444
|
+
const field = fields.find((item) => item.id === entry.fieldId);
|
|
2445
|
+
const updatedAt = Date.parse(entry.updatedAt);
|
|
2446
|
+
return {
|
|
2447
|
+
fieldId: entry.fieldId,
|
|
2448
|
+
name: field?.name ?? (entry.fieldId.startsWith(FACT_PREFIX) ? factName : entry.fieldId),
|
|
2449
|
+
value: fieldValueText(entry.value),
|
|
2450
|
+
updatedAt: dateFormat.format(updatedAt),
|
|
2451
|
+
stale: field ? isFieldStale(field, updatedAt, now()) : false,
|
|
2452
|
+
busy: busy.has(entry.fieldId),
|
|
2453
|
+
onForget: () => void forgetField(entry.fieldId)
|
|
2454
|
+
};
|
|
2455
|
+
});
|
|
2456
|
+
return { state, fields: views, forgettingAll, onForgetAll: () => void forgetAll(), onRetry: () => void load() };
|
|
2457
|
+
}
|
|
2458
|
+
|
|
2459
|
+
// src/react/client-card/client-card-panel.tsx
|
|
2460
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
2461
|
+
function ClientCardPanel({ skin, ...options }) {
|
|
2462
|
+
return /* @__PURE__ */ jsx3(skin.ClientCardPanel, { ...useClientCard(options) });
|
|
2463
|
+
}
|
|
2464
|
+
|
|
2465
|
+
// src/react/test-chat/flow-test-chat.tsx
|
|
2466
|
+
import { createContext as createContext3, useContext as useContext3, useMemo as useMemo5 } from "react";
|
|
2467
|
+
|
|
2468
|
+
// src/react/test-chat/use-flow-test-chat.ts
|
|
2469
|
+
import { useCallback as useCallback5, useEffect as useEffect4, useRef as useRef4, useState as useState6 } from "react";
|
|
2470
|
+
var RUN_POLL_MS = 1e3;
|
|
2471
|
+
function runOf(run) {
|
|
2472
|
+
if (!run || typeof run.state !== "string") return null;
|
|
2473
|
+
const visited = new Set((run.path ?? []).flatMap((step2) => typeof step2.state === "string" && step2.state !== run.state ? [step2.state] : []));
|
|
2474
|
+
return { state: run.state, visited, day: run.day ?? null };
|
|
2475
|
+
}
|
|
2476
|
+
var sameDay = (left, right) => left === right || left !== null && right !== null && left.day === right.day && left.totalDays === right.totalDays && left.dayStartedAt === right.dayStartedAt;
|
|
2477
|
+
var sameRun = (left, right) => left === right || left !== null && right !== null && left.state === right.state && sameDay(left.day, right.day) && left.visited.size === right.visited.size && [...left.visited].every((id) => right.visited.has(id));
|
|
2478
|
+
var NOTHING_VISITED = /* @__PURE__ */ new Set();
|
|
2479
|
+
function useFlowTestChat(connection, flow) {
|
|
2480
|
+
const [opened, setOpened] = useState6(false);
|
|
2481
|
+
const [chatId, setChatId] = useState6(null);
|
|
2482
|
+
const [timeOffset, setTimeOffset] = useState6(0);
|
|
2483
|
+
const [busy, setBusy] = useState6(false);
|
|
2484
|
+
const [failed, setFailed] = useState6(false);
|
|
2485
|
+
const [run, setRun] = useState6(null);
|
|
2486
|
+
const latest = useRef4({ connection, flow, chatId });
|
|
2487
|
+
latest.current = { connection, flow, chatId };
|
|
2488
|
+
const refresh = useCallback5(async (id) => {
|
|
2489
|
+
try {
|
|
2490
|
+
const next = runOf(await latest.current.connection.readRun(id));
|
|
2491
|
+
if (latest.current.chatId === id) setRun((current) => sameRun(current, next) ? current : next);
|
|
2492
|
+
} catch (error) {
|
|
2493
|
+
console.error("[flow] test chat: run not read", error);
|
|
2494
|
+
}
|
|
2495
|
+
}, []);
|
|
2496
|
+
useEffect4(() => {
|
|
2497
|
+
if (!opened || chatId === null) return;
|
|
2498
|
+
void refresh(chatId);
|
|
2499
|
+
const timer = setInterval(() => void refresh(chatId), RUN_POLL_MS);
|
|
2500
|
+
return () => clearInterval(timer);
|
|
2501
|
+
}, [opened, chatId, refresh]);
|
|
2502
|
+
const work = useCallback5(async (job) => {
|
|
2503
|
+
setBusy(true);
|
|
2504
|
+
setFailed(false);
|
|
2505
|
+
try {
|
|
2506
|
+
await job();
|
|
2507
|
+
} catch (error) {
|
|
2508
|
+
console.error("[flow] test chat:", error);
|
|
2509
|
+
setFailed(true);
|
|
2510
|
+
} finally {
|
|
2511
|
+
setBusy(false);
|
|
2512
|
+
}
|
|
2513
|
+
}, []);
|
|
2514
|
+
const start = useCallback5(
|
|
2515
|
+
() => work(async () => {
|
|
2516
|
+
const { chatId: next } = await latest.current.connection.start(latest.current.flow);
|
|
2517
|
+
setRun(null);
|
|
2518
|
+
setChatId(next);
|
|
2519
|
+
setTimeOffset(0);
|
|
2520
|
+
}),
|
|
2521
|
+
[work]
|
|
2522
|
+
);
|
|
2523
|
+
const shift2 = (move) => {
|
|
2524
|
+
const current = latest.current.chatId;
|
|
2525
|
+
if (current === null) return;
|
|
2526
|
+
void work(async () => {
|
|
2527
|
+
const { timeOffset: next } = await move(current);
|
|
2528
|
+
setTimeOffset(next);
|
|
2529
|
+
await refresh(current);
|
|
2530
|
+
});
|
|
2531
|
+
};
|
|
2532
|
+
return {
|
|
2533
|
+
opened,
|
|
2534
|
+
chatId,
|
|
2535
|
+
timeOffset,
|
|
2536
|
+
busy,
|
|
2537
|
+
failed,
|
|
2538
|
+
stateId: chatId === null ? null : run?.state ?? flow.start,
|
|
2539
|
+
visited: run?.visited ?? NOTHING_VISITED,
|
|
2540
|
+
day: chatId === null ? null : run?.day ?? null,
|
|
2541
|
+
open() {
|
|
2542
|
+
setOpened(true);
|
|
2543
|
+
if (latest.current.chatId === null) void start();
|
|
2544
|
+
},
|
|
2545
|
+
close: () => setOpened(false),
|
|
2546
|
+
reset: () => void start(),
|
|
2547
|
+
advance: (ms) => shift2((id) => latest.current.connection.advance(id, ms)),
|
|
2548
|
+
nextDay: connection.advanceToNextDay ? () => shift2((id) => latest.current.connection.advanceToNextDay(id)) : null
|
|
2549
|
+
};
|
|
2550
|
+
}
|
|
2551
|
+
|
|
2552
|
+
// src/react/test-chat/flow-test-chat.tsx
|
|
2553
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
2554
|
+
var MINUTE = 6e4;
|
|
2555
|
+
var HOUR = 60 * MINUTE;
|
|
2556
|
+
var DAY = 24 * HOUR;
|
|
2557
|
+
var DEFAULT_ADVANCE_STEPS = [20 * MINUTE, 3 * HOUR, DAY];
|
|
2558
|
+
function formatDuration(ms, units) {
|
|
2559
|
+
const minutes = Math.round(ms / MINUTE);
|
|
2560
|
+
const parts = [
|
|
2561
|
+
[Math.floor(minutes / (24 * 60)), units.day],
|
|
2562
|
+
[Math.floor(minutes / 60) % 24, units.hour],
|
|
2563
|
+
[minutes % 60, units.minute]
|
|
2564
|
+
];
|
|
2565
|
+
return parts.filter(([amount]) => amount > 0).map(([amount, unit]) => `${amount} ${unit}`).join(" ");
|
|
2566
|
+
}
|
|
2567
|
+
function formatClockOffset(ms, labels = russianFlowTestChatLabels) {
|
|
2568
|
+
const { noOffset, units } = labels;
|
|
2569
|
+
const text = formatDuration(ms, units);
|
|
2570
|
+
return text ? `+${text}` : noOffset;
|
|
2571
|
+
}
|
|
2572
|
+
function formatDayLine(day, at, timeZone, labels = russianFlowTestChatLabels) {
|
|
2573
|
+
if (!day) return null;
|
|
2574
|
+
const time = new Intl.DateTimeFormat("ru-RU", { timeZone, hour: "2-digit", minute: "2-digit", hourCycle: "h23" }).format(at);
|
|
2575
|
+
return `${day.day === 0 ? labels.beforeFirstDay : labels.day(day.day, day.totalDays)} \xB7 ${time}`;
|
|
2576
|
+
}
|
|
2577
|
+
var browserTimeZone2 = () => Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
|
|
2578
|
+
var TestChatContext = createContext3(null);
|
|
2579
|
+
function useTestChatView() {
|
|
2580
|
+
const view = useContext3(TestChatContext);
|
|
2581
|
+
if (!view) throw new Error("Test chat parts render only inside FlowTestChat.");
|
|
2582
|
+
return view;
|
|
2583
|
+
}
|
|
2584
|
+
function withTestChat(skin) {
|
|
2585
|
+
function Header(props) {
|
|
2586
|
+
const { controller } = useTestChatView();
|
|
2587
|
+
return /* @__PURE__ */ jsx4(skin.FlowHeader, { ...props, onOpenTestChat: controller.open });
|
|
2588
|
+
}
|
|
2589
|
+
function Frame(props) {
|
|
2590
|
+
const { panel } = useTestChatView();
|
|
2591
|
+
return /* @__PURE__ */ jsx4(skin.FlowEditorFrame, { ...props, panel: panel ?? props.panel });
|
|
2592
|
+
}
|
|
2593
|
+
function StateNode(props) {
|
|
2594
|
+
const { controller } = useTestChatView();
|
|
2595
|
+
if (!controller.opened || controller.stateId === null) return /* @__PURE__ */ jsx4(skin.FlowStateNode, { ...props });
|
|
2596
|
+
const run = controller.stateId === props.stateId ? "current" : controller.visited.has(props.stateId) ? "visited" : null;
|
|
2597
|
+
return /* @__PURE__ */ jsx4(skin.FlowStateNode, { ...props, run, dimmed: false });
|
|
2598
|
+
}
|
|
2599
|
+
return { ...skin, FlowHeader: Header, FlowEditorFrame: Frame, FlowStateNode: StateNode };
|
|
2600
|
+
}
|
|
2601
|
+
function FlowTestChat({ testChat, advanceSteps = DEFAULT_ADVANCE_STEPS, skin, ...editor }) {
|
|
2602
|
+
const labels = useFlowLabels().testChat;
|
|
2603
|
+
const controller = useFlowTestChat(testChat, editor.value);
|
|
2604
|
+
const testSkin = useMemo5(() => withTestChat(skin), [skin]);
|
|
2605
|
+
const state = controller.stateId === null ? void 0 : stateById(editor.value, controller.stateId);
|
|
2606
|
+
const panel = controller.opened ? /* @__PURE__ */ jsx4(
|
|
2607
|
+
skin.FlowTestChatFrame,
|
|
2608
|
+
{
|
|
2609
|
+
stateTitle: state?.title || controller.stateId || "",
|
|
2610
|
+
clock: formatClockOffset(controller.timeOffset, labels),
|
|
2611
|
+
advanceSteps: advanceSteps.map((ms) => ({ label: formatDuration(ms, labels.units), ms })),
|
|
2612
|
+
onAdvance: controller.advance,
|
|
2613
|
+
day: formatDayLine(controller.day, (editor.now?.() ?? Date.now()) + controller.timeOffset, editor.timeZone ?? browserTimeZone2(), labels),
|
|
2614
|
+
...controller.nextDay && editor.value.schedule ? { onNextDay: controller.nextDay } : {},
|
|
2615
|
+
busy: controller.busy,
|
|
2616
|
+
failed: controller.failed,
|
|
2617
|
+
onReset: controller.reset,
|
|
2618
|
+
onClose: controller.close,
|
|
2619
|
+
children: controller.chatId === null ? null : testChat.renderChat(controller.chatId)
|
|
2620
|
+
}
|
|
2621
|
+
) : null;
|
|
2622
|
+
return /* @__PURE__ */ jsx4(TestChatContext.Provider, { value: { controller, panel }, children: /* @__PURE__ */ jsx4(FlowEditor, { skin: testSkin, ...editor }) });
|
|
2623
|
+
}
|
|
2624
|
+
export {
|
|
2625
|
+
ClientCardPanel,
|
|
2626
|
+
DEFAULT_ADVANCE_STEPS,
|
|
2627
|
+
DEFAULT_NUDGES,
|
|
2628
|
+
DEFAULT_SCHEDULE,
|
|
2629
|
+
FlowEditor,
|
|
2630
|
+
FlowLabelsProvider,
|
|
2631
|
+
FlowTestChat,
|
|
2632
|
+
chooseExit,
|
|
2633
|
+
chooseNothing,
|
|
2634
|
+
conditionText,
|
|
2635
|
+
englishFlowLabels,
|
|
2636
|
+
formatClockOffset,
|
|
2637
|
+
formatDayLine,
|
|
2638
|
+
formatDuration,
|
|
2639
|
+
nextQuestionField,
|
|
2640
|
+
russianFlowLabels,
|
|
2641
|
+
setKnownValue,
|
|
2642
|
+
simulationChoices,
|
|
2643
|
+
startSimulation,
|
|
2644
|
+
stepBack,
|
|
2645
|
+
useClientCard,
|
|
2646
|
+
useFlowEditor,
|
|
2647
|
+
useFlowLabels,
|
|
2648
|
+
useFlowTestChat
|
|
2649
|
+
};
|
|
2650
|
+
//# sourceMappingURL=react.js.map
|