ikanban-web 0.2.10 → 0.2.11

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.
@@ -101,7 +101,7 @@ const SessionRow = (props: {
101
101
  cancelHoverPrefetch: () => void
102
102
  }): JSX.Element => (
103
103
  <A
104
- href={`/${props.slug}/session/${props.session.id}`}
104
+ href={`/${props.slug}/${props.session.id}`}
105
105
  class={`flex items-center justify-between gap-3 min-w-0 text-left w-full focus:outline-none transition-[padding] ${props.mobile ? "pr-7" : ""} group-hover/session:pr-7 group-focus-within/session:pr-7 group-active/session:pr-7 ${props.dense ? "py-0.5" : "py-1"}`}
106
106
  onPointerEnter={props.scheduleHoverPrefetch}
107
107
  onPointerLeave={props.cancelHoverPrefetch}
@@ -310,7 +310,7 @@ export const SessionItem = (props: SessionItemProps): JSX.Element => {
310
310
  onMessageSelect={(message) => {
311
311
  if (!isActive()) {
312
312
  layout.pendingMessage.set(`${base64Encode(props.session.directory)}/${props.session.id}`, message.id)
313
- navigate(`${props.slug}/session/${props.session.id}`)
313
+ navigate(`/${props.slug}/${props.session.id}`)
314
314
  return
315
315
  }
316
316
  window.history.replaceState(null, "", `#message-${message.id}`)
@@ -360,7 +360,7 @@ export const NewSessionItem = (props: {
360
360
  const tooltip = () => props.mobile || !props.sidebarExpanded()
361
361
  const item = (
362
362
  <A
363
- href={`/${props.slug}/session`}
363
+ href={`/${props.slug}`}
364
364
  end
365
365
  class={`flex items-center justify-between gap-3 min-w-0 text-left w-full focus:outline-none ${props.dense ? "py-0.5" : "py-1"}`}
366
366
  onClick={() => {
@@ -437,7 +437,7 @@ export const SortableWorkspace = (props: {
437
437
  root={props.project.worktree}
438
438
  setHoverSession={props.ctx.setHoverSession}
439
439
  clearHoverProjectSoon={props.ctx.clearHoverProjectSoon}
440
- navigateToNewSession={() => navigate(`/${slug()}/session`)}
440
+ navigateToNewSession={() => navigate(`/${slug()}`)}
441
441
  />
442
442
  </div>
443
443
  </div>
@@ -49,7 +49,10 @@ import { useTheme, type ColorScheme } from "ikanban-ui/theme"
49
49
  import { DialogSelectProvider } from "@/components/dialog-select-provider"
50
50
  import { DialogSelectServer } from "@/components/dialog-select-server"
51
51
  import { DialogSettings } from "@/components/dialog-settings"
52
+ import { DialogCommandPalette } from "@/components/dialog-command-palette"
53
+ import { DialogSelectFile } from "@/components/dialog-select-file"
52
54
  import { useCommand, type CommandOption } from "@/context/command"
55
+ import { FileProvider } from "@/context/file"
53
56
  import { ConstrainDragXAxis } from "@/utils/solid-dnd"
54
57
  import { DialogSelectDirectory } from "@/components/dialog-select-directory"
55
58
  import { DialogEditProject } from "@/components/dialog-edit-project"
@@ -121,9 +124,10 @@ export default function Layout(props: ParentProps) {
121
124
  }
122
125
  const colorSchemeLabel = (scheme: ColorScheme) => language.t(colorSchemeKey[scheme])
123
126
  const currentDir = createMemo(() => decode64(params.dir) ?? "")
127
+ const hasDirectoryRoute = createMemo(() => !!params.dir)
124
128
 
125
129
  const [state, setState] = createStore({
126
- autoselect: !initialDirectory,
130
+ autoselect: false,
127
131
  busyWorkspaces: {} as Record<string, boolean>,
128
132
  hoverSession: undefined as string | undefined,
129
133
  hoverProject: undefined as string | undefined,
@@ -196,15 +200,7 @@ export default function Layout(props: ParentProps) {
196
200
  setHoverProject(undefined)
197
201
  })
198
202
 
199
- const autoselecting = createMemo(() => {
200
- if (params.dir) return false
201
- if (!state.autoselect) return false
202
- if (!pageReady()) return true
203
- if (!layoutReady()) return true
204
- const list = layout.projects.list()
205
- if (list.length > 0) return true
206
- return !!server.projects.last()
207
- })
203
+ const autoselecting = createMemo(() => false)
208
204
 
209
205
  createEffect(() => {
210
206
  if (!state.autoselect) return
@@ -325,7 +321,7 @@ export default function Layout(props: ParentProps) {
325
321
  e.details.type === "permission.asked"
326
322
  ? language.t("notification.permission.description", { sessionTitle, projectName })
327
323
  : language.t("notification.question.description", { sessionTitle, projectName })
328
- const href = `/${base64Encode(directory)}/session/${props.sessionID}`
324
+ const href = `/${base64Encode(directory)}/${props.sessionID}`
329
325
 
330
326
  const now = Date.now()
331
327
  const lastAlerted = alertedAtBySession.get(sessionKey) ?? 0
@@ -426,34 +422,6 @@ export default function Layout(props: ParentProps) {
426
422
  return projects.find((p) => p.worktree === root)
427
423
  })
428
424
 
429
- createEffect(
430
- on(
431
- () => ({ ready: pageReady(), layoutReady: layoutReady(), dir: params.dir, list: layout.projects.list() }),
432
- (value) => {
433
- if (!value.ready) return
434
- if (!value.layoutReady) return
435
- if (!state.autoselect) return
436
- if (value.dir) return
437
-
438
- const last = server.projects.last()
439
-
440
- if (value.list.length === 0) {
441
- if (!last) return
442
- setState("autoselect", false)
443
- openProject(last, false)
444
- navigateToProject(last)
445
- return
446
- }
447
-
448
- const next = value.list.find((project) => project.worktree === last) ?? value.list[0]
449
- if (!next) return
450
- setState("autoselect", false)
451
- openProject(next.worktree, false)
452
- navigateToProject(next.worktree)
453
- },
454
- ),
455
- )
456
-
457
425
  const workspaceName = (directory: string, projectId?: string, branch?: string) => {
458
426
  const key = workspaceKey(directory)
459
427
  const direct = store.workspaceName[key] ?? store.workspaceName[directory]
@@ -793,9 +761,9 @@ export default function Layout(props: ParentProps) {
793
761
  )
794
762
  if (session.id === params.id) {
795
763
  if (nextSession) {
796
- navigate(`/${params.dir}/session/${nextSession.id}`)
764
+ navigate(`/${params.dir}/${nextSession.id}`)
797
765
  } else {
798
- navigate(`/${params.dir}/session`)
766
+ navigate(`/${params.dir}`)
799
767
  }
800
768
  }
801
769
  }
@@ -809,6 +777,15 @@ export default function Layout(props: ParentProps) {
809
777
  keybind: "mod+b",
810
778
  onSelect: () => layout.sidebar.toggle(),
811
779
  },
780
+ {
781
+ id: "file.open",
782
+ title: language.t("command.file.open"),
783
+ description: params.dir ? language.t("palette.search.placeholder") : language.t("command.project.open"),
784
+ category: language.t("command.category.file"),
785
+ keybind: "mod+p",
786
+ slash: "open",
787
+ onSelect: openGlobalFilePicker,
788
+ },
812
789
  {
813
790
  id: "project.open",
814
791
  title: language.t("command.project.open"),
@@ -1059,7 +1036,7 @@ export default function Layout(props: ParentProps) {
1059
1036
 
1060
1037
  function navigateToSession(session: Session | undefined) {
1061
1038
  if (!session) return
1062
- navigateWithSidebarReset(`/${base64Encode(session.directory)}/session/${session.id}`)
1039
+ navigateWithSidebarReset(`/${base64Encode(session.directory)}/${session.id}`)
1063
1040
  }
1064
1041
 
1065
1042
  function openProject(directory: string, navigate = true) {
@@ -1124,7 +1101,7 @@ export default function Layout(props: ParentProps) {
1124
1101
  return
1125
1102
  }
1126
1103
 
1127
- navigateWithSidebarReset(`/${base64Encode(next.worktree)}/session`)
1104
+ navigateWithSidebarReset(`/${base64Encode(next.worktree)}`)
1128
1105
  layout.projects.close(directory)
1129
1106
  queueMicrotask(() => {
1130
1107
  void navigateToProject(next.worktree)
@@ -1169,6 +1146,19 @@ export default function Layout(props: ParentProps) {
1169
1146
  }
1170
1147
  }
1171
1148
 
1149
+ function openGlobalFilePicker() {
1150
+ if (!params.dir) {
1151
+ dialog.show(() => <DialogCommandPalette />)
1152
+ return
1153
+ }
1154
+
1155
+ dialog.show(() => (
1156
+ <FileProvider>
1157
+ <DialogSelectFile />
1158
+ </FileProvider>
1159
+ ))
1160
+ }
1161
+
1172
1162
  const deleteWorkspace = async (root: string, directory: string, leaveDeletedWorkspace = false) => {
1173
1163
  if (directory === root) return
1174
1164
 
@@ -1177,7 +1167,7 @@ export default function Layout(props: ParentProps) {
1177
1167
  const deletedKey = workspaceKey(directory)
1178
1168
  const shouldLeave = leaveDeletedWorkspace || (!!params.dir && currentKey === deletedKey)
1179
1169
  if (!leaveDeletedWorkspace && shouldLeave) {
1180
- navigateWithSidebarReset(`/${base64Encode(root)}/session`)
1170
+ navigateWithSidebarReset(`/${base64Encode(root)}`)
1181
1171
  }
1182
1172
 
1183
1173
  setBusy(directory, true)
@@ -1225,7 +1215,7 @@ export default function Layout(props: ParentProps) {
1225
1215
  const valid = dirs.some((item) => workspaceKey(item) === nextKey)
1226
1216
 
1227
1217
  if (params.dir && projectRoot(nextCurrent) === root && !valid) {
1228
- navigateWithSidebarReset(`/${base64Encode(root)}/session`)
1218
+ navigateWithSidebarReset(`/${base64Encode(root)}`)
1229
1219
  }
1230
1220
  }
1231
1221
 
@@ -1294,7 +1284,7 @@ export default function Layout(props: ParentProps) {
1294
1284
  {
1295
1285
  label: language.t("command.session.new"),
1296
1286
  onClick: () => {
1297
- const href = `/${base64Encode(directory)}/session`
1287
+ const href = `/${base64Encode(directory)}`
1298
1288
  navigate(href)
1299
1289
  layout.mobileSidebar.hide()
1300
1290
  },
@@ -1330,7 +1320,7 @@ export default function Layout(props: ParentProps) {
1330
1320
  const handleDelete = () => {
1331
1321
  const leaveDeletedWorkspace = !!params.dir && workspaceKey(currentDir()) === workspaceKey(props.directory)
1332
1322
  if (leaveDeletedWorkspace) {
1333
- navigateWithSidebarReset(`/${base64Encode(props.root)}/session`)
1323
+ navigateWithSidebarReset(`/${base64Encode(props.root)}`)
1334
1324
  }
1335
1325
  dialog.close()
1336
1326
  void deleteWorkspace(props.root, props.directory, leaveDeletedWorkspace)
@@ -1481,6 +1471,10 @@ export default function Layout(props: ParentProps) {
1481
1471
  )
1482
1472
 
1483
1473
  createEffect(() => {
1474
+ if (!hasDirectoryRoute()) {
1475
+ document.documentElement.style.setProperty("--dialog-left-margin", "0px")
1476
+ return
1477
+ }
1484
1478
  const sidebarWidth = layout.sidebar.opened() ? layout.sidebar.width() : 48
1485
1479
  document.documentElement.style.setProperty("--dialog-left-margin", `${sidebarWidth}px`)
1486
1480
  })
@@ -1628,7 +1622,7 @@ export default function Layout(props: ParentProps) {
1628
1622
  })
1629
1623
 
1630
1624
  globalSync.child(created.directory)
1631
- navigateWithSidebarReset(`/${base64Encode(created.directory)}/session`)
1625
+ navigateWithSidebarReset(`/${base64Encode(created.directory)}`)
1632
1626
  }
1633
1627
 
1634
1628
  const workspaceSidebarCtx: WorkspaceSidebarContext = {
@@ -1821,7 +1815,7 @@ export default function Layout(props: ParentProps) {
1821
1815
  size="large"
1822
1816
  icon="plus-small"
1823
1817
  class="w-full"
1824
- onClick={() => navigateWithSidebarReset(`/${base64Encode(p().worktree)}/session`)}
1818
+ onClick={() => navigateWithSidebarReset(`/${base64Encode(p().worktree)}`)}
1825
1819
  >
1826
1820
  {language.t("command.session.new")}
1827
1821
  </Button>
@@ -1923,136 +1917,143 @@ export default function Layout(props: ParentProps) {
1923
1917
 
1924
1918
  return (
1925
1919
  <div class="relative bg-background-base flex-1 min-h-0 flex flex-col select-none [&_input]:select-text [&_textarea]:select-text [&_[contenteditable]]:select-text">
1926
- <Titlebar />
1920
+ <Show when={hasDirectoryRoute()}>
1921
+ <Titlebar />
1922
+ </Show>
1927
1923
  <div class="flex-1 min-h-0 flex">
1928
- <nav
1929
- aria-label={language.t("sidebar.nav.projectsAndSessions")}
1930
- data-component="sidebar-nav-desktop"
1931
- classList={{
1932
- "hidden xl:block": true,
1933
- "relative shrink-0": true,
1934
- }}
1935
- style={{ width: layout.sidebar.opened() ? `${Math.max(layout.sidebar.width(), 244)}px` : "64px" }}
1936
- ref={(el) => {
1937
- setState("nav", el)
1938
- }}
1939
- onMouseEnter={() => {
1940
- if (navLeave.current === undefined) return
1941
- clearTimeout(navLeave.current)
1942
- navLeave.current = undefined
1943
- }}
1944
- onMouseLeave={() => {
1945
- aim.reset()
1946
- if (!sidebarHovering()) return
1947
-
1948
- if (navLeave.current !== undefined) clearTimeout(navLeave.current)
1949
- navLeave.current = window.setTimeout(() => {
1950
- navLeave.current = undefined
1951
- setHoverProject(undefined)
1952
- setState("hoverSession", undefined)
1953
- }, 300)
1954
- }}
1955
- >
1956
- <div class="@container w-full h-full contain-strict">
1957
- <SidebarContent
1958
- opened={() => layout.sidebar.opened()}
1959
- aimMove={aim.move}
1960
- projects={() => layout.projects.list()}
1961
- renderProject={(project) => (
1962
- <SortableProject ctx={projectSidebarCtx} project={project} sortNow={sortNow} />
1963
- )}
1964
- handleDragStart={handleDragStart}
1965
- handleDragEnd={handleDragEnd}
1966
- handleDragOver={handleDragOver}
1967
- openProjectLabel={language.t("command.project.open")}
1968
- openProjectKeybind={() => command.keybind("project.open")}
1969
- onOpenProject={chooseProject}
1970
- renderProjectOverlay={() => (
1971
- <ProjectDragOverlay projects={() => layout.projects.list()} activeProject={() => store.activeProject} />
1972
- )}
1973
- settingsLabel={() => language.t("sidebar.settings")}
1974
- settingsKeybind={() => command.keybind("settings.open")}
1975
- onOpenSettings={openSettings}
1976
- helpLabel={() => language.t("sidebar.help")}
1977
- onOpenHelp={() => platform.openLink("https://github.com/isomoes/ikanban/issues")}
1978
- renderPanel={() => (
1979
- <Show when={currentProject()} keyed>
1980
- {(project) => <SidebarPanel project={project} />}
1981
- </Show>
1982
- )}
1983
- />
1984
- </div>
1985
- <Show when={!layout.sidebar.opened() ? hoverProjectData()?.worktree : undefined} keyed>
1986
- {(worktree) => (
1987
- <div class="absolute inset-y-0 left-16 z-50 flex" onMouseEnter={aim.reset}>
1988
- <SidebarPanel project={hoverProjectData()} />
1924
+ <Show when={hasDirectoryRoute()}>
1925
+ <>
1926
+ <nav
1927
+ aria-label={language.t("sidebar.nav.projectsAndSessions")}
1928
+ data-component="sidebar-nav-desktop"
1929
+ classList={{
1930
+ "hidden xl:block": true,
1931
+ "relative shrink-0": true,
1932
+ }}
1933
+ style={{ width: layout.sidebar.opened() ? `${Math.max(layout.sidebar.width(), 244)}px` : "64px" }}
1934
+ ref={(el) => {
1935
+ setState("nav", el)
1936
+ }}
1937
+ onMouseEnter={() => {
1938
+ if (navLeave.current === undefined) return
1939
+ clearTimeout(navLeave.current)
1940
+ navLeave.current = undefined
1941
+ }}
1942
+ onMouseLeave={() => {
1943
+ aim.reset()
1944
+ if (!sidebarHovering()) return
1945
+
1946
+ if (navLeave.current !== undefined) clearTimeout(navLeave.current)
1947
+ navLeave.current = window.setTimeout(() => {
1948
+ navLeave.current = undefined
1949
+ setHoverProject(undefined)
1950
+ setState("hoverSession", undefined)
1951
+ }, 300)
1952
+ }}
1953
+ >
1954
+ <div class="@container w-full h-full contain-strict">
1955
+ <SidebarContent
1956
+ opened={() => layout.sidebar.opened()}
1957
+ aimMove={aim.move}
1958
+ projects={() => layout.projects.list()}
1959
+ renderProject={(project) => (
1960
+ <SortableProject ctx={projectSidebarCtx} project={project} sortNow={sortNow} />
1961
+ )}
1962
+ handleDragStart={handleDragStart}
1963
+ handleDragEnd={handleDragEnd}
1964
+ handleDragOver={handleDragOver}
1965
+ openProjectLabel={language.t("command.project.open")}
1966
+ openProjectKeybind={() => command.keybind("project.open")}
1967
+ onOpenProject={chooseProject}
1968
+ renderProjectOverlay={() => (
1969
+ <ProjectDragOverlay projects={() => layout.projects.list()} activeProject={() => store.activeProject} />
1970
+ )}
1971
+ settingsLabel={() => language.t("sidebar.settings")}
1972
+ settingsKeybind={() => command.keybind("settings.open")}
1973
+ onOpenSettings={openSettings}
1974
+ helpLabel={() => language.t("sidebar.help")}
1975
+ onOpenHelp={() => platform.openLink("https://github.com/isomoes/ikanban/issues")}
1976
+ renderPanel={() => (
1977
+ <Show when={currentProject()} keyed>
1978
+ {(project) => <SidebarPanel project={project} />}
1979
+ </Show>
1980
+ )}
1981
+ />
1989
1982
  </div>
1990
- )}
1991
- </Show>
1992
- <Show when={layout.sidebar.opened()}>
1993
- <ResizeHandle
1994
- direction="horizontal"
1995
- size={layout.sidebar.width()}
1996
- min={244}
1997
- max={typeof window === "undefined" ? 1000 : window.innerWidth * 0.3 + 64}
1998
- collapseThreshold={244}
1999
- onResize={layout.sidebar.resize}
2000
- onCollapse={layout.sidebar.close}
2001
- />
2002
- </Show>
2003
- </nav>
2004
- <div class="xl:hidden">
2005
- <div
2006
- classList={{
2007
- "fixed inset-x-0 top-10 bottom-0 z-40 transition-opacity duration-200": true,
2008
- "opacity-100 pointer-events-auto": layout.mobileSidebar.opened(),
2009
- "opacity-0 pointer-events-none": !layout.mobileSidebar.opened(),
2010
- }}
2011
- onClick={(e) => {
2012
- if (e.target === e.currentTarget) layout.mobileSidebar.hide()
2013
- }}
2014
- />
2015
- <nav
2016
- aria-label={language.t("sidebar.nav.projectsAndSessions")}
2017
- data-component="sidebar-nav-mobile"
2018
- classList={{
2019
- "@container fixed top-10 bottom-0 left-0 z-50 w-72 bg-background-base transition-transform duration-200 ease-out": true,
2020
- "translate-x-0": layout.mobileSidebar.opened(),
2021
- "-translate-x-full": !layout.mobileSidebar.opened(),
2022
- }}
2023
- onClick={(e) => e.stopPropagation()}
2024
- >
2025
- <SidebarContent
2026
- mobile
2027
- opened={() => layout.sidebar.opened()}
2028
- aimMove={aim.move}
2029
- projects={() => layout.projects.list()}
2030
- renderProject={(project) => (
2031
- <SortableProject ctx={projectSidebarCtx} project={project} sortNow={sortNow} mobile />
2032
- )}
2033
- handleDragStart={handleDragStart}
2034
- handleDragEnd={handleDragEnd}
2035
- handleDragOver={handleDragOver}
2036
- openProjectLabel={language.t("command.project.open")}
2037
- openProjectKeybind={() => command.keybind("project.open")}
2038
- onOpenProject={chooseProject}
2039
- renderProjectOverlay={() => (
2040
- <ProjectDragOverlay projects={() => layout.projects.list()} activeProject={() => store.activeProject} />
2041
- )}
2042
- settingsLabel={() => language.t("sidebar.settings")}
2043
- settingsKeybind={() => command.keybind("settings.open")}
2044
- onOpenSettings={openSettings}
2045
- helpLabel={() => language.t("sidebar.help")}
2046
- onOpenHelp={() => platform.openLink("https://github.com/isomoes/ikanban/issues")}
2047
- renderPanel={() => <SidebarPanel project={currentProject()} mobile />}
2048
- />
2049
- </nav>
2050
- </div>
1983
+ <Show when={!layout.sidebar.opened() ? hoverProjectData()?.worktree : undefined} keyed>
1984
+ {(worktree) => (
1985
+ <div class="absolute inset-y-0 left-16 z-50 flex" onMouseEnter={aim.reset}>
1986
+ <SidebarPanel project={hoverProjectData()} />
1987
+ </div>
1988
+ )}
1989
+ </Show>
1990
+ <Show when={layout.sidebar.opened()}>
1991
+ <ResizeHandle
1992
+ direction="horizontal"
1993
+ size={layout.sidebar.width()}
1994
+ min={244}
1995
+ max={typeof window === "undefined" ? 1000 : window.innerWidth * 0.3 + 64}
1996
+ collapseThreshold={244}
1997
+ onResize={layout.sidebar.resize}
1998
+ onCollapse={layout.sidebar.close}
1999
+ />
2000
+ </Show>
2001
+ </nav>
2002
+ <div class="xl:hidden">
2003
+ <div
2004
+ classList={{
2005
+ "fixed inset-x-0 top-10 bottom-0 z-40 transition-opacity duration-200": true,
2006
+ "opacity-100 pointer-events-auto": layout.mobileSidebar.opened(),
2007
+ "opacity-0 pointer-events-none": !layout.mobileSidebar.opened(),
2008
+ }}
2009
+ onClick={(e) => {
2010
+ if (e.target === e.currentTarget) layout.mobileSidebar.hide()
2011
+ }}
2012
+ />
2013
+ <nav
2014
+ aria-label={language.t("sidebar.nav.projectsAndSessions")}
2015
+ data-component="sidebar-nav-mobile"
2016
+ classList={{
2017
+ "@container fixed top-10 bottom-0 left-0 z-50 w-72 bg-background-base transition-transform duration-200 ease-out": true,
2018
+ "translate-x-0": layout.mobileSidebar.opened(),
2019
+ "-translate-x-full": !layout.mobileSidebar.opened(),
2020
+ }}
2021
+ onClick={(e) => e.stopPropagation()}
2022
+ >
2023
+ <SidebarContent
2024
+ mobile
2025
+ opened={() => layout.sidebar.opened()}
2026
+ aimMove={aim.move}
2027
+ projects={() => layout.projects.list()}
2028
+ renderProject={(project) => (
2029
+ <SortableProject ctx={projectSidebarCtx} project={project} sortNow={sortNow} mobile />
2030
+ )}
2031
+ handleDragStart={handleDragStart}
2032
+ handleDragEnd={handleDragEnd}
2033
+ handleDragOver={handleDragOver}
2034
+ openProjectLabel={language.t("command.project.open")}
2035
+ openProjectKeybind={() => command.keybind("project.open")}
2036
+ onOpenProject={chooseProject}
2037
+ renderProjectOverlay={() => (
2038
+ <ProjectDragOverlay projects={() => layout.projects.list()} activeProject={() => store.activeProject} />
2039
+ )}
2040
+ settingsLabel={() => language.t("sidebar.settings")}
2041
+ settingsKeybind={() => command.keybind("settings.open")}
2042
+ onOpenSettings={openSettings}
2043
+ helpLabel={() => language.t("sidebar.help")}
2044
+ onOpenHelp={() => platform.openLink("https://github.com/isomoes/ikanban/issues")}
2045
+ renderPanel={() => <SidebarPanel project={currentProject()} mobile />}
2046
+ />
2047
+ </nav>
2048
+ </div>
2049
+ </>
2050
+ </Show>
2051
2051
 
2052
2052
  <main
2053
2053
  classList={{
2054
2054
  "size-full overflow-x-hidden flex flex-col items-start contain-strict border-t border-border-weak-base": true,
2055
- "xl:border-l xl:rounded-tl-[12px]": !layout.sidebar.opened(),
2055
+ "xl:border-l xl:rounded-tl-[12px]": hasDirectoryRoute() && !layout.sidebar.opened(),
2056
+ "border-t-0": !hasDirectoryRoute(),
2056
2057
  }}
2057
2058
  >
2058
2059
  <Show when={!autoselecting()} fallback={<div class="size-full" />}>
@@ -346,14 +346,14 @@ export function MessageTimeline(props: {
346
346
  const navigateAfterSessionRemoval = (sessionID: string, parentID?: string, nextSessionID?: string) => {
347
347
  if (params.id !== sessionID) return
348
348
  if (parentID) {
349
- navigate(`/${params.dir}/session/${parentID}`)
349
+ navigate(`/${params.dir}/${parentID}`)
350
350
  return
351
351
  }
352
352
  if (nextSessionID) {
353
- navigate(`/${params.dir}/session/${nextSessionID}`)
353
+ navigate(`/${params.dir}/${nextSessionID}`)
354
354
  return
355
355
  }
356
- navigate(`/${params.dir}/session`)
356
+ navigate(`/${params.dir}`)
357
357
  }
358
358
 
359
359
  const archiveSession = async (sessionID: string) => {
@@ -446,7 +446,7 @@ export function MessageTimeline(props: {
446
446
  const navigateParent = () => {
447
447
  const id = parentID()
448
448
  if (!id) return
449
- navigate(`/${params.dir}/session/${id}`)
449
+ navigate(`/${params.dir}/${id}`)
450
450
  }
451
451
 
452
452
  function DialogDeleteSession(props: { sessionID: string }) {
@@ -194,7 +194,7 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
194
194
  title: language.t("command.session.new"),
195
195
  keybind: "mod+shift+s",
196
196
  slash: "new",
197
- onSelect: () => navigate(`/${params.dir}/session`),
197
+ onSelect: () => navigate(`/${params.dir}`),
198
198
  }),
199
199
  ]);
200
200
 
@@ -1319,7 +1319,7 @@ export default function Page() {
1319
1319
  if (!target) return
1320
1320
  if (target === sdk.directory) return
1321
1321
  layout.projects.open(target)
1322
- navigate(`/${base64Encode(target)}/session`)
1322
+ navigate(`/${base64Encode(target)}`)
1323
1323
  }}
1324
1324
  />
1325
1325
  </Match>
@@ -1 +0,0 @@
1
- import{u as K,a as N,b as q,c as H,d as T,e as V,f as J,g as Q,h as P,s as U,i as W,j as t,k as s,L as X,B as z,l as I,m as Y,n as Z,D as ee,M as A,I as L,S as R,o as O,p as te,t as c,F as re,q as se,r as le,v as ie}from"./index-n2BMSLCz.js";var ne=c("<div>"),oe=c('<div class="mt-6 flex w-full flex-col gap-6"><section class="flex flex-col gap-4"><div class="grid gap-3 lg:grid-cols-2">'),ae=c('<div class="mt-6 rounded-2xl border border-dashed border-border-weak bg-background-base/60 px-6 py-10"><div class="mx-auto flex max-w-md flex-col items-center gap-3 text-center"><div class="flex size-12 items-center justify-center rounded-2xl border border-border-weak bg-background-base/80"></div><div class="flex flex-col gap-1 items-center justify-center"><div class="text-14-medium text-text-strong"></div><div class="text-12-regular text-text-weak">'),ce=c('<div class="mx-auto w-full max-w-7xl px-4 py-6 md:px-6 md:py-8"><div class="flex flex-col gap-4 md:flex-row md:items-start md:justify-between"><div class=min-w-0><div class="flex items-center gap-3"><div class="flex size-10 items-center justify-center rounded-2xl border border-border-weak bg-background-base/80"></div><div><div class="text-18-medium text-text-strong"></div><div class="mt-0.5 text-12-regular text-text-weak"></div></div></div></div><div class="flex items-center gap-2 self-start md:justify-end">'),de=c('<div class="rounded-xl border border-dashed border-border-weak px-3 py-8 text-center text-12-regular text-text-weak">'),ue=c('<section class="rounded-2xl border border-border-weak bg-background-base/80 p-3 md:p-4 min-h-72"><div class="flex items-center justify-between gap-3"><div class="flex items-center gap-2 min-w-0"><div></div><div class="text-14-medium text-text-strong"></div></div><div class="text-12-mono text-text-weak"></div></div><div class="mt-3 flex flex-col gap-2">'),me=c('<span class="min-w-0 flex-1 truncate text-12-regular text-text-weak">'),ge=c('<div class="flex shrink-0 items-center gap-2 text-12-regular text-text-weak"><span>'),fe=c('<div class="overflow-hidden rounded-xl border border-border-weak bg-background-base/70 hover:border-border-strong"><div class="flex items-start justify-between gap-3 px-3 pt-3"><div class="min-w-0 flex-1 pt-1"><div class="truncate text-14-medium leading-tight text-text-strong">');function xe(){const l=K(),j=N(),x=q(),h=H(),v=T(),b=V(),g=J(),i=Q(),C=P(()=>l.data.project.slice().sort((e,r)=>(r.time.updated??r.time.created)-(e.time.updated??e.time.created)).slice(0,5)),n=P(()=>{const e=Date.now(),r={progress:[],idle:[]};for(const a of C()){const[d]=l.child(a.worktree),u=U(d,e);for(const f of u){const y=d.session_status[f.id],B=f.time.updated??f.time.created,_={session:f,projectDirectory:a.worktree,updatedAt:B};if(y?.type==="busy"||y?.type==="retry"){r.progress.push(_);continue}r.idle.push(_)}}for(const a of Object.keys(r))r[a].sort((d,u)=>u.updatedAt-d.updatedAt);return r});W(()=>{for(const e of C())l.child(e.worktree)});const w=P(()=>{const e=g.healthy();return e===!0?"bg-icon-success-base":e===!1?"bg-icon-critical-base":"bg-border-weak-base"});function $(e){j.projects.open(e),g.projects.touch(e),v(`/${O(e)}/session`)}function S(e,r){j.projects.open(e),g.projects.touch(e),v(`/${O(e)}/session/${r}`)}async function D(e,r){const[,a]=l.child(e,{bootstrap:!1});await b.client.session.update({sessionID:r,time:{archived:Date.now()}}),a("session",d=>d.filter(u=>u.id!==r))}async function o(){function e(r){if(Array.isArray(r))for(const a of r)$(a);else r&&$(r)}if(x.openDirectoryPickerDialog&&g.isLocal()){const r=await x.openDirectoryPickerDialog?.({title:i.t("command.project.open"),multiple:!0});e(r)}else h.show(()=>s(te,{multiple:!0,onSelect:e}),()=>e(null))}return(()=>{var e=ce(),r=e.firstChild,a=r.firstChild,d=a.firstChild,u=d.firstChild,f=u.nextSibling,y=f.firstChild,B=y.nextSibling,_=a.nextSibling;return t(u,s(X,{class:"w-6 opacity-90"})),t(y,()=>i.t("home.sessionBoard")),t(B,()=>i.t("home.sessionBoard.description")),t(_,s(z,{icon:"folder-add-left",size:"normal",class:"pl-2 pr-3",onClick:o,get children(){return i.t("command.project.open")}}),null),t(_,s(z,{size:"normal",variant:"ghost",class:"pr-3 text-13-regular text-text-weak",onClick:()=>h.show(()=>s(ee,{})),get children(){return[(()=>{var m=ne();return I(p=>Y(m,{"size-2 rounded-full":!0,[w()]:!0},p)),m})(),Z(()=>g.name)]}}),null),t(e,s(R,{get children(){return[s(A,{get when(){return l.data.project.length>0},get children(){var m=oe(),p=m.firstChild,k=p.firstChild;return t(k,s(E,{get title(){return i.t("home.sessionBoard.progress")},icon:"brain",tone:"progress",get cards(){return n().progress},onOpen:S,onArchive:D,get empty(){return i.t("home.sessionBoard.emptyProgress")}}),null),t(k,s(E,{get title(){return i.t("home.sessionBoard.idle")},icon:"dash",tone:"idle",get cards(){return n().idle},onOpen:S,onArchive:D,get empty(){return i.t("home.sessionBoard.emptyIdle")}}),null),m}}),s(A,{when:!0,get children(){var m=ae(),p=m.firstChild,k=p.firstChild,F=k.nextSibling,M=F.firstChild,G=M.nextSibling;return t(k,s(L,{name:"folder-add-left",size:"large"})),t(M,()=>i.t("home.empty.title")),t(G,()=>i.t("home.empty.description")),t(p,s(z,{class:"mt-1 px-3",onClick:o,get children(){return i.t("command.project.open")}}),null),m}})]}}),null),e})()}function E(l){const j=()=>l.tone==="progress"?"bg-emerald-500/12 text-emerald-300 border-emerald-500/20":"bg-sky-500/12 text-sky-300 border-sky-500/20";return(()=>{var x=ue(),h=x.firstChild,v=h.firstChild,b=v.firstChild,g=b.nextSibling,i=v.nextSibling,C=h.nextSibling;return t(b,s(L,{get name(){return l.icon},size:"small"})),t(g,()=>l.title),t(i,()=>l.cards.length),t(C,s(R,{get children(){return[s(A,{get when(){return l.cards.length>0},get children(){return s(re,{get each(){return l.cards},children:n=>(()=>{var w=fe(),$=w.firstChild,S=$.firstChild,D=S.firstChild;return t(D,()=>n.session.title),t($,s(se,{icon:"archive",variant:"ghost",class:"size-7 rounded-md","aria-label":"Archive session",onClick:o=>{o.preventDefault(),o.stopPropagation(),l.onArchive(n.projectDirectory,n.session.id)}}),null),t(w,s(z,{variant:"ghost",class:"flex h-auto w-full min-w-0 items-center justify-between gap-3 rounded-none border-0 bg-transparent px-3 pb-3 pt-2 text-left",onClick:()=>l.onOpen(n.projectDirectory,n.session.id),get children(){return[(()=>{var o=me();return t(o,()=>n.projectDirectory.replace(/^.*?([^/\\]+(?:[/\\][^/\\]+)?)$/,"$1")),o})(),(()=>{var o=ge(),e=o.firstChild;return t(e,()=>le.fromMillis(n.updatedAt).toRelative()),t(o,s(L,{name:"arrow-right",size:"small",class:"text-icon-weak shrink-0"}),null),o})()]}}),null),w})()})}}),s(A,{when:!0,get children(){var n=de();return t(n,()=>l.empty),n}})]}})),I(()=>ie(b,`size-7 rounded-full border flex items-center justify-center ${j()}`)),x})()}export{xe as default};