@olares/lares-mobile 0.15.5 → 0.15.6
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/README.md +6 -2
- package/package.json +2 -2
- package/src/App.vue +917 -178
- package/src/chat/ChatBar.vue +2 -2
- package/src/chat/Composer.vue +467 -16
- package/src/chat/Deliverables.vue +22 -10
- package/src/chat/HistoryPanel.vue +747 -55
- package/src/chat/MessageActions.vue +88 -34
- package/src/chat/QuestionCard.vue +5 -5
- package/src/chat/StageRow.vue +8 -5
- package/src/chat/Transcript.vue +20 -6
- package/src/chat/composer-tokens.js +32 -0
- package/src/chat/format.js +37 -0
- package/src/desktop/DesktopComposer.vue +871 -0
- package/src/desktop/DesktopContextMeter.vue +81 -0
- package/src/desktop/DesktopGoal.vue +88 -0
- package/src/desktop/DesktopPreview.vue +58 -0
- package/src/desktop/DesktopPreviewTabs.vue +82 -0
- package/src/desktop/DesktopQuestions.vue +118 -0
- package/src/desktop/DesktopQueue.vue +203 -0
- package/src/desktop/DesktopShell.vue +203 -0
- package/src/desktop/DesktopSidebar.vue +645 -0
- package/src/desktop/DesktopTodos.vue +52 -0
- package/src/desktop/DirectoryBrowser.vue +170 -0
- package/src/desktop/DirectoryNewFolder.vue +124 -0
- package/src/desktop/ui/PcDialog.vue +85 -0
- package/src/desktop/ui/PcPopover.vue +110 -0
- package/src/desktop/ui/PcTooltip.vue +95 -0
- package/src/desktop/ui/place.js +24 -0
- package/src/desktop/workspace-view.js +121 -0
- package/src/host.js +20 -2
- package/src/index.d.ts +2 -0
- package/src/mobile/MobileDirectoryNewFolder.vue +121 -0
- package/src/mobile/MobileDirectorySheet.vue +162 -0
- package/src/mobile/MobileNewSession.vue +217 -0
- package/src/mobile/MobileShell.vue +367 -0
- package/src/motion.css +166 -0
- package/src/preview/Preview.vue +91 -150
- package/src/preview/PreviewBody.vue +185 -0
- package/src/settings/AgentSettings.vue +126 -36
- package/src/settings/Select.vue +183 -0
- package/src/settings/SettingRow.vue +5 -4
- package/src/settings/Sheet.vue +4 -4
- package/src/styles.css +9 -0
|
@@ -1,56 +1,75 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="lares-actions" role="toolbar" :aria-label="t('action.bar')">
|
|
3
3
|
<div class="lares-actions__left">
|
|
4
|
-
<
|
|
5
|
-
<
|
|
6
|
-
<
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
</
|
|
37
|
-
</
|
|
4
|
+
<LaresPcTooltip :label="copied ? t('action.copied') : t('action.copy')">
|
|
5
|
+
<button type="button" :aria-label="copied ? t('action.copied') : t('action.copy')" @click="copy">
|
|
6
|
+
<svg viewBox="0 0 24 24" aria-hidden="true">
|
|
7
|
+
<rect x="9" y="9" width="11" height="13" rx="2" />
|
|
8
|
+
<path d="M6 16H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
|
|
9
|
+
</svg>
|
|
10
|
+
</button>
|
|
11
|
+
</LaresPcTooltip>
|
|
12
|
+
<LaresPcTooltip :label="t('action.like')">
|
|
13
|
+
<button
|
|
14
|
+
type="button"
|
|
15
|
+
:aria-label="t('action.like')"
|
|
16
|
+
:aria-pressed="reaction === 'like' ? 'true' : 'false'"
|
|
17
|
+
:data-on="reaction === 'like' ? 'true' : 'false'"
|
|
18
|
+
@click="$emit('react', reaction === 'like' ? '' : 'like')"
|
|
19
|
+
>
|
|
20
|
+
<svg viewBox="0 0 24 24" aria-hidden="true">
|
|
21
|
+
<path d="M7 11v10H4a1 1 0 0 1-1-1v-8a1 1 0 0 1 1-1h3Zm0 0 4.2-8.1A2.2 2.2 0 0 1 15.2 4v5H20a2 2 0 0 1 2 2.3l-1.2 8A2 2 0 0 1 18.8 21H7" />
|
|
22
|
+
</svg>
|
|
23
|
+
</button>
|
|
24
|
+
</LaresPcTooltip>
|
|
25
|
+
<LaresPcTooltip :label="t('action.dislike')">
|
|
26
|
+
<button
|
|
27
|
+
type="button"
|
|
28
|
+
:aria-label="t('action.dislike')"
|
|
29
|
+
:aria-pressed="reaction === 'dislike' ? 'true' : 'false'"
|
|
30
|
+
:data-on="reaction === 'dislike' ? 'true' : 'false'"
|
|
31
|
+
@click="$emit('react', reaction === 'dislike' ? '' : 'dislike')"
|
|
32
|
+
>
|
|
33
|
+
<svg viewBox="0 0 24 24" aria-hidden="true">
|
|
34
|
+
<path d="M17 13V3h3a1 1 0 0 1 1 1v8a1 1 0 0 1-1 1h-3Zm0 0-4.2 8.1A2.2 2.2 0 0 1 8.8 20v-5H4a2 2 0 0 1-2-2.3l1.2-8A2 2 0 0 1 5.2 3H17" />
|
|
35
|
+
</svg>
|
|
36
|
+
</button>
|
|
37
|
+
</LaresPcTooltip>
|
|
38
|
+
<LaresPcTooltip :label="t('action.share')">
|
|
39
|
+
<button type="button" :aria-label="t('action.share')" @click="share">
|
|
40
|
+
<svg viewBox="0 0 24 24" aria-hidden="true">
|
|
41
|
+
<path d="M4 20v-7a4 4 0 0 1 4-4h12" />
|
|
42
|
+
<path d="m16 5 4 4-4 4" />
|
|
43
|
+
</svg>
|
|
44
|
+
</button>
|
|
45
|
+
</LaresPcTooltip>
|
|
38
46
|
</div>
|
|
47
|
+
<span v-if="stats" class="lares-actions__stats">{{ stats }}</span>
|
|
39
48
|
</div>
|
|
40
49
|
</template>
|
|
41
50
|
|
|
42
51
|
<script>
|
|
52
|
+
import { metricsLine } from "./format.js";
|
|
53
|
+
import LaresPcTooltip from "../desktop/ui/PcTooltip.vue";
|
|
54
|
+
|
|
43
55
|
export default {
|
|
44
56
|
name: "LaresMessageActions",
|
|
57
|
+
components: { LaresPcTooltip },
|
|
45
58
|
props: {
|
|
46
59
|
text: { type: String, default: "" },
|
|
47
60
|
reaction: { type: String, default: "" },
|
|
61
|
+
metrics: { type: Object, default: null },
|
|
48
62
|
t: { type: Function, required: true },
|
|
49
63
|
},
|
|
50
64
|
emits: ["react"],
|
|
51
65
|
data() {
|
|
52
66
|
return { copied: false, copyTimer: 0 };
|
|
53
67
|
},
|
|
68
|
+
computed: {
|
|
69
|
+
stats() {
|
|
70
|
+
return metricsLine(this.metrics, this.t);
|
|
71
|
+
},
|
|
72
|
+
},
|
|
54
73
|
beforeUnmount() {
|
|
55
74
|
if (this.copyTimer) clearTimeout(this.copyTimer);
|
|
56
75
|
},
|
|
@@ -117,6 +136,41 @@ export default {
|
|
|
117
136
|
background: var(--q-btn-bg-pressed);
|
|
118
137
|
}
|
|
119
138
|
|
|
139
|
+
/* Pointer chrome only: on touch there is no hover to reveal the readings, and
|
|
140
|
+
the mobile reply keeps its bare row of icons. */
|
|
141
|
+
.lares-actions__stats {
|
|
142
|
+
display: none;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
@media (hover: hover) {
|
|
146
|
+
.lares-actions__stats {
|
|
147
|
+
display: inline;
|
|
148
|
+
opacity: 0;
|
|
149
|
+
transition: opacity 80ms ease;
|
|
150
|
+
color: var(--q-ink-3);
|
|
151
|
+
font-size: 13px;
|
|
152
|
+
white-space: nowrap;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.lares-actions button {
|
|
156
|
+
cursor: pointer;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.lares-actions button:hover {
|
|
160
|
+
background: var(--q-background-hover);
|
|
161
|
+
color: var(--q-ink-2);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.lares-actions button[data-on="true"]:hover {
|
|
165
|
+
color: var(--q-blue-default);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.lares-actions:hover .lares-actions__stats,
|
|
169
|
+
.lares-actions:focus-within .lares-actions__stats {
|
|
170
|
+
opacity: 1;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
120
174
|
.lares-actions svg {
|
|
121
175
|
width: 18px;
|
|
122
176
|
height: 18px;
|
|
@@ -74,13 +74,13 @@ export default {
|
|
|
74
74
|
.lares-question__heading {
|
|
75
75
|
margin: 0;
|
|
76
76
|
color: var(--q-ink-2);
|
|
77
|
-
font-size:
|
|
77
|
+
font-size: 14px;
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
.lares-question__prompt {
|
|
81
81
|
margin: 0;
|
|
82
82
|
color: var(--q-ink-1);
|
|
83
|
-
font-size:
|
|
83
|
+
font-size: 17px;
|
|
84
84
|
font-weight: 500;
|
|
85
85
|
}
|
|
86
86
|
|
|
@@ -88,7 +88,7 @@ export default {
|
|
|
88
88
|
.lares-question__wait {
|
|
89
89
|
margin: 0;
|
|
90
90
|
color: var(--q-ink-3);
|
|
91
|
-
font-size:
|
|
91
|
+
font-size: 15px;
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
.lares-question__options {
|
|
@@ -115,12 +115,12 @@ export default {
|
|
|
115
115
|
}
|
|
116
116
|
|
|
117
117
|
.lares-question__label {
|
|
118
|
-
font-size:
|
|
118
|
+
font-size: 16px;
|
|
119
119
|
color: var(--q-ink-1);
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
.lares-question__hint {
|
|
123
|
-
font-size:
|
|
123
|
+
font-size: 14px;
|
|
124
124
|
color: var(--q-ink-3);
|
|
125
125
|
}
|
|
126
126
|
</style>
|
package/src/chat/StageRow.vue
CHANGED
|
@@ -78,8 +78,8 @@ export default {
|
|
|
78
78
|
.lares-stage__summary {
|
|
79
79
|
min-width: 0;
|
|
80
80
|
overflow: hidden;
|
|
81
|
-
font-size:
|
|
82
|
-
line-height:
|
|
81
|
+
font-size: 16px;
|
|
82
|
+
line-height: 26px;
|
|
83
83
|
text-overflow: ellipsis;
|
|
84
84
|
white-space: nowrap;
|
|
85
85
|
}
|
|
@@ -106,14 +106,14 @@ export default {
|
|
|
106
106
|
box-sizing: border-box;
|
|
107
107
|
min-width: 0;
|
|
108
108
|
max-width: 100%;
|
|
109
|
-
max-height:
|
|
109
|
+
max-height: 156px;
|
|
110
110
|
margin: 4px 0 0 22px;
|
|
111
111
|
border-radius: 8px;
|
|
112
112
|
padding: 10px 12px;
|
|
113
113
|
overflow: auto;
|
|
114
114
|
background: var(--q-background-3);
|
|
115
115
|
color: var(--q-ink-2);
|
|
116
|
-
font: 400
|
|
116
|
+
font: 400 13px/18px ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
|
117
117
|
white-space: pre-wrap;
|
|
118
118
|
overflow-wrap: anywhere;
|
|
119
119
|
word-break: break-word;
|
|
@@ -126,6 +126,9 @@ export default {
|
|
|
126
126
|
border-left: 2px solid var(--q-input-stroke);
|
|
127
127
|
padding: 0 0 0 12px;
|
|
128
128
|
background: transparent;
|
|
129
|
-
font:
|
|
129
|
+
font-family: inherit;
|
|
130
|
+
font-size: 15px;
|
|
131
|
+
font-weight: 400;
|
|
132
|
+
line-height: 1.6;
|
|
130
133
|
}
|
|
131
134
|
</style>
|
package/src/chat/Transcript.vue
CHANGED
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
:body="row.text"
|
|
34
34
|
/>
|
|
35
35
|
<LaresQuestionCard
|
|
36
|
-
v-else-if="row.type === 'tool' && row.name === 'ask_user_question' && row.status === 'running'"
|
|
36
|
+
v-else-if="!questionTakeover && row.type === 'tool' && row.name === 'ask_user_question' && row.status === 'running'"
|
|
37
37
|
:questions="questionItems(row)"
|
|
38
38
|
:rpc-id="question?.rpcId || ''"
|
|
39
39
|
:busy="questionBusy"
|
|
@@ -64,6 +64,7 @@
|
|
|
64
64
|
<LaresMessageActions
|
|
65
65
|
:text="row.text"
|
|
66
66
|
:reaction="reactions[actionKey(index)] || ''"
|
|
67
|
+
:metrics="row.metrics || null"
|
|
67
68
|
:t="t"
|
|
68
69
|
@react="setReaction(index, $event)"
|
|
69
70
|
/>
|
|
@@ -106,6 +107,7 @@ export default {
|
|
|
106
107
|
sessionId: { type: String, default: "" },
|
|
107
108
|
question: { type: Object, default: null },
|
|
108
109
|
questionBusy: { type: Boolean, default: false },
|
|
110
|
+
questionTakeover: { type: Boolean, default: false },
|
|
109
111
|
t: { type: Function, required: true },
|
|
110
112
|
},
|
|
111
113
|
emits: ["open", "media", "answer"],
|
|
@@ -225,7 +227,7 @@ export default {
|
|
|
225
227
|
|
|
226
228
|
.lares-log__empty {
|
|
227
229
|
margin: 24px 0 0;
|
|
228
|
-
font-size:
|
|
230
|
+
font-size: 15px;
|
|
229
231
|
color: var(--q-ink-3);
|
|
230
232
|
}
|
|
231
233
|
|
|
@@ -267,7 +269,8 @@ export default {
|
|
|
267
269
|
}
|
|
268
270
|
|
|
269
271
|
@media (prefers-reduced-motion: reduce) {
|
|
270
|
-
.lares-sk
|
|
272
|
+
.lares-sk,
|
|
273
|
+
.lares-log__item[data-pending="true"] {
|
|
271
274
|
animation: none;
|
|
272
275
|
}
|
|
273
276
|
}
|
|
@@ -280,6 +283,17 @@ export default {
|
|
|
280
283
|
max-width: 100%;
|
|
281
284
|
}
|
|
282
285
|
|
|
286
|
+
.lares-log__item[data-pending="true"] {
|
|
287
|
+
animation: lares-msg-in var(--lares-duration-normal, 220ms) var(--lares-ease-out, cubic-bezier(0.32, 0.72, 0, 1));
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
@keyframes lares-msg-in {
|
|
291
|
+
from {
|
|
292
|
+
opacity: 0;
|
|
293
|
+
transform: translateY(8px);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
283
297
|
.lares-log__item[data-type="user"],
|
|
284
298
|
.lares-log__item[data-type="assistant"],
|
|
285
299
|
.lares-log__item[data-type="files"] {
|
|
@@ -305,7 +319,7 @@ export default {
|
|
|
305
319
|
min-width: 0;
|
|
306
320
|
max-width: 100%;
|
|
307
321
|
overflow-x: hidden;
|
|
308
|
-
font-size:
|
|
322
|
+
font-size: 17px;
|
|
309
323
|
line-height: 1.55;
|
|
310
324
|
word-break: break-word;
|
|
311
325
|
overflow-wrap: anywhere;
|
|
@@ -350,7 +364,7 @@ export default {
|
|
|
350
364
|
.lares-md :deep(h2),
|
|
351
365
|
.lares-md :deep(h3) {
|
|
352
366
|
margin: 0 0 8px;
|
|
353
|
-
font-size:
|
|
367
|
+
font-size: 18px;
|
|
354
368
|
font-weight: 600;
|
|
355
369
|
}
|
|
356
370
|
|
|
@@ -358,7 +372,7 @@ export default {
|
|
|
358
372
|
border-radius: 4px;
|
|
359
373
|
padding: 1px 4px;
|
|
360
374
|
background: var(--q-background-3);
|
|
361
|
-
font-size:
|
|
375
|
+
font-size: 15px;
|
|
362
376
|
}
|
|
363
377
|
|
|
364
378
|
.lares-md :deep(pre) {
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { activeReferenceToken, insertReference } from "@olares/lares-core/larepass/references";
|
|
2
|
+
|
|
3
|
+
export const REFERENCE_DEBOUNCE_MS = 100;
|
|
4
|
+
|
|
5
|
+
export function referenceRows(references, t) {
|
|
6
|
+
let previous = "";
|
|
7
|
+
return (references ?? []).map((row) => {
|
|
8
|
+
const group = row.kind === "session" ? "sessions" : "files";
|
|
9
|
+
const section = group === previous ? "" : t(`references.section.${group}`);
|
|
10
|
+
previous = group;
|
|
11
|
+
return { ...row, section, name: `${t(`references.${row.kind}`)} · ${row.label}` };
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function visibleCommands(commands, draft, commandTyped) {
|
|
16
|
+
const rows = commands ?? [];
|
|
17
|
+
if (!commandTyped) return rows;
|
|
18
|
+
const needle = String(draft).match(/^\/([^\s]*)$/)?.[1]?.toLowerCase() ?? "";
|
|
19
|
+
return rows.filter((row) => row.name.toLowerCase().includes(needle));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function isCommandLine(draft) {
|
|
23
|
+
return /^\/[^\s]*$/.test(String(draft ?? ""));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function activeReference(draft, caret) {
|
|
27
|
+
return activeReferenceToken(draft, caret);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function applyReference(draft, token, row) {
|
|
31
|
+
return insertReference(draft, token, row);
|
|
32
|
+
}
|
package/src/chat/format.js
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { interpolate } from "@olares/lares-core/i18n/t";
|
|
2
2
|
import { STAGE_COPY } from "@olares/lares-core/larepass/stage-copy";
|
|
3
|
+
import {
|
|
4
|
+
clockParts,
|
|
5
|
+
durationParts,
|
|
6
|
+
latencySeconds,
|
|
7
|
+
throughputTokens,
|
|
8
|
+
} from "@olares/lares-core/larepass/turn-metrics";
|
|
3
9
|
|
|
4
10
|
export function withPendingUser(items, pendingUser) {
|
|
5
11
|
if (!pendingUser) return items;
|
|
@@ -50,6 +56,37 @@ export function failText(t, failed, error) {
|
|
|
50
56
|
return t("chat.failed", { error: failed || error });
|
|
51
57
|
}
|
|
52
58
|
|
|
59
|
+
function clockText(time, t, now) {
|
|
60
|
+
const { clock, scope, y, m, d } = clockParts(time, now);
|
|
61
|
+
if (scope === "day") return clock;
|
|
62
|
+
return `${t(scope === "md" ? "clock.md" : "clock.ymd", { y, m, d })} ${clock}`;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* The reply's own footer: when it landed, and whatever the log recorded about
|
|
67
|
+
* how it got there. Each reading is dropped rather than faked when the events
|
|
68
|
+
* never carried it.
|
|
69
|
+
*/
|
|
70
|
+
export function metricsLine(metrics, t, now = Date.now()) {
|
|
71
|
+
if (!metrics?.time) return "";
|
|
72
|
+
const parts = [clockText(metrics.time, t, now)];
|
|
73
|
+
if (metrics.runMs !== undefined) {
|
|
74
|
+
const { minutes, seconds } = durationParts(metrics.runMs);
|
|
75
|
+
parts.push(t("message.ranFor", {
|
|
76
|
+
duration: minutes > 0
|
|
77
|
+
? t("duration.minutes", { minutes, seconds })
|
|
78
|
+
: t("duration.seconds", { seconds }),
|
|
79
|
+
}));
|
|
80
|
+
}
|
|
81
|
+
if (metrics.ttftMs !== undefined) {
|
|
82
|
+
parts.push(t("message.ttft", { seconds: latencySeconds(metrics.ttftMs) }));
|
|
83
|
+
}
|
|
84
|
+
if (metrics.tokensPerSecond !== undefined) {
|
|
85
|
+
parts.push(t("message.tokensPerSecond", { tps: throughputTokens(metrics.tokensPerSecond) }));
|
|
86
|
+
}
|
|
87
|
+
return parts.join(" · ");
|
|
88
|
+
}
|
|
89
|
+
|
|
53
90
|
export function retryStatus(row) {
|
|
54
91
|
const copy = STAGE_COPY.retry;
|
|
55
92
|
const label = row.retryState === "started"
|