TidGi-Desktop/docs/internal/PromptTreeNavigation.md
lin onetwo 3718d0bd39
Fix/edit agent and several bugs (#670)
* refactor: simplify tool writing

* feat: load prompt from plugin in a wiki, let agent know what to do

based on https://github.com/TiddlyWiki/TiddlyWiki5/issues/9378

* fix: i18n

fix: i18n

fix: wrong i18n structure

fix: empty i18n

* Add ContentLoading component and suspense fallback

* fix: monaco loading

* docs: usage of chrome mcp to contron during dev

* fix: provider config truncate user input when typing

* fix: legacy usage

* Update package.json

* fix: not loadin initial data

* feat: better prompt sort

* fix: sorting of array

* fix: drag

* Create DragAndDrop.md

* feat: directly enter edit mode

* fix: workspace config change cause immediate main wiki restart

* Add 'Press Enter to confirm' to tag help texts

* fix: dont show system tag when adding sub wiki

* feat: inform user to press enter on tag auto complete

* refactor: let sub wiki auto complete tag

* Revert Add 'Press Enter to confirm' to tag help texts

* fix: not able to open prompt editor by click prompt tree

* fix: click to open plugin config

* chore: remove log

* feat: Auto-select the first file if none is selected

* fix: don't preview not enabled prompt parts

* fix: Keep i18n ally think these keys exist, otherwise it will delete them during "check usage"

* lint: fix

* Update externalAPI.logging.test.ts
2025-12-15 17:33:59 +08:00

5.5 KiB

Prompt Tree Navigation to Form Editor

Overview

Click on any prompt item in the tree view and automatically open the corresponding form editor with the correct tab and expanded state.

Implementation Details

1. Source Path Tracking

When plugins inject content into the prompt tree using injectToolList() or injectContent(), the system automatically adds a source field to track the original configuration location.

File: src/services/agentInstance/tools/defineTool.ts

// Build source path pointing to the plugin configuration
const pluginIndex = context.pluginIndex;
const source = pluginIndex !== undefined ? ['plugins', toolConfig.id] : undefined;

const toolPrompt: IPrompt = {
  id: `${toolId}-tool-list-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`,
  text: toolContent,
  caption: options.caption ?? `${definition.displayName} Tools`,
  enabled: true,
  source, // ['plugins', 'plugin-id']
};

The source array format: ['plugins', pluginId] or ['prompts', promptId, ...]

2. Navigation Trigger

File: src/pages/ChatTabContent/components/PromptTree.tsx

When a user clicks a tree node:

const handleNodeClick = useCallback((event: React.MouseEvent) => {
  event.stopPropagation();
  
  // Use source path if available, otherwise construct from fieldPath
  const targetFieldPath = (node.source && node.source.length > 0) 
    ? node.source 
    : [...fieldPath, node.id];
  
  setFormFieldsToScrollTo(targetFieldPath);
}, [node.source, node.id, fieldPath, setFormFieldsToScrollTo]);

3. Tab Switching

File: src/pages/ChatTabContent/components/PromptPreviewDialog/PromptConfigForm/templates/RootObjectFieldTemplate.tsx

The root form template listens to the navigation path and switches to the appropriate tab:

useEffect(() => {
  if (formFieldsToScrollTo.length > 0) {
    const targetTab = formFieldsToScrollTo[0]; // 'prompts', 'plugins', or 'response'
    const tabIndex = properties.findIndex(property => property.name === targetTab);
    if (tabIndex !== -1 && tabIndex !== activeTab) {
      setActiveTab(tabIndex);
    }
  }
}, [formFieldsToScrollTo, properties, activeTab]);

4. Item Expansion

File: src/pages/ChatTabContent/components/PromptPreviewDialog/EditView.tsx

The EditView component handles the expansion of nested items:

useEffect(() => {
  if (formFieldsToScrollTo.length > 0 && editorMode === 'form') {
    const savedPath = [...formFieldsToScrollTo];
    
    // Wait for RootObjectFieldTemplate to switch tabs
    setTimeout(() => {
      setFormFieldsToScrollTo([]); // Clear after tab switches
      
      // Step 1: Expand top-level item
      const topLevelKey = savedPath[0];
      const firstItemId = savedPath[1];
      expandItemsByPath(topLevelKey, [firstItemId]);
      
      // Step 2: Expand nested children if present
      if (savedPath.length > 2) {
        setTimeout(() => {
          const parentIndex = findParentIndex(topLevelKey, firstItemId);
          const nestedFieldPath = `${topLevelKey}_${parentIndex}_children`;
          const nestedItemIds = savedPath.slice(2);
          expandItemsByPath(nestedFieldPath, nestedItemIds);
        }, 300);
      }
    }, 100);
  }
}, [formFieldsToScrollTo, editorMode, agentFrameworkConfig]);

5. Store Management

File: src/pages/Agent/store/agentChatStore/PromptPreviewStore.ts

The store maintains:

  • Expansion state for all array fields
  • Navigation path queue
  • Helper functions to expand/collapse items
interface PromptPreviewStore {
  arrayExpansionStore: Record<string, Record<number, boolean>>;
  formFieldsToScrollTo: string[];
  
  setItemExpanded: (fieldPath: string, index: number, expanded: boolean) => void;
  setFormFieldsToScrollTo: (path: string[]) => void;
}

Data Flow

  1. User clicks "Git Tools" in tree view
  2. PromptTree reads node.source['plugins', 'g3f4e5d6-7e8f-9g0h-1i2j-l3m4n5o6p7q8']
  3. Calls setFormFieldsToScrollTo(['plugins', 'g3f4e5d6-7e8f-9g0h-1i2j-l3m4n5o6p7q8'])
  4. RootObjectFieldTemplate detects path[0] = 'plugins' → switches to plugins tab
  5. EditView expands the plugin item with id 'g3f4e5d6-7e8f-9g0h-1i2j-l3m4n5o6p7q8'
  6. Form scrolls to and highlights the target element

Timing Considerations

The implementation uses careful timing to ensure proper rendering:

  • 100ms delay: Wait for tab switch before expanding items
  • 300ms delay: Wait for parent expansion before expanding nested children
  • 500ms delay: Wait for all expansions before scrolling to target

These delays account for React's rendering cycles and DOM updates.

Extension Points

To add navigation support for custom components:

  1. Add source field when creating prompts
  2. Ensure source points to the configuration location: ['topLevelKey', 'itemId', ...]
  3. The navigation system will automatically handle the rest
  • src/services/agentInstance/tools/defineTool.ts - Source path injection
  • src/services/agentInstance/tools/types.ts - Context types with pluginIndex
  • src/services/agentInstance/promptConcat/promptConcat.ts - Plugin index passing
  • src/pages/ChatTabContent/components/PromptTree.tsx - Click handler
  • src/pages/ChatTabContent/components/PromptPreviewDialog/EditView.tsx - Expansion logic
  • src/pages/ChatTabContent/components/PromptPreviewDialog/PromptConfigForm/templates/RootObjectFieldTemplate.tsx - Tab switching
  • src/pages/Agent/store/agentChatStore/PromptPreviewStore.ts - State management