ikanban-web 0.2.6 → 0.2.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/README.md +25 -20
- package/dist/assets/{ghostty-web-BsS8qmby.js → ghostty-web-BnnMBzi5.js} +1 -1
- package/dist/assets/{home-BVcfU4oW.js → home-DeLBKxk3.js} +1 -1
- package/dist/assets/{index-BbYqEnGb.js → index-ehO8js0v.js} +36 -36
- package/dist/assets/{index-B1DFsnCu.css → index-pnX-hTQP.css} +1 -1
- package/dist/assets/session-D7vMISoz.js +24 -0
- package/dist/index.html +2 -2
- package/package.json +1 -1
- package/src/components/session/session-header.tsx +2 -211
- package/src/context/server.tsx +24 -4
- package/src/i18n/en.ts +1 -1
- package/src/i18n/parity.test.ts +4 -22
- package/src/i18n/zh.ts +1 -1
- package/src/pages/layout.tsx +2 -2
- package/src/pages/session/use-session-commands.tsx +0 -121
- package/dist/assets/session-CZ-wC_3g.js +0 -24
- package/src/i18n/ar.ts +0 -751
- package/src/i18n/br.ts +0 -759
- package/src/i18n/bs.ts +0 -836
- package/src/i18n/da.ts +0 -830
- package/src/i18n/de.ts +0 -768
- package/src/i18n/es.ts +0 -842
- package/src/i18n/fr.ts +0 -766
- package/src/i18n/ja.ts +0 -755
- package/src/i18n/ko.ts +0 -755
- package/src/i18n/no.ts +0 -838
- package/src/i18n/pl.ts +0 -757
- package/src/i18n/ru.ts +0 -838
- package/src/i18n/th.ts +0 -828
- package/src/i18n/tr.ts +0 -849
- package/src/i18n/zht.ts +0 -821
package/dist/index.html
CHANGED
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
<meta property="og:image" content="/ikanban/social-share.png" />
|
|
15
15
|
<meta property="twitter:image" content="/social-share.png" />
|
|
16
16
|
<script id="oc-theme-preload-script" src="/ikanban/oc-theme-preload.js"></script>
|
|
17
|
-
<script type="module" crossorigin src="/ikanban/assets/index-
|
|
18
|
-
<link rel="stylesheet" crossorigin href="/ikanban/assets/index-
|
|
17
|
+
<script type="module" crossorigin src="/ikanban/assets/index-ehO8js0v.js"></script>
|
|
18
|
+
<link rel="stylesheet" crossorigin href="/ikanban/assets/index-pnX-hTQP.css">
|
|
19
19
|
</head>
|
|
20
20
|
<body class="antialiased overscroll-none text-12-regular overflow-hidden">
|
|
21
21
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
package/package.json
CHANGED
|
@@ -4,23 +4,19 @@ import { DropdownMenu } from "ikanban-ui/dropdown-menu"
|
|
|
4
4
|
import { Icon } from "ikanban-ui/icon"
|
|
5
5
|
import { IconButton } from "ikanban-ui/icon-button"
|
|
6
6
|
import { Keybind } from "ikanban-ui/keybind"
|
|
7
|
-
import { Popover } from "ikanban-ui/popover"
|
|
8
7
|
import { Spinner } from "ikanban-ui/spinner"
|
|
9
|
-
import { TextField } from "ikanban-ui/text-field"
|
|
10
8
|
import { showToast } from "ikanban-ui/toast"
|
|
11
|
-
import {
|
|
9
|
+
import { TooltipKeybind } from "ikanban-ui/tooltip"
|
|
12
10
|
import { getFilename } from "ikanban-utils/path"
|
|
13
11
|
import { useParams } from "@solidjs/router"
|
|
14
|
-
import { createEffect, createMemo, For,
|
|
12
|
+
import { createEffect, createMemo, For, Show } from "solid-js"
|
|
15
13
|
import { createStore } from "solid-js/store"
|
|
16
14
|
import { Portal } from "solid-js/web"
|
|
17
15
|
import { useCommand } from "@/context/command"
|
|
18
|
-
import { useGlobalSDK } from "@/context/global-sdk"
|
|
19
16
|
import { useLanguage } from "@/context/language"
|
|
20
17
|
import { useLayout } from "@/context/layout"
|
|
21
18
|
import { usePlatform } from "@/context/platform"
|
|
22
19
|
import { useServer } from "@/context/server"
|
|
23
|
-
import { useSync } from "@/context/sync"
|
|
24
20
|
import { decode64 } from "@/utils/base64"
|
|
25
21
|
import { Persist, persisted } from "@/utils/persist"
|
|
26
22
|
import { StatusPopover } from "../status-popover"
|
|
@@ -134,99 +130,11 @@ const showRequestError = (language: ReturnType<typeof useLanguage>, err: unknown
|
|
|
134
130
|
})
|
|
135
131
|
}
|
|
136
132
|
|
|
137
|
-
function useSessionShare(args: {
|
|
138
|
-
globalSDK: ReturnType<typeof useGlobalSDK>
|
|
139
|
-
currentSession: () =>
|
|
140
|
-
| {
|
|
141
|
-
share?: {
|
|
142
|
-
url?: string
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
| undefined
|
|
146
|
-
sessionID: () => string | undefined
|
|
147
|
-
projectDirectory: () => string
|
|
148
|
-
platform: ReturnType<typeof usePlatform>
|
|
149
|
-
}) {
|
|
150
|
-
const [state, setState] = createStore({
|
|
151
|
-
share: false,
|
|
152
|
-
unshare: false,
|
|
153
|
-
copied: false,
|
|
154
|
-
timer: undefined as number | undefined,
|
|
155
|
-
})
|
|
156
|
-
const shareUrl = createMemo(() => args.currentSession()?.share?.url)
|
|
157
|
-
|
|
158
|
-
createEffect(() => {
|
|
159
|
-
const url = shareUrl()
|
|
160
|
-
if (url) return
|
|
161
|
-
if (state.timer) window.clearTimeout(state.timer)
|
|
162
|
-
setState({ copied: false, timer: undefined })
|
|
163
|
-
})
|
|
164
|
-
|
|
165
|
-
onCleanup(() => {
|
|
166
|
-
if (state.timer) window.clearTimeout(state.timer)
|
|
167
|
-
})
|
|
168
|
-
|
|
169
|
-
const shareSession = () => {
|
|
170
|
-
const sessionID = args.sessionID()
|
|
171
|
-
if (!sessionID || state.share) return
|
|
172
|
-
setState("share", true)
|
|
173
|
-
args.globalSDK.client.session
|
|
174
|
-
.share({ sessionID, directory: args.projectDirectory() })
|
|
175
|
-
.catch((error) => {
|
|
176
|
-
console.error("Failed to share session", error)
|
|
177
|
-
})
|
|
178
|
-
.finally(() => {
|
|
179
|
-
setState("share", false)
|
|
180
|
-
})
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
const unshareSession = () => {
|
|
184
|
-
const sessionID = args.sessionID()
|
|
185
|
-
if (!sessionID || state.unshare) return
|
|
186
|
-
setState("unshare", true)
|
|
187
|
-
args.globalSDK.client.session
|
|
188
|
-
.unshare({ sessionID, directory: args.projectDirectory() })
|
|
189
|
-
.catch((error) => {
|
|
190
|
-
console.error("Failed to unshare session", error)
|
|
191
|
-
})
|
|
192
|
-
.finally(() => {
|
|
193
|
-
setState("unshare", false)
|
|
194
|
-
})
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
const copyLink = (onError: (error: unknown) => void) => {
|
|
198
|
-
const url = shareUrl()
|
|
199
|
-
if (!url) return
|
|
200
|
-
navigator.clipboard
|
|
201
|
-
.writeText(url)
|
|
202
|
-
.then(() => {
|
|
203
|
-
if (state.timer) window.clearTimeout(state.timer)
|
|
204
|
-
setState("copied", true)
|
|
205
|
-
const timer = window.setTimeout(() => {
|
|
206
|
-
setState("copied", false)
|
|
207
|
-
setState("timer", undefined)
|
|
208
|
-
}, 3000)
|
|
209
|
-
setState("timer", timer)
|
|
210
|
-
})
|
|
211
|
-
.catch(onError)
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
const viewShare = () => {
|
|
215
|
-
const url = shareUrl()
|
|
216
|
-
if (!url) return
|
|
217
|
-
args.platform.openLink(url)
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
return { state, shareUrl, shareSession, unshareSession, copyLink, viewShare }
|
|
221
|
-
}
|
|
222
|
-
|
|
223
133
|
export function SessionHeader() {
|
|
224
|
-
const globalSDK = useGlobalSDK()
|
|
225
134
|
const layout = useLayout()
|
|
226
135
|
const params = useParams()
|
|
227
136
|
const command = useCommand()
|
|
228
137
|
const server = useServer()
|
|
229
|
-
const sync = useSync()
|
|
230
138
|
const platform = usePlatform()
|
|
231
139
|
const language = useLanguage()
|
|
232
140
|
|
|
@@ -243,9 +151,6 @@ export function SessionHeader() {
|
|
|
243
151
|
})
|
|
244
152
|
const hotkey = createMemo(() => command.keybind("file.open"))
|
|
245
153
|
|
|
246
|
-
const currentSession = createMemo(() => (params.id ? sync.session.get(params.id) : undefined))
|
|
247
|
-
const shareEnabled = createMemo(() => sync.data.config.share !== "disabled")
|
|
248
|
-
const showShare = createMemo(() => shareEnabled() && !!params.id)
|
|
249
154
|
const sessionKey = createMemo(() => `${params.dir}${params.id ? "/" + params.id : ""}`)
|
|
250
155
|
const view = createMemo(() => layout.view(sessionKey))
|
|
251
156
|
const os = createMemo(() => detectOS(platform))
|
|
@@ -343,14 +248,6 @@ export function SessionHeader() {
|
|
|
343
248
|
.catch((err: unknown) => showRequestError(language, err))
|
|
344
249
|
}
|
|
345
250
|
|
|
346
|
-
const share = useSessionShare({
|
|
347
|
-
globalSDK,
|
|
348
|
-
currentSession,
|
|
349
|
-
sessionID: () => params.id,
|
|
350
|
-
projectDirectory,
|
|
351
|
-
platform,
|
|
352
|
-
})
|
|
353
|
-
|
|
354
251
|
const centerMount = createMemo(() => document.getElementById("opencode-titlebar-center"))
|
|
355
252
|
const rightMount = createMemo(() => document.getElementById("opencode-titlebar-right"))
|
|
356
253
|
|
|
@@ -505,112 +402,6 @@ export function SessionHeader() {
|
|
|
505
402
|
</Show>
|
|
506
403
|
</div>
|
|
507
404
|
</Show>
|
|
508
|
-
<Show when={showShare()}>
|
|
509
|
-
<div class="flex items-center">
|
|
510
|
-
<Popover
|
|
511
|
-
title={language.t("session.share.popover.title")}
|
|
512
|
-
description={
|
|
513
|
-
share.shareUrl()
|
|
514
|
-
? language.t("session.share.popover.description.shared")
|
|
515
|
-
: language.t("session.share.popover.description.unshared")
|
|
516
|
-
}
|
|
517
|
-
gutter={4}
|
|
518
|
-
placement="bottom-end"
|
|
519
|
-
shift={-64}
|
|
520
|
-
class="rounded-xl [&_[data-slot=popover-close-button]]:hidden"
|
|
521
|
-
triggerAs={Button}
|
|
522
|
-
triggerProps={{
|
|
523
|
-
variant: "ghost",
|
|
524
|
-
class:
|
|
525
|
-
"rounded-md h-[24px] px-3 border border-border-weak-base bg-surface-panel shadow-none data-[expanded]:bg-surface-base-active",
|
|
526
|
-
classList: {
|
|
527
|
-
"rounded-r-none": share.shareUrl() !== undefined,
|
|
528
|
-
"border-r-0": share.shareUrl() !== undefined,
|
|
529
|
-
},
|
|
530
|
-
style: { scale: 1 },
|
|
531
|
-
}}
|
|
532
|
-
trigger={<span class="text-12-regular">{language.t("session.share.action.share")}</span>}
|
|
533
|
-
>
|
|
534
|
-
<div class="flex flex-col gap-2">
|
|
535
|
-
<Show
|
|
536
|
-
when={share.shareUrl()}
|
|
537
|
-
fallback={
|
|
538
|
-
<div class="flex">
|
|
539
|
-
<Button
|
|
540
|
-
size="large"
|
|
541
|
-
variant="primary"
|
|
542
|
-
class="w-1/2"
|
|
543
|
-
onClick={share.shareSession}
|
|
544
|
-
disabled={share.state.share}
|
|
545
|
-
>
|
|
546
|
-
{share.state.share
|
|
547
|
-
? language.t("session.share.action.publishing")
|
|
548
|
-
: language.t("session.share.action.publish")}
|
|
549
|
-
</Button>
|
|
550
|
-
</div>
|
|
551
|
-
}
|
|
552
|
-
>
|
|
553
|
-
<div class="flex flex-col gap-2">
|
|
554
|
-
<TextField
|
|
555
|
-
value={share.shareUrl() ?? ""}
|
|
556
|
-
readOnly
|
|
557
|
-
copyable
|
|
558
|
-
copyKind="link"
|
|
559
|
-
tabIndex={-1}
|
|
560
|
-
class="w-full"
|
|
561
|
-
/>
|
|
562
|
-
<div class="grid grid-cols-2 gap-2">
|
|
563
|
-
<Button
|
|
564
|
-
size="large"
|
|
565
|
-
variant="secondary"
|
|
566
|
-
class="w-full shadow-none border border-border-weak-base"
|
|
567
|
-
onClick={share.unshareSession}
|
|
568
|
-
disabled={share.state.unshare}
|
|
569
|
-
>
|
|
570
|
-
{share.state.unshare
|
|
571
|
-
? language.t("session.share.action.unpublishing")
|
|
572
|
-
: language.t("session.share.action.unpublish")}
|
|
573
|
-
</Button>
|
|
574
|
-
<Button
|
|
575
|
-
size="large"
|
|
576
|
-
variant="primary"
|
|
577
|
-
class="w-full"
|
|
578
|
-
onClick={share.viewShare}
|
|
579
|
-
disabled={share.state.unshare}
|
|
580
|
-
>
|
|
581
|
-
{language.t("session.share.action.view")}
|
|
582
|
-
</Button>
|
|
583
|
-
</div>
|
|
584
|
-
</div>
|
|
585
|
-
</Show>
|
|
586
|
-
</div>
|
|
587
|
-
</Popover>
|
|
588
|
-
<Show when={share.shareUrl()} fallback={<div aria-hidden="true" />}>
|
|
589
|
-
<Tooltip
|
|
590
|
-
value={
|
|
591
|
-
share.state.copied
|
|
592
|
-
? language.t("session.share.copy.copied")
|
|
593
|
-
: language.t("session.share.copy.copyLink")
|
|
594
|
-
}
|
|
595
|
-
placement="top"
|
|
596
|
-
gutter={8}
|
|
597
|
-
>
|
|
598
|
-
<IconButton
|
|
599
|
-
icon={share.state.copied ? "check" : "link"}
|
|
600
|
-
variant="ghost"
|
|
601
|
-
class="rounded-l-none h-[24px] border border-border-weak-base bg-surface-panel shadow-none"
|
|
602
|
-
onClick={() => share.copyLink((error) => showRequestError(language, error))}
|
|
603
|
-
disabled={share.state.unshare}
|
|
604
|
-
aria-label={
|
|
605
|
-
share.state.copied
|
|
606
|
-
? language.t("session.share.copy.copied")
|
|
607
|
-
: language.t("session.share.copy.copyLink")
|
|
608
|
-
}
|
|
609
|
-
/>
|
|
610
|
-
</Tooltip>
|
|
611
|
-
</Show>
|
|
612
|
-
</div>
|
|
613
|
-
</Show>
|
|
614
405
|
<div class="flex items-center gap-1">
|
|
615
406
|
<div class="hidden md:flex items-center gap-1 shrink-0">
|
|
616
407
|
<TooltipKeybind
|
package/src/context/server.tsx
CHANGED
|
@@ -102,6 +102,7 @@ export const { use: useServer, provider: ServerProvider } = createSimpleContext(
|
|
|
102
102
|
Persist.global("server", ["server.v3"]),
|
|
103
103
|
createStore({
|
|
104
104
|
list: [] as StoredServer[],
|
|
105
|
+
deleted: [] as ServerConnection.Key[],
|
|
105
106
|
projects: {} as Record<string, StoredProject[]>,
|
|
106
107
|
lastProject: {} as Record<string, string>,
|
|
107
108
|
}),
|
|
@@ -110,8 +111,9 @@ export const { use: useServer, provider: ServerProvider } = createSimpleContext(
|
|
|
110
111
|
const url = (x: StoredServer) => (typeof x === "string" ? x : "type" in x ? x.http.url : x.url)
|
|
111
112
|
|
|
112
113
|
const allServers = createMemo((): Array<ServerConnection.Any> => {
|
|
114
|
+
const deletedSet = new Set(store.deleted)
|
|
113
115
|
const servers = [
|
|
114
|
-
...(props.servers ?? []),
|
|
116
|
+
...(props.servers ?? []).filter((s) => !deletedSet.has(ServerConnection.key(s))),
|
|
115
117
|
...store.list.map((value) =>
|
|
116
118
|
typeof value === "string"
|
|
117
119
|
? {
|
|
@@ -172,6 +174,7 @@ export const { use: useServer, provider: ServerProvider } = createSimpleContext(
|
|
|
172
174
|
const url_ = normalizeServerUrl(input.http.url)
|
|
173
175
|
if (!url_) return
|
|
174
176
|
const conn = { ...input, http: { ...input.http, url: url_ } }
|
|
177
|
+
const connKey = ServerConnection.key(conn)
|
|
175
178
|
return batch(() => {
|
|
176
179
|
const existing = store.list.findIndex((x) => url(x) === url_)
|
|
177
180
|
if (existing !== -1) {
|
|
@@ -179,18 +182,35 @@ export const { use: useServer, provider: ServerProvider } = createSimpleContext(
|
|
|
179
182
|
} else {
|
|
180
183
|
setStore("list", store.list.length, conn)
|
|
181
184
|
}
|
|
182
|
-
|
|
185
|
+
// If this server was previously deleted, un-delete it
|
|
186
|
+
if (store.deleted.includes(connKey)) {
|
|
187
|
+
setStore("deleted", store.deleted.filter((k) => k !== connKey))
|
|
188
|
+
}
|
|
189
|
+
setState("active", connKey)
|
|
183
190
|
return conn
|
|
184
191
|
})
|
|
185
192
|
}
|
|
186
193
|
|
|
187
194
|
function remove(key: ServerConnection.Key) {
|
|
188
195
|
const list = store.list.filter((x) => url(x) !== key)
|
|
196
|
+
const isPropServer = (props.servers ?? []).some((s) => ServerConnection.key(s) === key)
|
|
189
197
|
batch(() => {
|
|
190
198
|
setStore("list", list)
|
|
199
|
+
// If it's a prop-injected server, record it as deleted so it stays gone after reload
|
|
200
|
+
if (isPropServer && !store.deleted.includes(key)) {
|
|
201
|
+
setStore("deleted", [...store.deleted, key])
|
|
202
|
+
}
|
|
191
203
|
if (state.active === key) {
|
|
192
|
-
const
|
|
193
|
-
|
|
204
|
+
const nextStored = list[0]
|
|
205
|
+
const nextProp = (props.servers ?? []).find(
|
|
206
|
+
(s) => !store.deleted.includes(ServerConnection.key(s)) && ServerConnection.key(s) !== key,
|
|
207
|
+
)
|
|
208
|
+
const nextKey = nextStored
|
|
209
|
+
? ServerConnection.Key.make(url(nextStored))
|
|
210
|
+
: nextProp
|
|
211
|
+
? ServerConnection.key(nextProp)
|
|
212
|
+
: props.defaultServer
|
|
213
|
+
setState("active", nextKey)
|
|
194
214
|
}
|
|
195
215
|
})
|
|
196
216
|
}
|
package/src/i18n/en.ts
CHANGED
|
@@ -610,7 +610,7 @@ export const dict = {
|
|
|
610
610
|
"sidebar.project.viewAllSessions": "View all sessions",
|
|
611
611
|
"sidebar.project.clearNotifications": "Clear notifications",
|
|
612
612
|
|
|
613
|
-
"app.name.desktop": "
|
|
613
|
+
"app.name.desktop": "iKanban",
|
|
614
614
|
|
|
615
615
|
"settings.section.desktop": "Desktop",
|
|
616
616
|
"settings.section.server": "Server",
|
package/src/i18n/parity.test.ts
CHANGED
|
@@ -1,32 +1,14 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test"
|
|
2
2
|
import { dict as en } from "./en"
|
|
3
|
-
import { dict as ar } from "./ar"
|
|
4
|
-
import { dict as br } from "./br"
|
|
5
|
-
import { dict as bs } from "./bs"
|
|
6
|
-
import { dict as da } from "./da"
|
|
7
|
-
import { dict as de } from "./de"
|
|
8
|
-
import { dict as es } from "./es"
|
|
9
|
-
import { dict as fr } from "./fr"
|
|
10
|
-
import { dict as ja } from "./ja"
|
|
11
|
-
import { dict as ko } from "./ko"
|
|
12
|
-
import { dict as no } from "./no"
|
|
13
|
-
import { dict as pl } from "./pl"
|
|
14
|
-
import { dict as ru } from "./ru"
|
|
15
|
-
import { dict as th } from "./th"
|
|
16
3
|
import { dict as zh } from "./zh"
|
|
17
|
-
import { dict as zht } from "./zht"
|
|
18
|
-
import { dict as tr } from "./tr"
|
|
19
4
|
|
|
20
|
-
const locales = [ar, br, bs, da, de, es, fr, ja, ko, no, pl, ru, th, tr, zh, zht]
|
|
21
5
|
const keys = ["command.session.previous.unseen", "command.session.next.unseen"] as const
|
|
22
6
|
|
|
23
7
|
describe("i18n parity", () => {
|
|
24
|
-
test("
|
|
25
|
-
for (const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
expect(locale[key]).not.toBe(en[key])
|
|
29
|
-
}
|
|
8
|
+
test("zh translates targeted unseen session keys", () => {
|
|
9
|
+
for (const key of keys) {
|
|
10
|
+
expect(zh[key]).toBeDefined()
|
|
11
|
+
expect(zh[key]).not.toBe(en[key])
|
|
30
12
|
}
|
|
31
13
|
})
|
|
32
14
|
})
|
package/src/i18n/zh.ts
CHANGED
|
@@ -581,7 +581,7 @@ export const dict = {
|
|
|
581
581
|
"sidebar.project.viewAllSessions": "查看全部会话",
|
|
582
582
|
"sidebar.project.clearNotifications": "清除通知",
|
|
583
583
|
|
|
584
|
-
"app.name.desktop": "
|
|
584
|
+
"app.name.desktop": "iKanban",
|
|
585
585
|
|
|
586
586
|
"settings.section.desktop": "桌面",
|
|
587
587
|
"settings.section.server": "服务器",
|
package/src/pages/layout.tsx
CHANGED
|
@@ -2080,7 +2080,7 @@ export default function Layout(props: ParentProps) {
|
|
|
2080
2080
|
settingsKeybind={() => command.keybind("settings.open")}
|
|
2081
2081
|
onOpenSettings={openSettings}
|
|
2082
2082
|
helpLabel={() => language.t("sidebar.help")}
|
|
2083
|
-
onOpenHelp={() => platform.openLink("https://
|
|
2083
|
+
onOpenHelp={() => platform.openLink("https://github.com/isomoes/ikanban/issues")}
|
|
2084
2084
|
renderPanel={() => (
|
|
2085
2085
|
<Show when={currentProject()} keyed>
|
|
2086
2086
|
{(project) => <SidebarPanel project={project} />}
|
|
@@ -2149,7 +2149,7 @@ export default function Layout(props: ParentProps) {
|
|
|
2149
2149
|
settingsKeybind={() => command.keybind("settings.open")}
|
|
2150
2150
|
onOpenSettings={openSettings}
|
|
2151
2151
|
helpLabel={() => language.t("sidebar.help")}
|
|
2152
|
-
onOpenHelp={() => platform.openLink("https://
|
|
2152
|
+
onOpenHelp={() => platform.openLink("https://github.com/isomoes/ikanban/issues")}
|
|
2153
2153
|
renderPanel={() => <SidebarPanel project={currentProject()} mobile />}
|
|
2154
2154
|
/>
|
|
2155
2155
|
</nav>
|
|
@@ -404,126 +404,6 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
|
|
|
404
404
|
}),
|
|
405
405
|
]);
|
|
406
406
|
|
|
407
|
-
const shareCommands = createMemo(() => {
|
|
408
|
-
if (sync.data.config.share === "disabled") return [];
|
|
409
|
-
return [
|
|
410
|
-
sessionCommand({
|
|
411
|
-
id: "session.share",
|
|
412
|
-
title: info()?.share?.url
|
|
413
|
-
? language.t("session.share.copy.copyLink")
|
|
414
|
-
: language.t("command.session.share"),
|
|
415
|
-
description: info()?.share?.url
|
|
416
|
-
? language.t("toast.session.share.success.description")
|
|
417
|
-
: language.t("command.session.share.description"),
|
|
418
|
-
slash: "share",
|
|
419
|
-
disabled: !params.id,
|
|
420
|
-
onSelect: async () => {
|
|
421
|
-
if (!params.id) return;
|
|
422
|
-
|
|
423
|
-
const write = (value: string) => {
|
|
424
|
-
const body =
|
|
425
|
-
typeof document === "undefined" ? undefined : document.body;
|
|
426
|
-
if (body) {
|
|
427
|
-
const textarea = document.createElement("textarea");
|
|
428
|
-
textarea.value = value;
|
|
429
|
-
textarea.setAttribute("readonly", "");
|
|
430
|
-
textarea.style.position = "fixed";
|
|
431
|
-
textarea.style.opacity = "0";
|
|
432
|
-
textarea.style.pointerEvents = "none";
|
|
433
|
-
body.appendChild(textarea);
|
|
434
|
-
textarea.select();
|
|
435
|
-
const copied = document.execCommand("copy");
|
|
436
|
-
body.removeChild(textarea);
|
|
437
|
-
if (copied) return Promise.resolve(true);
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
const clipboard =
|
|
441
|
-
typeof navigator === "undefined"
|
|
442
|
-
? undefined
|
|
443
|
-
: navigator.clipboard;
|
|
444
|
-
if (!clipboard?.writeText) return Promise.resolve(false);
|
|
445
|
-
return clipboard.writeText(value).then(
|
|
446
|
-
() => true,
|
|
447
|
-
() => false,
|
|
448
|
-
);
|
|
449
|
-
};
|
|
450
|
-
|
|
451
|
-
const copy = async (url: string, existing: boolean) => {
|
|
452
|
-
const ok = await write(url);
|
|
453
|
-
if (!ok) {
|
|
454
|
-
showToast({
|
|
455
|
-
title: language.t("toast.session.share.copyFailed.title"),
|
|
456
|
-
variant: "error",
|
|
457
|
-
});
|
|
458
|
-
return;
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
showToast({
|
|
462
|
-
title: existing
|
|
463
|
-
? language.t("session.share.copy.copied")
|
|
464
|
-
: language.t("toast.session.share.success.title"),
|
|
465
|
-
description: language.t(
|
|
466
|
-
"toast.session.share.success.description",
|
|
467
|
-
),
|
|
468
|
-
variant: "success",
|
|
469
|
-
});
|
|
470
|
-
};
|
|
471
|
-
|
|
472
|
-
const existing = info()?.share?.url;
|
|
473
|
-
if (existing) {
|
|
474
|
-
await copy(existing, true);
|
|
475
|
-
return;
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
const url = await sdk.client.session
|
|
479
|
-
.share({ sessionID: params.id })
|
|
480
|
-
.then((res) => res.data?.share?.url)
|
|
481
|
-
.catch(() => undefined);
|
|
482
|
-
if (!url) {
|
|
483
|
-
showToast({
|
|
484
|
-
title: language.t("toast.session.share.failed.title"),
|
|
485
|
-
description: language.t("toast.session.share.failed.description"),
|
|
486
|
-
variant: "error",
|
|
487
|
-
});
|
|
488
|
-
return;
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
await copy(url, false);
|
|
492
|
-
},
|
|
493
|
-
}),
|
|
494
|
-
sessionCommand({
|
|
495
|
-
id: "session.unshare",
|
|
496
|
-
title: language.t("command.session.unshare"),
|
|
497
|
-
description: language.t("command.session.unshare.description"),
|
|
498
|
-
slash: "unshare",
|
|
499
|
-
disabled: !params.id || !info()?.share?.url,
|
|
500
|
-
onSelect: async () => {
|
|
501
|
-
if (!params.id) return;
|
|
502
|
-
await sdk.client.session
|
|
503
|
-
.unshare({ sessionID: params.id })
|
|
504
|
-
.then(() =>
|
|
505
|
-
showToast({
|
|
506
|
-
title: language.t("toast.session.unshare.success.title"),
|
|
507
|
-
description: language.t(
|
|
508
|
-
"toast.session.unshare.success.description",
|
|
509
|
-
),
|
|
510
|
-
variant: "success",
|
|
511
|
-
}),
|
|
512
|
-
)
|
|
513
|
-
.catch(() =>
|
|
514
|
-
showToast({
|
|
515
|
-
title: language.t("toast.session.unshare.failed.title"),
|
|
516
|
-
description: language.t(
|
|
517
|
-
"toast.session.unshare.failed.description",
|
|
518
|
-
),
|
|
519
|
-
variant: "error",
|
|
520
|
-
}),
|
|
521
|
-
);
|
|
522
|
-
},
|
|
523
|
-
}),
|
|
524
|
-
];
|
|
525
|
-
});
|
|
526
|
-
|
|
527
407
|
command.register("session", () =>
|
|
528
408
|
[
|
|
529
409
|
sessionCommands(),
|
|
@@ -534,7 +414,6 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
|
|
|
534
414
|
agentCommands(),
|
|
535
415
|
permissionCommands(),
|
|
536
416
|
sessionActionCommands(),
|
|
537
|
-
shareCommands(),
|
|
538
417
|
].flatMap((x) => x),
|
|
539
418
|
);
|
|
540
419
|
};
|