Flow - New Prompt¶
-
Application Entry Point
main.dart
main() → runApp(MyApp()) -
Home Screen Initialization
HomeScreen (StatefulWidget)
Location: lib/screens/home_screen.dart
State Initialization (HomeScreenState.initState())- Initialize PromptArgs (empty state)
- Create BackendService instance
- Create PointManager(backendService)
- Create TreeViewManager
- Create ShardManager(pointManager, backendService)
- Create ThreadManager(pointManager, shardManager, backendService, threadMap, onMapUpdated)
- Create ContextManager(threadManager, apiService, threadMap)
- Create HomeScreenManager(managers..., callbacks...)
- Initialize UI state variables:
- threadMap: Map<String, Point>
- treeViewList: List
- loadingExchanges: Set
- isLoading: bool
- errorMessage: String?
-
User Input Flow
PromptInputField (StatefulWidget)
Location: lib/ui/widgets/prompt_input_field.dart
User Types Prompt
TextField(controller: _textController)
↓
User presses Send button or Enter
↓
_handleSendRequest()
↓
widget.onPrompt(text) → calls HomeScreen.onPrompt() -
Prompt Processing Flow
HomeScreen.onPrompt(String promptText)
Async method that orchestrates the entire prompt handling- Clear error messages (setState)
- Determine if this is a sub-prompt or regular prompt
- Create/update PromptArgs:
- For regular prompt: use last Point in treeViewList as parent
- For sub-prompt: use existing promptArgs with shard info
- Call HomeScreenManager.handlePrompt(...)
- Clear promptArgs (setState)
- Handle errors with setState and error display
-
Home Screen Manager Processing
HomeScreenManager.handlePrompt(...)
Location: lib/managers/home_screen_manager.dart- Validate prompt text (not empty)
- onLoadingStateChanged(true) → updates UI loading state
- ThreadManager.updateThreadMap(promptText, promptArgs)
→ Returns: Point newPoint - Create AiModelProperties (model config)
- ContextManager.createPromptContext(newPoint)
→ Returns: List promptContextMessages - Track loading state for this exchange:
- Extract exchangeId from newPoint.exchangesList.last
- Add to loadingExchanges set
- updateLoadingExchanges callback → updates HomeScreen state
- ApiService.sendPromptToAi(pointId, promptContextMessages, aiModelProperties)
→ Returns: Point updatedNewPoint (with AI response) - Remove exchangeId from loadingExchanges
- Update threadMap[updatedNewPoint.id] = updatedNewPoint
- onMapUpdated() callback
- populateTreeViewList(treeViewList, updateTreeViewList)
- onClearPromptInput() → clears input field
- onLoadingStateChanged(false)
-
Thread Map Update (Core Logic)
ThreadManager.updateThreadMap(...)
Location: lib/managers/thread_manager.dart-
PointManager.createNewPoint(promptArgs, promptText)
→ Returns: Point newPoint (with prompt, no response yet) -
IF newPoint.id == newPoint.parentPointId:
→ Root point: Add to threadMap and return -
ELSE: Get parent point
- IF NOT in threadMap:
→ PointManager.getParentPoint(fetchArgs, newPoint.id)
→ Cache parent in threadMap - ELSE:
→ Retrieve parent from threadMap
- Decision Branch:
A. IF promptArgs.isShardChild AND selectedText exists:
SHARDING FLOW (Sub-prompt)
↓
ShardManager.addShardToParentPoint(newPoint.id, parentPoint, promptArgs)
→ Returns: Point updatedParentPoint (with new Shard added)
↓
Extract parentShardId from updatedParentPoint.shardsList
↓
newPoint.copyWith(parentShardId: parentShardId)
↓
Update threadMap with both points
↓
onMapUpdated() callbackB. ELSE:
REGULAR FLOW (Continue conversation)
↓
PointManager.updateParentPoint(newPoint.id, parentPoint)
→ Returns: Point updatedParentPoint (with newPoint.id in pointChildren)
↓
Update threadMap with both points
↓
onMapUpdated() callback- Return newPoint (or updatedNewPoint)
-
-
Tree View Update
HomeScreenManager.populateTreeViewList(...)
TreeViewManager.updateTreeViewList(threadMap, treeViewList)
→ Returns: List newTreeViewList
↓
updateTreeViewList callback → updates HomeScreen.treeViewList
↓
setState() triggers UI rebuild -
Tree Rendering Flow
TreeSliverThread (StatefulWidget)
Location: lib/ui/widgets/tree_sliver_thread.dart
State Update (_TreeSliverThreadState)
didUpdateWidget(TreeSliverThread oldWidget)
↓
IF treeViewList or threadMap changed:
↓
_updateTreeList()
↓
TreeSliverManager.buildFlatTreeList(threadMap, treeViewList)
→ Returns: List _flatTreeList
↓
setState() → triggers rebuild -
Tree Structure Building
TreeSliverManager.buildFlatTreeList(...)
Location: lib/managers/tree_sliver_manager.dart
This is the core algorithm that creates the tree structure shown in the UI¶
1. _buildTreeStructure(treeViewList, threadMap)
→ Returns: List<TreeNodeModel> treeNodes
Step A: Create all nodes (First Pass)
─────────────────────────────────────
FOR EACH Point in treeViewList:
- Get first Exchange
- Check if Point has valid shards:
IF has valid shards:
→ _createShardPointNode(point, exchange, nodeMap, threadMap)
Creates:
├─ Main prompt node (no response)
├─ Shard segment nodes (response parts) [stored in nodeMap]
└─ "After" segment if text remains
ELSE:
→ Create regular TreeNodeModel
Contains both prompt and full response
Step B: Build hierarchy (Second Pass)
──────────────────────────────────────
FOR EACH Point in treeViewList:
IF Point.parentPointId == Point.id:
→ Add as root node
→ IF has shards: Add shard segments as siblings
ELSE:
→ Find parent (could be a shard segment)
→ Add as child to parent
→ Update level (parent.level + 1)
→ IF this child has shards: Add child's shard segments as siblings
Step C: Update hasChildren flags
──────────────────────────────────
2. _flattenNode(node, flatList) [Recursive]
→ Flattens tree based on expansion states
→ Returns: List<TreeNodeModel> flatList
Shard Point Node Creation (_createShardPointNode)¶
1. Create main prompt node (no response content)
2. Filter valid shards (validate anchor positions)
3. Sort shards by start position
4. FOR EACH shard:
- Extract response segment (currentPosition to shard.endPosition)
- Create TreeNodeModel for segment (NodeType.shardResponse)
- Store in nodeMap (NOT added as child of main node)
- FOR EACH child in shard.shardChildren:
→ _buildChildNode(childPoint, shardSegment.id, shard.shardId, level=1, ...)
→ Add child as child of shard segment
→ IF child has shards: Add child's shard segments as siblings
- Update currentPosition
5. Create "after" segment if text remains
6. Return main prompt node
- Tree Item Rendering
TreeSliverThread.build(...)
SliverList.builder(
itemCount: _flatTreeList.length
itemBuilder: (context, index)
↓
Get TreeNodeModel node from _flatTreeList[index]
↓
Check if node.exchangeId is in loadingExchanges
↓
Return TreeSliverItem(node, isLoading, callbacks...)
) - Individual Tree Item Display
TreeSliverItem (StatelessWidget)
Location: lib/ui/widgets/tree_sliver_item.dart
Build Method Structure¶
Container (with left margin based on node.level)
↓
Card
↓
InkWell (onTap: collapses/expands, selects node)
↓
Padding
↓
Column:
├─ IF hasChildren: _buildExpansionHeader()
│ → Shows "Collapse" / "Expand" with icon
│
├─ IF promptContent != null: _buildPromptSection()
│ → Shows prompt with blue background
│ → Icon: person icon
│ → Label: "You" or "Shard"
│
└─ IF responseContent != null:
IF isLoading:
→ _buildStatusSection(StatusSectionConfig.loading())
Shows "Waiting for response..." with spinner
ELSE:
→ _buildResponseSection()
Shows response with color based on type:
- Green for AI Assistant
- Yellow for Shard Segments
Content Section Rendering (_buildContentSection)¶
Container (colored background)
↓
Column:
├─ Row (icon + label)
└─ _buildSelectableText(content)
↓
SelectableText with custom context menu
↓
IF text selected:
→ Custom menu includes "Sub-prompt" option
→ _handleSubPrompt(selectedText, selection)
├─ Copy text to clipboard
├─ Create PromptArgs with shard info
├─ Call prepareSubPromptInput callback
└─ Updates HomeScreen state
- Sub-Prompt Preparation
HomeScreen.prepareSubPromptInput(PromptArgs promptArgs)- Validate selectedText
- setState:
- Clear errorMessage
- Update this.promptArgs with new values:
- currentPointId
- isShardChild = true
- selectedText, startPosition, endPosition
- Show SnackBar: "Text copied to input..."
- Post frame callback:
- PromptInputFieldKey.currentState.setText(selectedText)
- PromptInputFieldKey.currentState.focusInput()
- Node Selection
HomeScreen.onNodeSelected(String currentPointId)- setState:
- Update promptArgs with selected node's pointId
- Set isShardChild = false (continuing from this point)
- Focus input field for user to type next prompt
- Data Models
Point Model (lib/models/point.dart)¶
Point:
- id: String
- parentPointId: String
- pointChildren: List<String>
- parentShardId: String?
- shardsList: List<Shard>
- exchangesList: List<Exchange>
- metadata: Metadata?
Shard:
- shardId: String
- shardChildren: List<String>
- anchor: Anchor (startPosition, endPosition, selectedText)
Exchange:
- exchangeId: String
- prompt: Prompt (model, promptMessage, maxTokens)
- response: Response? (with choices containing message content)
TreeNodeModel (lib/models/tree_node_model.dart)¶
TreeNodeModel:
- id: String (unique identifier for display)
- pointId: String (actual Point.id)
- parentId: String?
- parentShardId: String?
- exchangeId: String?
- shardId: String?
- level: int (indentation level)
- isExpanded: bool
- hasChildren: bool
- promptContent: String?
- responseContent: String?
- promptRole: String?
- responseRole: String?
- nodeType: NodeType (exchange, shard, shardResponse)
- children: List<TreeNodeModel>
- shardStartPosition: int?
- shardEndPosition: int?
- choiceIndex: int?
- Complete Flow Summary for Screenshot Example
Scenario: "What's Earth?" conversation-
User types "What's Earth?" → HomeScreen.onPrompt()
↓ -
HomeScreenManager.handlePrompt()
↓ -
ThreadManager.updateThreadMap() → Creates Point T001
↓ -
ApiService.sendPromptToAi() → Returns response "Third planet from the Sun"
↓ -
TreeViewManager.updateTreeViewList() → [T001]
↓ -
TreeSliverManager.buildFlatTreeList()
→ Creates TreeNodeModel for T001 (prompt + response)
↓ -
TreeSliverThread renders TreeSliverItem
→ Shows blue "You" box: "What's Earth?"
→ Shows green "AI Assistant" box: "Third planet from the Sun" -
User selects "Sun" text → Right-click → "Sub-prompt"
↓ -
HomeScreen.prepareSubPromptInput()
→ Sets promptArgs with shard info
→ Fills input field with "Sun - what's this?"
↓ -
User types sub-prompt → HomeScreen.onPrompt()
↓ -
ThreadManager.updateThreadMap() with isShardChild=true
↓ -
ShardManager.addShardToParentPoint()
→ Creates Shard S01 in Point T001
→ Creates new Point T002 (parentShardId = S01)
↓ -
TreeSliverManager rebuilds tree:
Point T001 now split into:
├─ Prompt: "What's Earth?"
├─ Response segment: "Third planet from the "
├─ Shard segment (with children):
│ ├─ "Sun"
│ └─ Child: T002
│ ├─ Prompt: "Sun - what's this?"
│ └─ Response: "The star" + "A luminous sphere" + "of plasma..."
└─ After segment: " [remaining text]" -
Each subsequent sub-prompt repeats steps 8-13,
creating nested tree structure as shown in screenshot
-
Updated by Tomislav Pleše about 2 months ago · 3 revisions