Ctrl K
ComunidadGuías…

app/types.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import type { Location, LocationDescriptor } from "history";
import type { TFunction } from "i18next";
import type {
  JSONValue,
  CollectionPermission,
  DocumentPermission,
  GroupPermission,
} from "@shared/types";
import type RootStore from "~/stores/RootStore";
import type { SidebarContextType } from "./components/Sidebar/components/SidebarContext";
import type Model from "./models/base/Model";
import type Document from "./models/Document";
import type User from "./models/User";
import type Policy from "./models/Policy";

export type PartialExcept<T, K extends keyof T> = Partial<Omit<T, K>> &
  Required<Pick<T, K>>;

export type MenuItemButton = {
  type: "button";
  title: React.ReactNode;
  onClick: React.MouseEventHandler<HTMLButtonElement | HTMLAnchorElement>;
  dangerous?: boolean;
  visible?: boolean;
  selected?: boolean;
  disabled?: boolean;
  icon?: React.ReactNode;
  tooltip?: React.ReactNode;
  shortcut?: string[];
  badge?: React.ReactNode;
};

export type MenuItemWithChildren = {
  type: "submenu";
  title: React.ReactNode;
  visible?: boolean;
  disabled?: boolean;
  style?: React.CSSProperties;
  hover?: boolean;
  /** Condition to check before preventing the submenu from closing */
  preventCloseCondition?: () => boolean;
  items: MenuItem[];
  icon?: React.ReactNode;
};

export type MenuSeparator = {
  type: "separator";
  visible?: boolean;
};

export type MenuHeading = {
  type: "heading";
  visible?: boolean;
  title: React.ReactNode;
};

export type MenuInternalLink = {
  type: "route";
  title: React.ReactNode;
  to: LocationDescriptor;
  visible?: boolean;
  selected?: boolean;
  disabled?: boolean;
  icon?: React.ReactNode;
  shortcut?: string[];
  badge?: React.ReactNode;
};

export type MenuExternalLink = {
  type: "link";
  title: React.ReactNode;
  href: string | { url: string; target?: string };
  visible?: boolean;
  selected?: boolean;
  disabled?: boolean;
  level?: number;
  icon?: React.ReactNode;
  shortcut?: string[];
  badge?: React.ReactNode;
};

export type MenuGroup = {
  type: "group";
  title: React.ReactNode;
  visible?: boolean;
  icon?: React.ReactNode; // added for backward compatibility
  items: MenuItem[];
};

export type MenuCustomContent = {
  type: "custom";
  visible?: boolean;
  content: React.ReactNode;
};

export type MenuItem =
  | MenuInternalLink
  | MenuItemButton
  | MenuExternalLink
  | MenuItemWithChildren
  | MenuSeparator
  | MenuHeading
  | MenuGroup
  | MenuCustomContent;

export type ActionContext = {
  isMenu: boolean;
  isCommandBar: boolean;
  isButton: boolean;
  /** True when the action runs as a WebMCP tool invoked by an agent. */
  isMCP?: boolean;
  /** Arguments supplied by an agent when the action runs as a WebMCP tool. */
  mcpArgs?: Record<string, unknown>;
  sidebarContext?: SidebarContextType;

  // Legacy (backward compatibility) - returns primary active model's ID
  activeCollectionId?: string | undefined;
  activeDocumentId: string | undefined;

  // New API - work directly with Model instances
  getActiveModels: <T extends Model>(
    modelClass: new (...args: never[]) => T
  ) => T[];
  getActiveModel: <T extends Model>(
    modelClass: new (...args: never[]) => T
  ) => T | undefined;
  getActivePolicies: <T extends Model>(
    modelClass: new (...args: never[]) => T
  ) => Policy[];
  isModelActive: (model: Model) => boolean;
  activeModels: ReadonlySet<Model>;

  currentUserId: string | undefined;
  currentTeamId: string | undefined;
  location: Location;
  stores: RootStore;
  event?: Event;
  t: TFunction;
};

