my-durable 0.0.19 → 0.0.21
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/apps/dpi-backend/src/git.ts +16 -2
- package/apps/dpi-playground/apps/dpi-playground-fe/dist/assets/index-BvDyZN2S.css +1 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/dist/assets/index-Cv3w8azJ.js +80 -0
- package/apps/dpi-playground/apps/dpi-playground-fe/dist/index.html +2 -2
- package/apps/dpi-playground/apps/dpi-playground-fe/src/components/GitChip.vue +5 -4
- package/apps/dpi-playground/apps/dpi-playground-fe/src/components/GitPanel.vue +43 -12
- package/package.json +1 -1
- package/apps/dpi-playground/apps/dpi-playground-fe/dist/assets/index-Cbohux_s.js +0 -80
- package/apps/dpi-playground/apps/dpi-playground-fe/dist/assets/index-DJZ6A1cN.css +0 -1
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
<meta name="referrer" content="no-referrer" />
|
|
8
8
|
<link rel="icon" type="image/svg+xml" href="/icons/durable-pi.svg" />
|
|
9
9
|
<title>Durable Pi | Network</title>
|
|
10
|
-
<script type="module" crossorigin src="/assets/index-
|
|
11
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
10
|
+
<script type="module" crossorigin src="/assets/index-Cv3w8azJ.js"></script>
|
|
11
|
+
<link rel="stylesheet" crossorigin href="/assets/index-BvDyZN2S.css">
|
|
12
12
|
</head>
|
|
13
13
|
<body>
|
|
14
14
|
<div id="app"></div>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Git chip for the session breadcrumb: branch + dirty at a glance, click for
|
|
3
3
|
// the real panel — status, worktrees, fetch/pull/log/commit, new worktree.
|
|
4
4
|
// Everything goes through the device's whitelisted /v1/git API.
|
|
5
|
-
import { computed, onBeforeUnmount, onMounted, ref, watch } from "vue";
|
|
5
|
+
import { computed, onBeforeUnmount, onMounted, onServerPrefetch, ref, watch } from "vue";
|
|
6
6
|
import { ChevronDown, GitBranch, RefreshCw } from "lucide-vue-next";
|
|
7
7
|
import { deviceId, sessionSummary, notify } from "../client/store";
|
|
8
8
|
import { deviceClient } from "../client/hub";
|
|
@@ -29,9 +29,10 @@ async function refresh() {
|
|
|
29
29
|
status.value = null;
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
-
|
|
32
|
+
// Client: fire now, the chip fills reactively. SSR waits on the prefetch
|
|
33
|
+
// hook — a top-level await would need <Suspense> and silently never mount.
|
|
34
|
+
if (!import.meta.env.SSR) void refresh();
|
|
35
|
+
onServerPrefetch(() => refresh());
|
|
35
36
|
watch(cwd, () => void refresh());
|
|
36
37
|
onMounted(() => {
|
|
37
38
|
timer = setInterval(refresh, 15000);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
// Inspector GIT tab: everything the device knows about the session's folder.
|
|
3
3
|
// Always visible — "not a git repository" is a state, not a reason to hide.
|
|
4
|
-
import { computed, onBeforeUnmount, onMounted, ref, watch } from "vue";
|
|
4
|
+
import { computed, onBeforeUnmount, onMounted, onServerPrefetch, ref, watch } from "vue";
|
|
5
5
|
import { RefreshCw } from "lucide-vue-next";
|
|
6
6
|
import { deviceId, sessionSummary, notify } from "../client/store";
|
|
7
7
|
import { deviceClient } from "../client/hub";
|
|
@@ -10,6 +10,9 @@ import type { GitStatus } from "@sdk/client";
|
|
|
10
10
|
const props = defineProps<{ deviceId?: string; cwd?: string }>();
|
|
11
11
|
const status = ref<GitStatus | null>(null);
|
|
12
12
|
const error = ref("");
|
|
13
|
+
const loading = ref(false); // first fetch, nothing to show yet — SAY SO
|
|
14
|
+
const refreshing = ref(false); // refetch with data on screen — dim, keep, never blank
|
|
15
|
+
let inFlight: Promise<void> | undefined;
|
|
13
16
|
const busy = ref("");
|
|
14
17
|
const out = ref("");
|
|
15
18
|
const commitMsg = ref("");
|
|
@@ -24,17 +27,35 @@ let timer: ReturnType<typeof setInterval> | undefined;
|
|
|
24
27
|
async function refresh() {
|
|
25
28
|
const c = client();
|
|
26
29
|
if (!c || !cwd.value) return;
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
if (inFlight) return inFlight; // interval + session-switch must not pile up
|
|
31
|
+
const first = !status.value;
|
|
32
|
+
if (first) loading.value = true;
|
|
33
|
+
else refreshing.value = true;
|
|
34
|
+
inFlight = (async () => {
|
|
35
|
+
try {
|
|
36
|
+
status.value = await c.gitStatus(cwd.value);
|
|
37
|
+
error.value = "";
|
|
38
|
+
} catch (e) {
|
|
39
|
+
const raw = (e as Error).message ?? "";
|
|
40
|
+
// Keep the LAST GOOD status — a dropped tunnel must not blank the tab
|
|
41
|
+
// the operator is reading (reported: "treo rất lâu", "Cannot reach …").
|
|
42
|
+
error.value = /fetch failed|Cannot reach|Failed to fetch|NetworkError|ERR_NAME/i.test(raw)
|
|
43
|
+
? `device unreachable — check that it is online, then retry (${raw.slice(0, 80)})`
|
|
44
|
+
: raw.slice(0, 160);
|
|
45
|
+
} finally {
|
|
46
|
+
loading.value = false;
|
|
47
|
+
refreshing.value = false;
|
|
48
|
+
inFlight = undefined;
|
|
49
|
+
}
|
|
50
|
+
})();
|
|
51
|
+
return inFlight;
|
|
34
52
|
}
|
|
35
|
-
//
|
|
36
|
-
// "
|
|
37
|
-
|
|
53
|
+
// NO top-level await: an async setup outside <Suspense> never mounts in the
|
|
54
|
+
// browser ("async setup invoked without a Suspense boundary") — the tab
|
|
55
|
+
// rendered EMPTY (reported: "TAB git trống"). Fire now on the client;
|
|
56
|
+
// onServerPrefetch is what SSR actually waits for.
|
|
57
|
+
if (!import.meta.env.SSR) void refresh();
|
|
58
|
+
onServerPrefetch(() => refresh());
|
|
38
59
|
onMounted(() => {
|
|
39
60
|
timer = setInterval(refresh, 10000);
|
|
40
61
|
});
|
|
@@ -120,8 +141,15 @@ async function removeWorktree(path: string) {
|
|
|
120
141
|
<button :disabled="!!busy" title="refresh" @click="refresh()"><RefreshCw :size="14" /></button>
|
|
121
142
|
</header>
|
|
122
143
|
|
|
123
|
-
<p v-if="
|
|
144
|
+
<p v-if="loading" class="empty">loading git…</p>
|
|
145
|
+
<p v-if="refreshing" class="muted note">refreshing…</p>
|
|
146
|
+
|
|
147
|
+
<p v-if="error" class="error">
|
|
148
|
+
{{ error }}
|
|
149
|
+
<button :disabled="!!busy || !!loading" @click="refresh()">retry</button>
|
|
150
|
+
</p>
|
|
124
151
|
|
|
152
|
+
<div v-if="status" :class="{ stale: !!error || refreshing }">
|
|
125
153
|
<p v-if="status && !status.repo" class="empty">
|
|
126
154
|
Not a git repository — run <code>git init</code> (or open a session inside a repo) to see branch, status and worktrees here.
|
|
127
155
|
</p>
|
|
@@ -163,6 +191,7 @@ async function removeWorktree(path: string) {
|
|
|
163
191
|
|
|
164
192
|
<pre v-if="out" class="out">{{ out.slice(0, 6000) }}</pre>
|
|
165
193
|
</template>
|
|
194
|
+
</div>
|
|
166
195
|
</template>
|
|
167
196
|
</div>
|
|
168
197
|
</template>
|
|
@@ -184,5 +213,7 @@ async function removeWorktree(path: string) {
|
|
|
184
213
|
.wt-of dd { color: var(--info, #58a6ff); }
|
|
185
214
|
.out { max-height: 300px; overflow: auto; background: rgb(0 0 0 / 25%); padding: 8px; border-radius: 6px; font-size: 11px; }
|
|
186
215
|
.empty { opacity: 0.75; }
|
|
216
|
+
.note { font-size: 11px; }
|
|
217
|
+
.stale { opacity: 0.55; transition: opacity 0.2s; }
|
|
187
218
|
.error { color: var(--danger, #f85149); }
|
|
188
219
|
</style>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "my-durable",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.21",
|
|
4
4
|
"description": "Durable Pi \u2014 the durable agent network: hub web UI, device backend daemon, TUI admin and the shared SDK, packaged as one runnable monorepo install",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|