@nyaruka/temba-components 0.156.12 → 0.156.14
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 +14 -0
- package/dist/temba-components.js +399 -219
- package/dist/temba-components.js.map +1 -1
- package/package.json +1 -1
- package/src/display/Button.ts +9 -4
- package/src/display/Options.ts +13 -0
- package/src/flow/AutoTranslate.ts +726 -0
- package/src/flow/Editor.ts +49 -299
- package/src/flow/EditorToolbar.ts +38 -2
- package/src/flow/flow-translations.ts +229 -0
- package/src/flow/flow-utils.ts +17 -0
- package/src/flow/nodes/split_by_llm.ts +3 -1
- package/src/flow/nodes/split_by_llm_categorize.ts +3 -1
- package/src/flow/types.ts +2 -1
- package/src/form/RichEditor.ts +4 -1
- package/src/layout/Dialog.ts +24 -3
- package/static/api/llms.json +11 -2
- package/temba-modules.ts +2 -0
package/src/flow/flow-utils.ts
CHANGED
|
@@ -15,3 +15,20 @@ export function shouldExcludeFlow(flow: any): boolean {
|
|
|
15
15
|
|
|
16
16
|
return false;
|
|
17
17
|
}
|
|
18
|
+
|
|
19
|
+
export type LLMRole = 'engine' | 'editing';
|
|
20
|
+
|
|
21
|
+
export interface LLMModel {
|
|
22
|
+
uuid: string;
|
|
23
|
+
name: string;
|
|
24
|
+
type?: string;
|
|
25
|
+
description?: string;
|
|
26
|
+
roles?: LLMRole[];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function hasLLMRole(
|
|
30
|
+
model: { roles?: string[] } | null | undefined,
|
|
31
|
+
role: LLMRole
|
|
32
|
+
): boolean {
|
|
33
|
+
return model?.roles?.includes(role) ?? false;
|
|
34
|
+
}
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
renderLineItem,
|
|
9
9
|
getLlmIcon
|
|
10
10
|
} from '../utils';
|
|
11
|
+
import { LLMModel, hasLLMRole } from '../flow-utils';
|
|
11
12
|
|
|
12
13
|
export const split_by_llm: NodeConfig = {
|
|
13
14
|
type: 'split_by_llm',
|
|
@@ -47,7 +48,8 @@ export const split_by_llm: NodeConfig = {
|
|
|
47
48
|
searchable: true,
|
|
48
49
|
valueKey: 'uuid',
|
|
49
50
|
nameKey: 'name',
|
|
50
|
-
placeholder: 'Select an LLM...'
|
|
51
|
+
placeholder: 'Select an LLM...',
|
|
52
|
+
shouldExclude: (option: LLMModel) => !hasLLMRole(option, 'engine')
|
|
51
53
|
},
|
|
52
54
|
input: {
|
|
53
55
|
type: 'text',
|
|
@@ -3,6 +3,7 @@ import { CallLLM, Node } from '../../store/flow-definition';
|
|
|
3
3
|
import { generateUUID, createMultiCategoryRouter } from '../../utils';
|
|
4
4
|
import { html } from 'lit';
|
|
5
5
|
import { validateWith } from '../utils';
|
|
6
|
+
import { LLMModel, hasLLMRole } from '../flow-utils';
|
|
6
7
|
|
|
7
8
|
export const split_by_llm_categorize: NodeConfig = {
|
|
8
9
|
type: 'split_by_llm_categorize',
|
|
@@ -19,7 +20,8 @@ export const split_by_llm_categorize: NodeConfig = {
|
|
|
19
20
|
endpoint: '/api/internal/llms.json',
|
|
20
21
|
valueKey: 'uuid',
|
|
21
22
|
nameKey: 'name',
|
|
22
|
-
placeholder: 'Select an LLM...'
|
|
23
|
+
placeholder: 'Select an LLM...',
|
|
24
|
+
shouldExclude: (option: LLMModel) => !hasLLMRole(option, 'engine')
|
|
23
25
|
},
|
|
24
26
|
input: {
|
|
25
27
|
type: 'text',
|
package/src/flow/types.ts
CHANGED
|
@@ -50,7 +50,8 @@ export const CONTEXT_MENU_SHORTCUTS: Record<FlowType, ContextMenuShortcut[]> = {
|
|
|
50
50
|
export const Features = {
|
|
51
51
|
AI: 'ai',
|
|
52
52
|
AIRTIME: 'airtime',
|
|
53
|
-
LOCATIONS: 'locations'
|
|
53
|
+
LOCATIONS: 'locations',
|
|
54
|
+
AUTO_TRANSLATE: 'auto_translate'
|
|
54
55
|
} as const;
|
|
55
56
|
|
|
56
57
|
export type Feature = (typeof Features)[keyof typeof Features];
|
package/src/form/RichEditor.ts
CHANGED
|
@@ -434,7 +434,10 @@ export class RichEditor extends FieldElement {
|
|
|
434
434
|
}
|
|
435
435
|
|
|
436
436
|
private handleKeydown(e: KeyboardEvent): void {
|
|
437
|
-
|
|
437
|
+
// On macOS, Cmd is the undo/redo modifier; leave Ctrl alone so Cocoa
|
|
438
|
+
// text bindings like Ctrl+K (kill) and Ctrl+Y (yank) keep working.
|
|
439
|
+
const isMac = /Mac|iPhone|iPad/.test(navigator.userAgent);
|
|
440
|
+
const mod = isMac ? e.metaKey : e.ctrlKey;
|
|
438
441
|
|
|
439
442
|
// Undo/redo via keyboard: preventDefault here suppresses the subsequent
|
|
440
443
|
// beforeinput event, avoiding double-handling
|
package/src/layout/Dialog.ts
CHANGED
|
@@ -142,11 +142,26 @@ export class Dialog extends ResizeElement {
|
|
|
142
142
|
flex-direction: row;
|
|
143
143
|
align-items: center;
|
|
144
144
|
font-size: 20px;
|
|
145
|
-
padding: 12px 20px;
|
|
145
|
+
padding: 12px 20px var(--dialog-header-padding-bottom, 12px);
|
|
146
146
|
color: var(--header-text);
|
|
147
147
|
background: var(--header-bg);
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
+
/* "flat" variant: header/footer share the body's white background
|
|
151
|
+
and lose their inner padding so the dialog reads as one
|
|
152
|
+
continuous surface. Use for content-focused dialogs that don't
|
|
153
|
+
need the tinted chrome. */
|
|
154
|
+
:host([variant='flat']) .header-text {
|
|
155
|
+
background: #fff;
|
|
156
|
+
color: var(--color-text);
|
|
157
|
+
padding-bottom: 0;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
:host([variant='flat']) .dialog-footer {
|
|
161
|
+
background: #fff;
|
|
162
|
+
padding-top: 0;
|
|
163
|
+
}
|
|
164
|
+
|
|
150
165
|
.header-text .title {
|
|
151
166
|
flex-grow: 1;
|
|
152
167
|
}
|
|
@@ -157,8 +172,11 @@ export class Dialog extends ResizeElement {
|
|
|
157
172
|
}
|
|
158
173
|
|
|
159
174
|
.dialog-footer {
|
|
160
|
-
background: var(
|
|
161
|
-
|
|
175
|
+
background: var(
|
|
176
|
+
--dialog-footer-background,
|
|
177
|
+
var(--color-primary-light)
|
|
178
|
+
);
|
|
179
|
+
padding: var(--dialog-footer-padding-top, 10px) 10px 10px;
|
|
162
180
|
display: flex;
|
|
163
181
|
flex-flow: row;
|
|
164
182
|
align-items: center;
|
|
@@ -227,6 +245,9 @@ export class Dialog extends ResizeElement {
|
|
|
227
245
|
@property({ type: Boolean })
|
|
228
246
|
hideOnClick: boolean;
|
|
229
247
|
|
|
248
|
+
@property({ type: String, reflect: true })
|
|
249
|
+
variant: string;
|
|
250
|
+
|
|
230
251
|
@property({ type: Boolean })
|
|
231
252
|
noFocus: boolean;
|
|
232
253
|
|
package/static/api/llms.json
CHANGED
|
@@ -6,13 +6,22 @@
|
|
|
6
6
|
"uuid": "2399e7d6-fcdf-4e47-a835-f3bdb7f80938",
|
|
7
7
|
"name": "GPT 4.1",
|
|
8
8
|
"type": "openai",
|
|
9
|
-
"description": "General-purpose reasoning model"
|
|
9
|
+
"description": "General-purpose reasoning model",
|
|
10
|
+
"roles": ["engine", "editing"]
|
|
10
11
|
},
|
|
11
12
|
{
|
|
12
13
|
"uuid": "4399e7d6-fcdf-4e47-a835-f3bdb7f80938",
|
|
13
14
|
"name": "GPT 5",
|
|
14
15
|
"type": "openai",
|
|
15
|
-
"description": "Latest experimental model"
|
|
16
|
+
"description": "Latest experimental model",
|
|
17
|
+
"roles": ["engine"]
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"uuid": "5499e7d6-fcdf-4e47-a835-f3bdb7f80938",
|
|
21
|
+
"name": "GPT 4.1 Mini",
|
|
22
|
+
"type": "openai",
|
|
23
|
+
"description": "Lightweight model for editing tasks",
|
|
24
|
+
"roles": ["editing"]
|
|
16
25
|
}
|
|
17
26
|
]
|
|
18
27
|
}
|
package/temba-modules.ts
CHANGED
|
@@ -85,6 +85,7 @@ import { Simulator } from './src/simulator/Simulator';
|
|
|
85
85
|
import { FlowSearch } from './src/flow/FlowSearch';
|
|
86
86
|
import { IssuesWindow } from './src/flow/IssuesWindow';
|
|
87
87
|
import { RevisionsWindow } from './src/flow/RevisionsWindow';
|
|
88
|
+
import { AutoTranslate } from './src/flow/AutoTranslate';
|
|
88
89
|
import { MessageTable } from './src/flow/MessageTable';
|
|
89
90
|
|
|
90
91
|
export function addCustomElement(name: string, comp: any) {
|
|
@@ -182,3 +183,4 @@ addCustomElement('temba-simulator', Simulator);
|
|
|
182
183
|
addCustomElement('temba-flow-search', FlowSearch);
|
|
183
184
|
addCustomElement('temba-issues-window', IssuesWindow);
|
|
184
185
|
addCustomElement('temba-revisions-window', RevisionsWindow);
|
|
186
|
+
addCustomElement('temba-auto-translate', AutoTranslate);
|