type BaseAction = {
  type: "action";
  id: string;
  analyticsName?: string;
  name: ((context: ActionContext) => React.ReactNode) | React.ReactNode;
  section: ((context: ActionContext) => string) | string;
  description?: ((context: ActionContext) => string) | string;
  shortcut?: ((context: ActionContext) => string[] | undefined) | string[];
  keywords?: string;
  /** Higher number is higher in results, default is 0. */
  priority?: number;
  icon?: ((context: ActionContext) => React.ReactNode) | React.ReactNode;
  iconInContextMenu?: boolean;
  /** Additional content displayed at the end of the item in the command bar. */
  badge?: ((context: ActionContext) => React.ReactNode) | React.ReactNode;
  placeholder?: ((context: ActionContext) => string) | string;
  selected?: ((context: ActionContext) => boolean) | boolean;
  visible?: ((context: ActionContext) => boolean) | boolean;
  disabled?: ((context: ActionContext) => boolean) | boolean;
  /** Configuration for exposing the action as a WebMCP tool. */
  mcp?: {
    /** JSON Schema describing the tool arguments, passed as `mcpArgs`. */
    inputSchema?: Record<string, unknown>;
  };
};

export type Action = BaseAction & {
  variant: "action";
  dangerous?: boolean;
  tooltip?: ((context: ActionContext) => React.ReactNode) | React.ReactNode;
  perform: (context: ActionContext) => unknown;
};

export type InternalLinkAction = BaseAction & {
  variant: "internal_link";
  to: ((context: ActionContext) => LocationDescriptor) | LocationDescriptor;
};

export type ExternalLinkAction = BaseAction & {
  variant: "external_link";
  url: string;
  target?: string;
};

export type ActionWithChildren = BaseAction & {
  variant: "action_with_children";
  children:
    | ((
        context: ActionContext
      ) => (ActionVariant | ActionGroup | ActionSeparator)[])
    | (ActionVariant | ActionGroup | ActionSeparator)[];
};

export type ActionFactory = () => ActionWithChildren;

export type ActionVariant =
  | Action
  | InternalLinkAction
  | ExternalLinkAction
  | ActionWithChildren;

// Specific to menu
export type ActionGroup = {
  type: "action_group";
  name: string;
  actions: (ActionVariant | ActionSeparator)[];
};

// Specific to menu
export type ActionSeparator = {
  type: "action_separator";
};

export type CommandBarAction = {
  id: string;
  name: string;
  section?: string;
  shortcut: string[];
  keywords: string;
  placeholder?: string;
  icon?: React.ReactNode;
  perform?: () => void;
  children?: string[];
  parent?: string;
};

export type FetchOptions = {
  prefetch?: boolean;
  revisionId?: string;
  shareId?: string;
  force?: boolean;
};

export type CollectionSort = {
  field: string;
  direction: "asc" | "desc";
};

// Pagination response in an API call
export type Pagination = {
  limit: number;
  nextPath: string;
  offset: number;
};

// Pagination request params
export type PaginationParams = {
  limit?: number;
  offset?: number;
  sort?: string;
  direction?: "ASC" | "DESC";
};

export type SearchResult = {
  id: string;
  ranking: number;
  context?: string;
  document: Document;
};

export type WebsocketEntityDeletedEvent = {
  modelId: string;
};

export type WebsocketEntitiesEvent = {
  documentIds: { id: string; updatedAt?: string }[];
  collectionIds: { id: string; updatedAt?: string }[];
  groupIds: { id: string; updatedAt?: string }[];
  invalidatedPolicies: string[];
  teamIds: string[];
  event: string;
};

export type WebsocketCollectionUpdateIndexEvent = {
  collectionId: string;
  index: string;
};

export type WebsocketCommentReactionEvent = {
  emoji: string;
  commentId: string;
  user: User;
};

type CursorPosition = {
  type: {
    client: number;
    clock: number;
  };
  tname: string | null;
  item: {
    client: number;
    clock: number;
  };
  assoc: number;
};

type Cursor = {
  anchor: CursorPosition;
  head: CursorPosition;
};

export type AwarenessChangeEvent = {
  states: {
    clientId: number;
    user?: { id: string };
    cursor: Cursor;
    scrollY: number | undefined;
  }[];
};

export const EmptySelectValue = "__empty__";

export type Permission = {
  label: string;
  value:
    | CollectionPermission
    | DocumentPermission
    | GroupPermission
    | typeof EmptySelectValue;
  divider?: boolean;
};

export type Properties<C> = {
  [
    Property in keyof C as C[Property] extends JSONValue ? Property : never
  ]?: C[Property];
};

export enum CommentSortType {
  MostRecent = "mostRecent",
  OrderInDocument = "orderInDocument",
}

export type CommentSortOption =
  | { type: CommentSortType.MostRecent }
  | { type: CommentSortType.OrderInDocument; referencedCommentIds: string[] };