@olares/lares-mobile 0.15.6 → 0.15.7
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@olares/lares-mobile",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.7",
|
|
4
4
|
"description": "Lares chat component for LarePass",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"vue": "^3.2.0"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@olares/lares-core": "0.15.
|
|
31
|
+
"@olares/lares-core": "0.15.7",
|
|
32
32
|
"markdown-it": "^14.3.0"
|
|
33
33
|
}
|
|
34
34
|
}
|
package/src/App.vue
CHANGED
|
@@ -1,7 +1,50 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<form class="desktop-composer" @submit.prevent>
|
|
2
|
+
<form class="desktop-composer" :data-hero="hero ? 'true' : undefined" @submit.prevent>
|
|
3
3
|
<div class="desktop-composer__model-row">
|
|
4
|
-
<LaresPcPopover
|
|
4
|
+
<LaresPcPopover
|
|
5
|
+
v-if="hero"
|
|
6
|
+
v-model="workspaceOpen"
|
|
7
|
+
class="desktop-composer__workspace-slot"
|
|
8
|
+
:width="240"
|
|
9
|
+
placement="top-start"
|
|
10
|
+
>
|
|
11
|
+
<template #trigger="{ toggle }">
|
|
12
|
+
<button
|
|
13
|
+
type="button"
|
|
14
|
+
class="desktop-composer__model desktop-composer__workspace"
|
|
15
|
+
:disabled="!state.workspacesReady"
|
|
16
|
+
aria-haspopup="menu"
|
|
17
|
+
:aria-expanded="workspaceOpen"
|
|
18
|
+
@mousedown.prevent
|
|
19
|
+
@click="toggle"
|
|
20
|
+
>
|
|
21
|
+
<svg viewBox="0 0 24 24" aria-hidden="true">
|
|
22
|
+
<path d="M3.5 7.5h6l1.6 2H20.5v9H3.5z" />
|
|
23
|
+
</svg>
|
|
24
|
+
<span>{{ workspaceLabel }}</span>
|
|
25
|
+
<ChevronIcon />
|
|
26
|
+
</button>
|
|
27
|
+
</template>
|
|
28
|
+
<div class="desktop-composer__menu">
|
|
29
|
+
<button
|
|
30
|
+
v-for="workspace in state.workspaces"
|
|
31
|
+
:key="workspace.workspaceId"
|
|
32
|
+
type="button"
|
|
33
|
+
class="desktop-composer__option"
|
|
34
|
+
:data-selected="workspace.workspaceId === state.currentWorkspaceId"
|
|
35
|
+
@click="pickWorkspace(workspace.workspaceId)"
|
|
36
|
+
>
|
|
37
|
+
<span>{{ workspace.title || defaultWorkspaceTitle }}</span>
|
|
38
|
+
<CheckIcon v-if="workspace.workspaceId === state.currentWorkspaceId" />
|
|
39
|
+
</button>
|
|
40
|
+
</div>
|
|
41
|
+
</LaresPcPopover>
|
|
42
|
+
<LaresPcPopover
|
|
43
|
+
v-model="modelOpen"
|
|
44
|
+
class="desktop-composer__model-slot"
|
|
45
|
+
:width="260"
|
|
46
|
+
placement="top-end"
|
|
47
|
+
>
|
|
5
48
|
<template #trigger="{ toggle }">
|
|
6
49
|
<button
|
|
7
50
|
type="button"
|
|
@@ -397,6 +440,7 @@
|
|
|
397
440
|
<script>
|
|
398
441
|
import { h } from "vue";
|
|
399
442
|
import { resolveSubmitMode } from "@olares/lares-core/larepass/submission-settings";
|
|
443
|
+
import { DEFAULT_WORKSPACE_TITLE } from "@olares/lares-core/workspace/constants";
|
|
400
444
|
import { activeReferenceToken, insertReference } from "@olares/lares-core/larepass/references";
|
|
401
445
|
import { draftImageStatus } from "@olares/lares-core/files/draft-images";
|
|
402
446
|
import LaresDesktopQueue from "./DesktopQueue.vue";
|
|
@@ -456,16 +500,18 @@ export default {
|
|
|
456
500
|
props: {
|
|
457
501
|
state: { type: Object, required: true },
|
|
458
502
|
t: { type: Function, required: true },
|
|
503
|
+
hero: { type: Boolean, default: false },
|
|
459
504
|
modelKey: { type: Function, required: true },
|
|
460
505
|
effortName: { type: Function, required: true },
|
|
461
506
|
},
|
|
462
507
|
emits: [
|
|
463
508
|
"update-draft", "send", "stop", "steer-queue", "files", "choose-model", "choose-effort", "answer-approval", "answer-questions",
|
|
464
|
-
"choose-permission", "disable-plan", "goal-action", "open-commands", "pick-command", "queue-action", "remove-image",
|
|
509
|
+
"choose-permission", "disable-plan", "goal-action", "open-commands", "pick-command", "queue-action", "remove-image", "pick-workspace",
|
|
465
510
|
],
|
|
466
511
|
data() {
|
|
467
512
|
return {
|
|
468
513
|
modelOpen: false,
|
|
514
|
+
workspaceOpen: false,
|
|
469
515
|
effortOpen: false,
|
|
470
516
|
permissionOpen: false,
|
|
471
517
|
permissionConfirm: false,
|
|
@@ -484,6 +530,16 @@ export default {
|
|
|
484
530
|
};
|
|
485
531
|
},
|
|
486
532
|
computed: {
|
|
533
|
+
defaultWorkspaceTitle() {
|
|
534
|
+
return DEFAULT_WORKSPACE_TITLE;
|
|
535
|
+
},
|
|
536
|
+
workspaceLabel() {
|
|
537
|
+
if (!this.state.workspacesReady) return this.t("agent.loading");
|
|
538
|
+
const current = this.state.workspaces.find(
|
|
539
|
+
(row) => row.workspaceId === this.state.currentWorkspaceId,
|
|
540
|
+
);
|
|
541
|
+
return current?.title || this.state.workspaces[0]?.title || DEFAULT_WORKSPACE_TITLE;
|
|
542
|
+
},
|
|
487
543
|
/** Held by id, so removing or sending the draft closes the dialog. */
|
|
488
544
|
previewImage() {
|
|
489
545
|
return this.state.draftImages.find((row) => row.id === this.previewImageId) ?? null;
|
|
@@ -524,6 +580,7 @@ export default {
|
|
|
524
580
|
return this.t("chat.placeholderSteerQueue", { shortcut });
|
|
525
581
|
}
|
|
526
582
|
if (this.state.planActive) return this.t("chat.placeholderPlan");
|
|
583
|
+
if (this.hero) return this.t("chat.placeholderNewSession");
|
|
527
584
|
return this.t("chat.placeholderDesktop");
|
|
528
585
|
},
|
|
529
586
|
},
|
|
@@ -544,6 +601,11 @@ export default {
|
|
|
544
601
|
window.removeEventListener("mousedown", this.onComposerOverlayPointer, true);
|
|
545
602
|
},
|
|
546
603
|
methods: {
|
|
604
|
+
pickWorkspace(workspaceId) {
|
|
605
|
+
this.workspaceOpen = false;
|
|
606
|
+
if (!workspaceId || workspaceId === this.state.currentWorkspaceId) return;
|
|
607
|
+
this.$emit("pick-workspace", workspaceId);
|
|
608
|
+
},
|
|
547
609
|
chooseModel(model) {
|
|
548
610
|
this.modelOpen = false;
|
|
549
611
|
this.$emit("choose-model", model);
|
|
@@ -768,12 +830,21 @@ export default {
|
|
|
768
830
|
padding: 8px 0 20px;
|
|
769
831
|
}
|
|
770
832
|
.desktop-composer__model-row { display:flex; justify-content:flex-end; gap:2px; min-height:28px; margin-bottom:6px; }
|
|
833
|
+
.desktop-composer[data-hero="true"] .desktop-composer__model-row { justify-content:space-between; }
|
|
771
834
|
.desktop-composer__model,
|
|
772
835
|
.desktop-composer__circle,
|
|
773
836
|
.desktop-composer__send {
|
|
774
837
|
border:0; color:var(--q-ink-2); cursor:pointer;
|
|
775
838
|
}
|
|
776
839
|
.desktop-composer__model { display:flex; align-items:center; gap:4px; border-radius:999px; padding:4px 10px; background:transparent; font-size:14px; }
|
|
840
|
+
/* The model name is the long label of the pair, so it is the one that gives way;
|
|
841
|
+
without a floor of its own the workspace chip absorbs every pixel of shrink. */
|
|
842
|
+
.desktop-composer__workspace-slot { flex:0 0 auto; max-width:45%; }
|
|
843
|
+
.desktop-composer__model-slot { min-width:0; }
|
|
844
|
+
.desktop-composer__workspace,
|
|
845
|
+
.desktop-composer__model-slot .desktop-composer__model { min-width:0; max-width:100%; }
|
|
846
|
+
.desktop-composer__workspace span,
|
|
847
|
+
.desktop-composer__model-slot .desktop-composer__model span { min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
|
777
848
|
.desktop-composer__model svg,
|
|
778
849
|
.desktop-composer__permission svg,
|
|
779
850
|
.desktop-composer__circle svg,
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
@archive-session="$emit('archive-session', $event)"
|
|
17
17
|
/>
|
|
18
18
|
<main class="desktop-shell__main">
|
|
19
|
-
<header class="desktop-shell__header">
|
|
19
|
+
<header v-if="!newSessionHero" class="desktop-shell__header">
|
|
20
20
|
<h1>{{ state.sessionTitle }}</h1>
|
|
21
21
|
<Transition name="lares-tabs">
|
|
22
22
|
<LaresDesktopPreviewTabs
|
|
@@ -30,12 +30,17 @@
|
|
|
30
30
|
/>
|
|
31
31
|
</Transition>
|
|
32
32
|
</header>
|
|
33
|
-
<section class="desktop-shell__conversation">
|
|
33
|
+
<section class="desktop-shell__conversation" :data-hero="newSessionHero ? 'true' : 'false'">
|
|
34
34
|
<div v-if="state.failed" class="desktop-shell__failure">
|
|
35
35
|
<span>{{ state.failText }}</span>
|
|
36
36
|
<button type="button" :disabled="state.starting" @click="$emit('retry')">{{ t("probe.retry") }}</button>
|
|
37
37
|
</div>
|
|
38
|
+
<div v-if="newSessionHero" class="desktop-shell__brand">
|
|
39
|
+
<div class="desktop-shell__logo" :style="logoStyle" aria-hidden="true" />
|
|
40
|
+
<h1>{{ t("shell.title") }}</h1>
|
|
41
|
+
</div>
|
|
38
42
|
<LaresTranscript
|
|
43
|
+
v-if="!newSessionHero"
|
|
39
44
|
ref="transcript"
|
|
40
45
|
:items="state.viewItems"
|
|
41
46
|
:running="state.running"
|
|
@@ -58,6 +63,7 @@
|
|
|
58
63
|
ref="composer"
|
|
59
64
|
:state="state"
|
|
60
65
|
:t="t"
|
|
66
|
+
:hero="newSessionHero"
|
|
61
67
|
:model-key="modelKey"
|
|
62
68
|
:effort-name="effortName"
|
|
63
69
|
@update-draft="$emit('update-draft', $event)"
|
|
@@ -67,6 +73,7 @@
|
|
|
67
73
|
@files="$emit('files', $event)"
|
|
68
74
|
@remove-image="$emit('remove-image', $event)"
|
|
69
75
|
@choose-model="$emit('choose-model', $event)"
|
|
76
|
+
@pick-workspace="$emit('pick-workspace', $event)"
|
|
70
77
|
@choose-effort="$emit('choose-effort', $event)"
|
|
71
78
|
@choose-permission="$emit('choose-permission', $event)"
|
|
72
79
|
@disable-plan="$emit('disable-plan')"
|
|
@@ -115,6 +122,7 @@ import LaresDesktopPreviewTabs from "./DesktopPreviewTabs.vue";
|
|
|
115
122
|
import LaresDesktopSidebar from "./DesktopSidebar.vue";
|
|
116
123
|
import LaresDesktopComposer from "./DesktopComposer.vue";
|
|
117
124
|
import LaresDirectoryBrowser from "./DirectoryBrowser.vue";
|
|
125
|
+
import { MARK_DATA_URI } from "@olares/lares-core/icons/mark";
|
|
118
126
|
|
|
119
127
|
export default {
|
|
120
128
|
name: "LaresDesktopShell",
|
|
@@ -138,7 +146,7 @@ export default {
|
|
|
138
146
|
effortName: { type: Function, required: true },
|
|
139
147
|
},
|
|
140
148
|
emits: [
|
|
141
|
-
"create", "pick-session", "retry", "open-file", "answer", "update-draft",
|
|
149
|
+
"create", "pick-session", "pick-workspace", "retry", "open-file", "answer", "update-draft",
|
|
142
150
|
"send", "stop", "steer-queue", "files", "remove-image", "choose-model", "choose-effort", "choose-permission", "disable-plan", "goal-action", "answer-approval", "answer-questions",
|
|
143
151
|
"open-commands", "open-references", "pick-command", "queue-action", "show-chat", "close-preview-tab",
|
|
144
152
|
"refresh-workspaces", "browse-workspaces", "browse-directory",
|
|
@@ -146,6 +154,20 @@ export default {
|
|
|
146
154
|
"delete-workspace", "move-workspace", "move-session", "rename-session",
|
|
147
155
|
"fork-session", "archive-session",
|
|
148
156
|
],
|
|
157
|
+
computed: {
|
|
158
|
+
newSessionHero() {
|
|
159
|
+
if (this.state.previewMode === "preview" && this.state.preview.path) return false;
|
|
160
|
+
return (
|
|
161
|
+
this.state.viewItems.length === 0
|
|
162
|
+
&& !this.state.running
|
|
163
|
+
&& !this.state.historyLoading
|
|
164
|
+
&& !this.state.starting
|
|
165
|
+
);
|
|
166
|
+
},
|
|
167
|
+
logoStyle() {
|
|
168
|
+
return { backgroundImage: MARK_DATA_URI };
|
|
169
|
+
},
|
|
170
|
+
},
|
|
149
171
|
methods: {
|
|
150
172
|
focusComposer() {
|
|
151
173
|
this.$refs.composer?.focus?.();
|
|
@@ -188,6 +210,10 @@ export default {
|
|
|
188
210
|
.desktop-shell__header { display:flex; height:44px; flex-shrink:0; align-items:center; gap:16px; padding:0 18px; border-bottom:1px solid var(--q-separator); }
|
|
189
211
|
.desktop-shell__header h1 { max-width:min(280px, 40%); overflow:hidden; margin:0; font-size:15px; font-weight:500; line-height:22px; text-overflow:ellipsis; white-space:nowrap; }
|
|
190
212
|
.desktop-shell__conversation { position:relative; display:flex; min-width:0; min-height:0; flex:1; flex-direction:column; }
|
|
213
|
+
.desktop-shell__conversation[data-hero="true"] { justify-content:center; }
|
|
214
|
+
.desktop-shell__brand { display:flex; align-items:center; justify-content:center; gap:12px; margin-bottom:36px; }
|
|
215
|
+
.desktop-shell__brand h1 { margin:0; color:var(--q-ink-1); font-size:26px; font-weight:600; line-height:34px; letter-spacing:-.01em; }
|
|
216
|
+
.desktop-shell__logo { width:44px; height:44px; background-position:center; background-repeat:no-repeat; background-size:cover; box-shadow:inset 0 1.5px 0 rgb(255 255 255 / 50%); }
|
|
191
217
|
.desktop-shell__failure { display:flex; width:calc(var(--lares-column) - 2 * var(--lares-gutter)); align-items:center; gap:10px; margin:12px auto 0; color:var(--q-orange-default); font-size:14px; }
|
|
192
218
|
.desktop-shell__failure button { border:0; border-radius:6px; padding:5px 8px; background:var(--q-blue-alpha); color:var(--q-blue-default); }
|
|
193
219
|
/* The scroller stays full width so the wheel works over the gutters; the rows carry the reading column. */
|
|
@@ -1,18 +1,50 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<LaresPcDialog
|
|
3
3
|
:open="state.open"
|
|
4
|
-
:title="t('desktop.
|
|
4
|
+
:title="t('desktop.selectWorkspaceDirectory')"
|
|
5
5
|
:close-label="t('desktop.close')"
|
|
6
6
|
:busy="state.creating || state.creatingFolder"
|
|
7
7
|
:width="620"
|
|
8
|
+
variant="directory"
|
|
8
9
|
@close="$emit('close')"
|
|
9
10
|
>
|
|
11
|
+
<template #header="{ titleId }">
|
|
12
|
+
<div class="directory-browser__header">
|
|
13
|
+
<h2 :id="titleId">{{ t("desktop.selectWorkspaceDirectory") }}</h2>
|
|
14
|
+
<div class="directory-browser__location">
|
|
15
|
+
<input
|
|
16
|
+
v-if="editingPath"
|
|
17
|
+
ref="pathInput"
|
|
18
|
+
v-model="pathDraft"
|
|
19
|
+
:aria-label="t('desktop.folderPath')"
|
|
20
|
+
@keydown.enter.prevent="submitPath"
|
|
21
|
+
@keydown.esc.prevent="editingPath = false"
|
|
22
|
+
@blur="editingPath = false"
|
|
23
|
+
/>
|
|
24
|
+
<nav v-else-if="state.listing" :aria-label="t('desktop.folderPath')">
|
|
25
|
+
<template v-for="(crumb, index) in homeCrumbs" :key="crumb.path">
|
|
26
|
+
<span v-if="index" aria-hidden="true">/</span>
|
|
27
|
+
<button type="button" @click="$emit('browse', crumb.path)">
|
|
28
|
+
{{ crumb.path === state.listing.home ? t("desktop.home") : crumb.name }}
|
|
29
|
+
</button>
|
|
30
|
+
</template>
|
|
31
|
+
</nav>
|
|
32
|
+
<button
|
|
33
|
+
v-if="!editingPath"
|
|
34
|
+
type="button"
|
|
35
|
+
class="directory-browser__edit"
|
|
36
|
+
:aria-label="t('desktop.folderPath')"
|
|
37
|
+
@click="beginPathEdit"
|
|
38
|
+
>
|
|
39
|
+
<svg viewBox="0 0 24 24" aria-hidden="true">
|
|
40
|
+
<path d="m4 16-.75 4.25L7.5 19.5 18.4 8.6a2.1 2.1 0 0 0-3-3Z" />
|
|
41
|
+
<path d="m13.8 7.2 3 3M3 21h18" />
|
|
42
|
+
</svg>
|
|
43
|
+
</button>
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
</template>
|
|
10
47
|
<div class="directory-browser">
|
|
11
|
-
<nav v-if="state.listing" class="directory-browser__crumbs" :aria-label="t('desktop.folderPath')">
|
|
12
|
-
<button v-for="crumb in state.listing.crumbs" :key="crumb.path" type="button" @click="$emit('browse', crumb.path)">
|
|
13
|
-
{{ crumb.path === state.listing.home ? t("desktop.home") : crumb.name }}
|
|
14
|
-
</button>
|
|
15
|
-
</nav>
|
|
16
48
|
<div class="directory-browser__body">
|
|
17
49
|
<p v-if="state.loading" class="directory-browser__status">{{ t("desktop.loadingFolders") }}</p>
|
|
18
50
|
<p v-else-if="state.error" class="directory-browser__error">{{ state.error }}</p>
|
|
@@ -40,9 +72,7 @@
|
|
|
40
72
|
</div>
|
|
41
73
|
<template #footer>
|
|
42
74
|
<div class="directory-browser__footer">
|
|
43
|
-
<
|
|
44
|
-
<div class="directory-browser__footer-row">
|
|
45
|
-
<div class="directory-browser__footer-left">
|
|
75
|
+
<div class="directory-browser__footer-left">
|
|
46
76
|
<button
|
|
47
77
|
type="button"
|
|
48
78
|
class="directory-browser__new-folder"
|
|
@@ -60,8 +90,8 @@
|
|
|
60
90
|
/>
|
|
61
91
|
<span>{{ t("desktop.showHiddenFiles") }}</span>
|
|
62
92
|
</label>
|
|
63
|
-
|
|
64
|
-
|
|
93
|
+
</div>
|
|
94
|
+
<div class="directory-browser__footer-right">
|
|
65
95
|
<button type="button" class="directory-browser__cancel" :disabled="state.creating" @click="$emit('close')">
|
|
66
96
|
{{ t("desktop.cancel") }}
|
|
67
97
|
</button>
|
|
@@ -71,9 +101,8 @@
|
|
|
71
101
|
:disabled="state.loading || state.creating || !(selected || state.listing?.path)"
|
|
72
102
|
@click="$emit('choose', selected || state.listing.path)"
|
|
73
103
|
>
|
|
74
|
-
{{ state.creating ? t("desktop.addingWorkspace") : t("desktop.
|
|
104
|
+
{{ state.creating ? t("desktop.addingWorkspace") : t("desktop.open") }}
|
|
75
105
|
</button>
|
|
76
|
-
</div>
|
|
77
106
|
</div>
|
|
78
107
|
</div>
|
|
79
108
|
</template>
|
|
@@ -100,17 +129,25 @@ export default {
|
|
|
100
129
|
},
|
|
101
130
|
emits: ["close", "browse", "choose", "create-folder", "toggle-hidden"],
|
|
102
131
|
data() {
|
|
103
|
-
return { selected: "", newFolderOpen: false };
|
|
132
|
+
return { selected: "", newFolderOpen: false, editingPath: false, pathDraft: "" };
|
|
104
133
|
},
|
|
105
134
|
computed: {
|
|
106
135
|
visibleEntries() {
|
|
107
136
|
const entries = this.state.listing?.entries ?? [];
|
|
108
137
|
return this.state.showHidden ? entries : entries.filter((entry) => !entry.hidden);
|
|
109
138
|
},
|
|
139
|
+
homeCrumbs() {
|
|
140
|
+
const crumbs = this.state.listing?.crumbs ?? [];
|
|
141
|
+
const home = this.state.listing?.home;
|
|
142
|
+
const homeIndex = crumbs.findIndex((crumb) => crumb.path === home);
|
|
143
|
+
return homeIndex >= 0 ? crumbs.slice(homeIndex) : crumbs;
|
|
144
|
+
},
|
|
110
145
|
},
|
|
111
146
|
watch: {
|
|
112
147
|
"state.listing.path"() {
|
|
113
148
|
this.selected = this.state.selectPath || "";
|
|
149
|
+
this.pathDraft = this.state.listing?.path || "";
|
|
150
|
+
this.editingPath = false;
|
|
114
151
|
},
|
|
115
152
|
"state.selectPath"(path) {
|
|
116
153
|
if (path) this.selected = path;
|
|
@@ -129,38 +166,62 @@ export default {
|
|
|
129
166
|
onCreateFolder(name) {
|
|
130
167
|
this.$emit("create-folder", name);
|
|
131
168
|
},
|
|
169
|
+
beginPathEdit() {
|
|
170
|
+
this.pathDraft = this.state.listing?.path || "";
|
|
171
|
+
this.editingPath = true;
|
|
172
|
+
this.$nextTick(() => {
|
|
173
|
+
this.$refs.pathInput?.focus();
|
|
174
|
+
this.$refs.pathInput?.select();
|
|
175
|
+
});
|
|
176
|
+
},
|
|
177
|
+
submitPath() {
|
|
178
|
+
const path = this.pathDraft.trim();
|
|
179
|
+
if (!path) return;
|
|
180
|
+
this.editingPath = false;
|
|
181
|
+
this.$emit("browse", path);
|
|
182
|
+
},
|
|
132
183
|
},
|
|
133
184
|
};
|
|
134
185
|
</script>
|
|
135
186
|
|
|
136
187
|
<style scoped>
|
|
137
|
-
.directory-
|
|
188
|
+
.directory-browser__header { height:76px; }
|
|
189
|
+
.directory-browser__header h2 { height:42px; margin:0; padding:19px 22px 0; font-size:16px; font-weight:500; line-height:22px; }
|
|
190
|
+
.directory-browser__location { display:flex; height:34px; align-items:center; gap:8px; padding:0 20px 7px 22px; }
|
|
191
|
+
.directory-browser__location nav { display:flex; min-width:0; flex:1; align-items:center; gap:7px; overflow:hidden; color:var(--q-ink-2); }
|
|
192
|
+
.directory-browser__location nav button { min-width:0; overflow:hidden; border:0; padding:0; background:transparent; color:inherit; font:inherit; font-size:14px; text-overflow:ellipsis; white-space:nowrap; cursor:pointer; }
|
|
193
|
+
.directory-browser__location nav button:hover { color:var(--q-ink-1); }
|
|
194
|
+
.directory-browser__location input { box-sizing:border-box; min-width:0; height:27px; flex:1; border:1px solid var(--q-input-stroke); border-radius:5px; padding:0 8px; outline:none; background:var(--q-background-2); color:var(--q-ink-1); font:inherit; font-size:13px; }
|
|
195
|
+
.directory-browser__edit { display:grid; width:30px; height:30px; flex-shrink:0; place-items:center; border:0; border-radius:6px; background:transparent; color:var(--q-ink-2); cursor:pointer; }
|
|
196
|
+
.directory-browser__edit:hover { background:var(--q-background-hover); color:var(--q-ink-1); }
|
|
197
|
+
.directory-browser__edit svg { width:17px; height:17px; fill:none; stroke:currentColor; stroke-width:1.6; stroke-linecap:round; stroke-linejoin:round; }
|
|
198
|
+
.directory-browser { display:flex; height:min(316px, calc(100vh - 186px)); flex-direction:column; }
|
|
138
199
|
.directory-browser svg { width:18px; height:18px; fill:none; stroke:currentColor; stroke-width:1.7; stroke-linecap:round; stroke-linejoin:round; }
|
|
139
|
-
.directory-
|
|
140
|
-
.directory-
|
|
141
|
-
.directory-
|
|
142
|
-
.directory-
|
|
143
|
-
.directory-browser__row { display:flex; width:100%; align-items:center; gap:9px; border:0; border-radius:7px; padding:8px 10px; background:transparent; color:var(--q-ink-1); text-align:left; }
|
|
144
|
-
.directory-browser__row:hover,
|
|
145
|
-
.directory-browser__row[data-selected="true"] { background:var(--q-background-hover); }
|
|
200
|
+
.directory-browser__body { flex:1; min-height:0; overflow:auto; padding:13px 20px; }
|
|
201
|
+
.directory-browser__row { display:flex; width:100%; height:28px; align-items:center; gap:7px; border:0; border-radius:6px; padding:0 6px; background:transparent; color:var(--q-ink-1); font-size:14px; text-align:left; }
|
|
202
|
+
.directory-browser__row:hover { background:var(--q-background-hover); }
|
|
203
|
+
.directory-browser__row[data-selected="true"] { background:var(--q-background-selected); }
|
|
146
204
|
.directory-browser__row > span { min-width:0; flex:1; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
|
147
205
|
.directory-browser__open { display:grid; width:26px; height:26px; place-items:center; border:0; background:transparent; color:var(--q-ink-3); }
|
|
148
206
|
.directory-browser__status,
|
|
149
|
-
.directory-browser__error { margin:20px; color:var(--q-ink-3); font-size:
|
|
207
|
+
.directory-browser__error { margin:20px; color:var(--q-ink-3); font-size:14px; text-align:center; }
|
|
150
208
|
.directory-browser__error { color:var(--q-orange-default); }
|
|
151
|
-
.directory-browser__footer { display:flex; width:100%;
|
|
152
|
-
.directory-browser__path { min-width:0; margin:0; overflow:hidden; color:var(--q-ink-3); font-size:13px; text-overflow:ellipsis; white-space:nowrap; text-align:left; }
|
|
153
|
-
.directory-browser__footer-row { display:flex; align-items:center; justify-content:space-between; gap:12px; }
|
|
209
|
+
.directory-browser__footer { display:flex; width:100%; align-items:center; justify-content:space-between; gap:12px; }
|
|
154
210
|
.directory-browser__footer-left { display:flex; min-width:0; flex:1; align-items:center; gap:12px; }
|
|
155
211
|
.directory-browser__footer-right { display:flex; flex-shrink:0; align-items:center; gap:8px; }
|
|
156
|
-
.directory-browser__new-folder { display:inline-flex; align-items:center; gap:6px; border:1px solid var(--q-
|
|
157
|
-
|
|
158
|
-
.directory-
|
|
159
|
-
.directory-browser__hidden
|
|
212
|
+
.directory-browser__new-folder { display:inline-flex; align-items:center; gap:6px; border:1px solid var(--q-btn-stroke); border-radius:999px; padding:0 14px; height:34px; background:transparent; color:var(--q-ink-1); font:inherit; font-size:14px; white-space:nowrap; }
|
|
213
|
+
/* The footer is a dialog slot, outside `.directory-browser`, so it misses the shared icon rule. */
|
|
214
|
+
.directory-browser__new-folder svg { width:14px; height:14px; fill:none; stroke:currentColor; stroke-width:1.7; stroke-linecap:round; stroke-linejoin:round; }
|
|
215
|
+
.directory-browser__hidden { display:inline-flex; align-items:center; color:var(--q-ink-2); font-size:13px; white-space:nowrap; cursor:pointer; }
|
|
216
|
+
.directory-browser__hidden input { position:absolute; width:1px; height:1px; overflow:hidden; opacity:0; pointer-events:none; }
|
|
217
|
+
.directory-browser__hidden:has(input:checked) { color:var(--q-ink-1); }
|
|
160
218
|
.directory-browser__cancel,
|
|
161
|
-
.directory-browser__choose { min-width:
|
|
162
|
-
.directory-browser__cancel { border:1px solid var(--q-
|
|
163
|
-
.directory-
|
|
219
|
+
.directory-browser__choose { min-width:67px; height:34px; border-radius:999px; padding:0 14px; font-size:14px; }
|
|
220
|
+
.directory-browser__cancel { border:1px solid var(--q-btn-stroke); background:transparent; color:var(--q-ink-1); }
|
|
221
|
+
.directory-browser__new-folder:hover:not(:disabled),
|
|
222
|
+
.directory-browser__cancel:hover:not(:disabled) { background:var(--q-btn-bg-hover); border-color:var(--q-btn-stroke-hover); }
|
|
223
|
+
/* The confirm button is the inverse surface: white on dark, dark on light. */
|
|
224
|
+
.directory-browser__choose { border:0; background:var(--q-background-9); color:var(--q-background-1); }
|
|
164
225
|
.directory-browser button,
|
|
165
226
|
.directory-browser__cancel,
|
|
166
227
|
.directory-browser__choose { cursor:pointer; font:inherit; }
|
|
@@ -1,17 +1,26 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<Teleport to="body">
|
|
3
3
|
<Transition name="lares-dialog">
|
|
4
|
-
<div
|
|
4
|
+
<div
|
|
5
|
+
v-if="open"
|
|
6
|
+
class="lares-pc-dialog"
|
|
7
|
+
role="dialog"
|
|
8
|
+
aria-modal="true"
|
|
9
|
+
:aria-labelledby="titleId"
|
|
10
|
+
:data-variant="variant || undefined"
|
|
11
|
+
>
|
|
5
12
|
<button class="lares-pc-dialog__backdrop" type="button" :aria-label="closeLabel" @click="close" />
|
|
6
13
|
<section class="lares-pc-dialog__surface" :style="{ width: surfaceWidth }">
|
|
7
14
|
<header class="lares-pc-dialog__header">
|
|
8
|
-
<
|
|
9
|
-
<
|
|
10
|
-
|
|
11
|
-
<
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
+
<slot name="header" :title-id="titleId">
|
|
16
|
+
<div class="lares-pc-dialog__title">
|
|
17
|
+
<h2 :id="titleId">{{ title }}</h2>
|
|
18
|
+
<button type="button" class="lares-pc-dialog__close" :aria-label="closeLabel" @click="close">
|
|
19
|
+
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="m7 7 10 10M17 7 7 17" /></svg>
|
|
20
|
+
</button>
|
|
21
|
+
</div>
|
|
22
|
+
<p v-if="description">{{ description }}</p>
|
|
23
|
+
</slot>
|
|
15
24
|
</header>
|
|
16
25
|
<div class="lares-pc-dialog__body">
|
|
17
26
|
<slot />
|
|
@@ -37,6 +46,7 @@ export default {
|
|
|
37
46
|
closeLabel: { type: String, default: "Close" },
|
|
38
47
|
width: { type: [Number, String], default: 440 },
|
|
39
48
|
busy: { type: Boolean, default: false },
|
|
49
|
+
variant: { type: String, default: "" },
|
|
40
50
|
},
|
|
41
51
|
emits: ["close"],
|
|
42
52
|
data() {
|
|
@@ -82,4 +92,9 @@ export default {
|
|
|
82
92
|
.lares-pc-dialog__close svg { width:18px; height:18px; fill:none; stroke:currentColor; stroke-width:1.7; stroke-linecap:round; }
|
|
83
93
|
.lares-pc-dialog__body { min-height:0; overflow:auto; padding:16px; }
|
|
84
94
|
.lares-pc-dialog__footer { display:flex; min-height:58px; flex-shrink:0; align-items:center; justify-content:flex-end; gap:8px; padding:0 16px; border-top:1px solid var(--q-separator); }
|
|
95
|
+
/* The directory variant only changes geometry; colors stay on the shared tokens. */
|
|
96
|
+
.lares-pc-dialog[data-variant="directory"] .lares-pc-dialog__surface { border-radius:20px; }
|
|
97
|
+
.lares-pc-dialog[data-variant="directory"] .lares-pc-dialog__header { padding:0; }
|
|
98
|
+
.lares-pc-dialog[data-variant="directory"] .lares-pc-dialog__body { padding:0; }
|
|
99
|
+
.lares-pc-dialog[data-variant="directory"] .lares-pc-dialog__footer { min-height:62px; padding:0 22px; }
|
|
85
100
|
</style>
|