|
6 | 6 | import type { ReactNode } from "react";
|
7 | 7 |
|
8 | 8 | import { IconName, IconRotation } from "@cocalc/frontend/components/icon";
|
| 9 | +import { StudentProjectFunctionality } from "@cocalc/util/db-schema/projects"; |
9 | 10 | import { IntlMessage } from "@cocalc/frontend/i18n";
|
10 | 11 | import type { ManageCommands } from "./manage";
|
11 | 12 | import { MENUS } from "./menus";
|
12 | 13 |
|
| 14 | +// Specification for a single menu in the frame title bar. |
13 | 15 | interface MenuSpec {
|
| 16 | + // Display label for the menu. Can be translated text or special APPLICATION_MENU constant |
14 | 17 | label: IntlMessage | string;
|
| 18 | + |
| 19 | + // Position for menu ordering. Lower numbers appear first |
15 | 20 | pos: number;
|
| 21 | + |
| 22 | + // Array of group names that belong to this menu. Groups contain the actual commands. |
16 | 23 | groups: string[];
|
17 | 24 | }
|
18 | 25 |
|
| 26 | +/** |
| 27 | + * Collection of all menu specifications. Menus are registered via addMenus() |
| 28 | + * in generic-menus.ts and define the top-level dropdown structure in frame title bars. |
| 29 | + * Examples: "file", "edit", "view", "go", "help", "app" |
| 30 | + */ |
19 | 31 | export interface Menus {
|
20 | 32 | [name: string]: MenuSpec;
|
21 | 33 | }
|
22 | 34 |
|
23 | 35 | export type Group = (typeof MENUS)[keyof typeof MENUS]["groups"][number];
|
24 | 36 |
|
| 37 | +/** |
| 38 | + * Function signature for command click handlers. Receives the ManageCommands |
| 39 | + * instance (providing access to frame context, props, etc.) and an optional |
| 40 | + * event object from the UI interaction. |
| 41 | + */ |
25 | 42 | export type OnClick = (opts: ManageCommands & { event? }) => void;
|
26 | 43 |
|
| 44 | +/** |
| 45 | + * Configuration options for Ant Design Popconfirm modal dialogs. |
| 46 | + * Used to show confirmation prompts before executing potentially destructive commands. |
| 47 | + * All fields support internationalization via IntlMessage. |
| 48 | + */ |
27 | 49 | interface PopconfirmOpts {
|
| 50 | + // Main title text shown at the top of the confirmation dialog |
28 | 51 | title?: string | IntlMessage | React.JSX.Element;
|
| 52 | + // Detailed description explaining the action and its consequences |
29 | 53 | description?: string | IntlMessage | React.JSX.Element;
|
| 54 | + // Text for the confirmation button (defaults to "OK") |
30 | 55 | okText?: string | IntlMessage;
|
| 56 | + // Text for the cancel button (defaults to "Cancel") |
31 | 57 | cancelText?: string | IntlMessage;
|
32 | 58 | }
|
33 | 59 |
|
| 60 | +/** |
| 61 | + * Defines a command that can appear in frame editor menus, toolbars, and buttons. |
| 62 | + * For example, split frame, zoom, save, etc. |
| 63 | + * |
| 64 | + * Commands are organized into menus via groups. |
| 65 | + * Each editor (LaTeX, code, etc.) can include specific commands in their EditorDescription.commands. |
| 66 | + * |
| 67 | + * The ManageCommands class handles visibility, rendering, and execution of commands |
| 68 | + * based on the current frame context, user permissions, and editor specifications. |
| 69 | + */ |
34 | 70 | export interface Command {
|
35 |
| - // group -- inside of a menu |
| 71 | + /** |
| 72 | + * The menu group this command belongs to. |
| 73 | + * Commands are organized into groups for logical menu structure. |
| 74 | + */ |
36 | 75 | group: Group;
|
37 |
| - name?: string; //not used |
38 |
| - // position, for sorting |
| 76 | + |
| 77 | + /** |
| 78 | + * Used in menu construction helpers (e.g., addEditorMenus) to identify |
| 79 | + * commands within the menu system. Not used in the final Command objects |
| 80 | + * that get registered, which are identified by their key in the commands object. |
| 81 | + */ |
| 82 | + name?: string; |
| 83 | + |
| 84 | + /** |
| 85 | + * Position within the group for sorting. Lower numbers appear first. |
| 86 | + * Used by ManageCommands.getAllCommandPositions() to determine display order |
| 87 | + * in menus and toolbars. Defaults to 1e6 (very high) if not specified. |
| 88 | + */ |
39 | 89 | pos?: number;
|
| 90 | + |
| 91 | + // Tooltip text shown when hovering over the command |
40 | 92 | title?: ReactNode | ((opts: ManageCommands) => ReactNode) | IntlMessage;
|
| 93 | + |
| 94 | + // Icon to display for this command. |
41 | 95 | icon?: IconName | ReactNode | ((opts: ManageCommands) => ReactNode);
|
| 96 | + |
| 97 | + // Rotation angle for the icon in degrees. Only applies when icon is an IconName. |
42 | 98 | iconRotate?: IconRotation;
|
| 99 | + |
| 100 | + // Label for button bar buttons. Separate from 'label' to allow different text in menus vs. compact button bar. |
43 | 101 | button?: ReactNode | ((opts: ManageCommands) => ReactNode) | IntlMessage;
|
| 102 | + |
| 103 | + // unclear? |
44 | 104 | //color?: string | ((opts: ManageCommands) => string);
|
| 105 | + |
| 106 | + // Primary label for the command in menus. |
45 | 107 | label?: ReactNode | ((opts: ManageCommands) => ReactNode) | IntlMessage;
|
46 |
| - // If onClick is NOT set, then editor_actions[name] must be defined |
47 |
| - // and be a function that takes the frame id as input. |
| 108 | + |
| 109 | + /** |
| 110 | + * Click handler for the command. If not provided, the system falls back to |
| 111 | + * calling props.actions[name](props.id) where 'name' is the command key. |
| 112 | + * Receives ManageCommands context plus an optional 'event' parameter. |
| 113 | + */ |
48 | 114 | onClick?: OnClick;
|
49 |
| - // isVisible: if a function, determine visibility based on that. |
50 |
| - // if a string, use editor spec for given frame. |
| 115 | + |
| 116 | + /** |
| 117 | + * Controls command visibility. |
| 118 | + * Used to conditionally show commands based on frame type, file type, etc. |
| 119 | + * Can be a string (references another command name in the editor spec) or a function |
| 120 | + * NOTE: Completely ignored if alwaysShow is true |
| 121 | + */ |
51 | 122 | isVisible?: string | (({ props }) => boolean);
|
52 |
| - disable?: string; |
| 123 | + |
| 124 | + /** |
| 125 | + * Disables the command when the specified student project functionality |
| 126 | + * is disabled. References keys in studentProjectFunctionality object. |
| 127 | + * Used in educational contexts to restrict certain features. |
| 128 | + */ |
| 129 | + disable?: keyof StudentProjectFunctionality; |
| 130 | + |
| 131 | + /** |
| 132 | + * Keyboard shortcut display. Shown in menu items on desktop (not mobile). |
| 133 | + * Should match actual keyboard shortcuts defined elsewhere in the app. |
| 134 | + */ |
53 | 135 | keyboard?: ReactNode;
|
| 136 | + |
| 137 | + // Sub-commands that appear as a submenu. |
54 | 138 | children?:
|
55 | 139 | | Partial<Command>[]
|
56 | 140 | | ((opts: ManageCommands) => Partial<Command>[]);
|
| 141 | + |
| 142 | + // if this returns true, the command should be disabled (grayed out) but still visible |
57 | 143 | disabled?: (opts: ManageCommands) => boolean;
|
58 |
| - // not used yet |
| 144 | + |
| 145 | + // for interactive tours -- not used yet |
59 | 146 | tour?: string;
|
| 147 | + |
60 | 148 | // do modal popconfirm first -- takes options to antd
|
61 | 149 | // Popconfirm, or a function that returns Popconfirm options.
|
62 | 150 | // See frontend/app/popconfirm-modal.tsx for subtleties.
|
63 | 151 | popconfirm?:
|
64 | 152 | | PopconfirmOpts
|
65 | 153 | | ((opts: ManageCommands) => PopconfirmOpts | undefined);
|
66 |
| - // if true, never show this on mobile |
| 154 | + |
| 155 | + /** |
| 156 | + * If true, this command is never shown on mobile devices. |
| 157 | + * Used for commands that don't work well on touch interfaces or |
| 158 | + * are not needed in mobile contexts. |
| 159 | + */ |
67 | 160 | neverVisibleOnMobile?: boolean;
|
68 |
| - // if true, always show this (unless neverVisibleOnMobile set, obviously). |
| 161 | + |
| 162 | + /** |
| 163 | + * If true, forces the command to always be visible, overriding all other |
| 164 | + * visibility checks including isVisible functions and editor spec requirements. |
| 165 | + * Used for essential commands like frame controls that should always be available. |
| 166 | + * Takes precedence over neverVisibleOnMobile. |
| 167 | + */ |
69 | 168 | alwaysShow?: boolean;
|
| 169 | + |
| 170 | + /** |
| 171 | + * If true, keeps dropdown menus open after clicking this command. |
| 172 | + * Used for commands that don't navigate away or change context, |
| 173 | + * allowing multiple selections (e.g., zoom level changes). |
| 174 | + */ |
70 | 175 | stayOpenOnClick?: boolean;
|
| 176 | + |
| 177 | + /** |
| 178 | + * Additional search terms for the command search functionality. |
| 179 | + * Used by the search_commands feature to find commands beyond their |
| 180 | + * title, label, and name. Improves discoverability. |
| 181 | + */ |
71 | 182 | search?: string;
|
72 | 183 | }
|
0 commit comments