# All files / src/utils navigation.ts

76.85% Statements 93/121
92.85% Branches 26/28
57.14% Functions 8/14
76.85% Lines 93/121

Press n or j to go to the next uncovered block, b, p or k for the previous block.

    1
    2
    3
    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
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214

    1x
    1x

    1x

    1x
    3x
    3x

    1x
    5x
    5x
    5x

    1x
    10x
    10x
    10x
    10x
    10x
    10x
    10x
    10x
    5x
    5x

    1x
    5x
    4x
    4x
    10x
    3x
    3x
    7x
    7x

    1x
    2x
    1x
    1x
    2x

    1x
    1x

    1x
    5x
    5x
    5x
    5x
    5x
    5x
    4x
    4x
    5x
    2x
    2x
    5x
    5x
    2x
    2x
    2x
    5x
    5x

    1x
    1x
    1x
    1x

    1x

    1x

    1x
    4x
    4x

    1x

    1x

    1x

    1x

    1x

    1x

    1x

    1x
    2x
    2x

    1x

    1x

    1x

    1x

    1x

    1x

    1x

    1x

    1x

    1x

    1x

    1x

    1x

    1x

    1x

    1x

    1x

    1x

     /**
     * Centralized URL builder functions and navigation helpers for all application routes.
     *
     * Every component that needs to navigate should import from here
     * instead of hardcoding URL strings.
     */

    import { useCallback } from "react";
    import { useNavigate, type NavigateOptions, type To } from "react-router";

    /**
     * Wrapper around react-router's `useNavigate` that returns a fire-and-forget
     * navigate function. This avoids lint conflicts between `no-floating-promises`
     * (which wants the returned `Promise<void> | void` handled) and `no-void`
     * (which forbids the `void` operator).
     */
    export function useAppNavigate(): (to: To | number, options?: NavigateOptions) => void {
      const nav = useNavigate();
      return useCallback(
        (to: To | number, options?: NavigateOptions) => {
          if (typeof to === "number") {
            // eslint-disable-next-line @typescript-eslint/no-floating-promises
            nav(to);
          } else {
            // eslint-disable-next-line @typescript-eslint/no-floating-promises
            nav(to, options);
          }
        },
        [nav],
      );
    }

    /** Build URL for a session detail page. */
    export function sessionUrl(sessionId: string): string {
      return `/sessions/${encodeURIComponent(sessionId)}`;
    }

    /** Build URL for a workspace overview page, nested under its environment when available. */
    export function workspaceUrl(workspaceId: string, environmentId?: string): string {
      if (environmentId) {
        return `/environments/${encodeURIComponent(environmentId)}/workspaces/${encodeURIComponent(workspaceId)}`;
      }
      // Fallback to legacy route so WorkspaceRedirect can resolve the environment.
      return `/workspaces/${encodeURIComponent(workspaceId)}`;
    }

    /** Build URL for a task detail page, optionally targeting a specific tab and workspace/environment scope. */
    export function taskUrl(
      taskId: string,
      tab?: "stream",
      workspaceId?: string,
      environmentId?: string,
    ): string {
      const encodedTaskId = encodeURIComponent(taskId);
      let base: string;
      if (workspaceId && environmentId) {
        base = `/environments/${encodeURIComponent(environmentId)}/workspaces/${encodeURIComponent(workspaceId)}/tasks/${encodedTaskId}`;
      } else if (workspaceId) {
        // Fallback to legacy route so WorkspaceRedirect can resolve the environment.
        base = `/workspaces/${encodeURIComponent(workspaceId)}/tasks/${encodedTaskId}`;
      } else {
        base = `/tasks/${encodedTaskId}`;
      }
      if (tab) {
        return `${base}/${tab}`;
      }
      return base;
    }

    /** Build URL for the task edit page. */
    export function taskEditUrl(taskId: string, workspaceId?: string, environmentId?: string): string {
      if (workspaceId && environmentId) {
        return `/environments/${encodeURIComponent(environmentId)}/workspaces/${encodeURIComponent(workspaceId)}/tasks/${encodeURIComponent(taskId)}/edit`;
      }
      if (workspaceId) {
        // Fallback to legacy route so WorkspaceRedirect can resolve the environment.
        return `/workspaces/${encodeURIComponent(workspaceId)}/tasks/${encodeURIComponent(taskId)}/edit`;
      }
      return `/tasks/${encodeURIComponent(taskId)}/edit`;
    }

    /** Build URL for the new task form. */
    export function newTaskUrl(
      workspaceId?: string,
      parentTaskId?: string,
      environmentId?: string,
    ): string {
      const params = new URLSearchParams();
      if (workspaceId) {
        params.set("workspace", workspaceId);
      }
      if (parentTaskId) {
        params.set("parent", parentTaskId);
      }
      const qs = params.toString();
      if (workspaceId && environmentId) {
        const base = `/environments/${encodeURIComponent(environmentId)}/workspaces/${encodeURIComponent(workspaceId)}/tasks/new`;
        return parentTaskId ? `${base}?parent=${encodeURIComponent(parentTaskId)}` : base;
      }
      return qs ? `/tasks/new?${qs}` : "/tasks/new";
    }

    /** Build URL for the new chat form. */
    export function newChatUrl(environmentId: string): string {
      const params = new URLSearchParams({ env: environmentId });
      return `/sessions/new?${params.toString()}`;
    }

    /** URL for the environments landing page. */
    export const ENVIRONMENTS_URL: string = "/environments";

    /** URL for the new environment form. */
    export const NEW_ENVIRONMENT_URL: string = "/environments/new";

    /** Build URL for an environment detail page. */
    export function environmentUrl(environmentId: string): string {
      return `/environments/${encodeURIComponent(environmentId)}`;
    }

    /** Build URL for the environment edit page. */
    export function environmentEditUrl(environmentId: string): string {
      return `/environments/${encodeURIComponent(environmentId)}/edit`;
    }

    /** URL for the settings page. */
    export const SETTINGS_URL: string = "/settings";

    /** URL for the settings environments tab. */
    export const SETTINGS_ENVIRONMENTS_URL: string = "/settings/environments";

    /** URL for the settings credentials tab. */
    export const SETTINGS_CREDENTIALS_URL: string = "/settings/credentials";

    /** URL for the GitHub accounts settings tab. */
    export const SETTINGS_GITHUB_ACCOUNTS_URL: string = "/settings/github-accounts";

    /** URL for the top-level Persona Library page. */
    export const PERSONAS_URL: string = "/personas";

    /** URL for the new persona form. */
    export const NEW_PERSONA_URL: string = "/personas/new";

    /** Build URL for a persona detail page. */
    export function personaUrl(personaId: string): string {
      return `/personas/${encodeURIComponent(personaId)}`;
    }

    /** Possible agent tab identifiers for URL construction (#1419). */
    export type AgentTab = "chat" | "sessions" | "schedules" | "settings";

    /** Build URL for an agent page, optionally targeting a specific tab. */
    export function agentUrl(agentId: string, tab?: AgentTab): string {
      const base = `/agents/${encodeURIComponent(agentId)}`;
      if (!tab || tab === "chat") return base;
      return `${base}/${tab}`;
    }

    /** Routes that render their own nav bar and suppress the global AppNav. */
    export function hasOwnNav(pathname: string): boolean {
      return /^\/agents\//.test(pathname);
    }

    /** URL for the top-level Schedules surface. */
    export const SCHEDULES_URL: string = "/schedules";

    /** URL for the new schedule form. */
    export const NEW_SCHEDULE_URL: string = "/schedules/new";

    /** Build URL for a schedule detail page. */
    export function scheduleUrl(scheduleId: string): string {
      return `/schedules/${encodeURIComponent(scheduleId)}`;
    }

    /** URL for the settings appearance tab. */
    export const SETTINGS_APPEARANCE_URL: string = "/settings/appearance";

    /** URL for the settings about tab. */
    export const SETTINGS_ABOUT_URL: string = "/settings/about";

    /** URL for the keyboard shortcuts reference tab. */
    export const SETTINGS_SHORTCUTS_URL: string = "/settings/shortcuts";

    /** URL for the device pairing page. */
    export const PAIR_PATH: string = "/pair";

    /** URL for the root-task ("Root") chat page. */
    export const CHAT_URL: string = "/chat";

    /** URL for the Coordination page (read-only IPC stream inventory). */
    export const COORDINATION_URL: string = "/coordination";

    /** URL for the Sessions activity monitor (all sessions, grouped by environment). */
    export const SESSIONS_URL: string = "/sessions";

    /** URL for the home dashboard page. */
    export const HOME_URL: string = "/";

    /** URL for the tasks landing page. */
    export const TASKS_URL: string = "/tasks";

    /** URL for the workspaces landing page. */
    export const WORKSPACES_URL: string = "/workspaces";

    /** URL for the new workspace form. */
    export const NEW_WORKSPACE_URL: string = "/workspaces/new";

    /** URL for the knowledge graph explorer page. */
    export const KNOWLEDGE_URL: string = "/knowledge";

    /** Build URL for the root-task chat page. */
    export function chatUrl(): string {
      return CHAT_URL;
    }