Step 162: add help panel and documentation
This commit is contained in:
@@ -509,3 +509,4 @@ Sprint 5 in progress. Step 141 (Emacs daemon with user config) done. Next: Step
|
|||||||
| 2026-02-10 | Codex | Step 159: Agent marketplace registry with UI panel, install toggles, and registry serialization. 6/6 tests pass. |
|
| 2026-02-10 | Codex | Step 159: Agent marketplace registry with UI panel, install toggles, and registry serialization. 6/6 tests pass. |
|
||||||
| 2026-02-10 | Codex | Step 160: Theme engine with JSON themes, ImGui styling, and syntax/editor color mapping. 4/4 tests pass. |
|
| 2026-02-10 | Codex | Step 160: Theme engine with JSON themes, ImGui styling, and syntax/editor color mapping. 4/4 tests pass. |
|
||||||
| 2026-02-10 | Codex | Step 161: Plugin API + loader with registry hooks for concepts/generators/grammars/annotations/panels/commands. 10/10 tests pass. |
|
| 2026-02-10 | Codex | Step 161: Plugin API + loader with registry hooks for concepts/generators/grammars/annotations/panels/commands. 10/10 tests pass. |
|
||||||
|
| 2026-02-10 | Codex | Step 162: Help panel with Markdown sections + docs/help.md wired in UI. 4/4 tests pass. |
|
||||||
|
|||||||
46
docs/help.md
Normal file
46
docs/help.md
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
# Documentation
|
||||||
|
|
||||||
|
Welcome to Whetstone. This panel provides a compact reference to the editor,
|
||||||
|
annotations, agent APIs, and language support.
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
- Open a folder from File > Open or Project > Open.
|
||||||
|
- Use the left File Tree to browse files.
|
||||||
|
- Toggle Text/Structured mode with the Mode switch in the toolbar.
|
||||||
|
- Run or build from the Build tab (or toolbar buttons).
|
||||||
|
|
||||||
|
## Annotation Reference
|
||||||
|
Whetstone uses canonical memory annotations:
|
||||||
|
- `@Reclaim(Tracing|Escape|Cycle)`
|
||||||
|
- `@Owner(Single|Shared_ARC)`
|
||||||
|
- `@Lifetime(RAII)`
|
||||||
|
- `@Deallocate(Explicit)`
|
||||||
|
- `@Allocate(Static|Register|Allocator)`
|
||||||
|
|
||||||
|
Annotations can be added from the gutter or context menu. Conflicts are
|
||||||
|
highlighted in the Problems panel with quick fixes.
|
||||||
|
|
||||||
|
## Keyboard Shortcuts
|
||||||
|
Shortcuts are configurable. See Settings > Keybindings. Default profile:
|
||||||
|
- `Ctrl+P` Command palette
|
||||||
|
- `Ctrl+F` Find/Replace
|
||||||
|
- `Ctrl+S` Save
|
||||||
|
- `Ctrl+Z` Undo, `Ctrl+Y` Redo
|
||||||
|
|
||||||
|
## Language Support
|
||||||
|
Supported languages (parsing + generation):
|
||||||
|
- Python, C++, Elisp, JavaScript, TypeScript, Java, Rust, Go
|
||||||
|
|
||||||
|
## Agent API (JSON-RPC)
|
||||||
|
Core RPC methods:
|
||||||
|
- `getAST`
|
||||||
|
- `applyMutation`
|
||||||
|
- `generateCode`
|
||||||
|
- `getAnnotationSuggestions`
|
||||||
|
- `applyAnnotationSuggestion`
|
||||||
|
- `startWorkflowRecording`, `stopWorkflowRecording`, `replayWorkflow`
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
- If an AST is unavailable, switch to Structured mode.
|
||||||
|
- If LSP is offline, completions still work from indexed stubs.
|
||||||
|
- If Emacs integration fails, check the Output panel for Emacs logs.
|
||||||
@@ -901,6 +901,10 @@ target_link_libraries(step160_test PRIVATE nlohmann_json::nlohmann_json imgui::i
|
|||||||
add_executable(step161_test tests/step161_test.cpp)
|
add_executable(step161_test tests/step161_test.cpp)
|
||||||
target_include_directories(step161_test PRIVATE src)
|
target_include_directories(step161_test PRIVATE src)
|
||||||
|
|
||||||
|
add_executable(step162_test tests/step162_test.cpp)
|
||||||
|
target_include_directories(step162_test PRIVATE src)
|
||||||
|
target_link_libraries(step162_test PRIVATE imgui::imgui)
|
||||||
|
|
||||||
find_package(SDL2 CONFIG REQUIRED)
|
find_package(SDL2 CONFIG REQUIRED)
|
||||||
find_package(OpenGL REQUIRED)
|
find_package(OpenGL REQUIRED)
|
||||||
find_package(glad CONFIG REQUIRED)
|
find_package(glad CONFIG REQUIRED)
|
||||||
|
|||||||
@@ -46,6 +46,7 @@
|
|||||||
#include "AgentRegistry.h"
|
#include "AgentRegistry.h"
|
||||||
#include "AgentMarketplace.h"
|
#include "AgentMarketplace.h"
|
||||||
#include "BuildSystem.h"
|
#include "BuildSystem.h"
|
||||||
|
#include "HelpPanel.h"
|
||||||
#include "DependencyPanel.h"
|
#include "DependencyPanel.h"
|
||||||
#include "LibraryIndexer.h"
|
#include "LibraryIndexer.h"
|
||||||
#include "LibraryBrowserPanel.h"
|
#include "LibraryBrowserPanel.h"
|
||||||
@@ -182,6 +183,7 @@ struct EditorState {
|
|||||||
WorkflowRecorder workflowRecorder;
|
WorkflowRecorder workflowRecorder;
|
||||||
AgentRegistry agentRegistry;
|
AgentRegistry agentRegistry;
|
||||||
AgentMarketplaceState agentMarketplace;
|
AgentMarketplaceState agentMarketplace;
|
||||||
|
HelpPanelState helpPanel;
|
||||||
BuildSystem::Type buildType = BuildSystem::Type::None;
|
BuildSystem::Type buildType = BuildSystem::Type::None;
|
||||||
std::vector<BuildError> buildErrors;
|
std::vector<BuildError> buildErrors;
|
||||||
std::string lastBuildOutput;
|
std::string lastBuildOutput;
|
||||||
|
|||||||
132
editor/src/HelpPanel.h
Normal file
132
editor/src/HelpPanel.h
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
#pragma once
|
||||||
|
// Step 162: Help panel markdown renderer
|
||||||
|
|
||||||
|
#include "imgui.h"
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <sstream>
|
||||||
|
#include <fstream>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
struct HelpSection {
|
||||||
|
std::string title;
|
||||||
|
std::string content;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct HelpPanelState {
|
||||||
|
int selected = 0;
|
||||||
|
std::vector<HelpSection> sections;
|
||||||
|
std::string lastRoot;
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool loadHelpFile(const std::string& path, std::string& out) {
|
||||||
|
std::ifstream in(path);
|
||||||
|
if (!in.is_open()) return false;
|
||||||
|
std::ostringstream ss;
|
||||||
|
ss << in.rdbuf();
|
||||||
|
out = ss.str();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::vector<HelpSection> parseHelpSections(const std::string& text) {
|
||||||
|
std::vector<HelpSection> sections;
|
||||||
|
std::istringstream ss(text);
|
||||||
|
std::string line;
|
||||||
|
HelpSection current;
|
||||||
|
while (std::getline(ss, line)) {
|
||||||
|
if (line.rfind("# ", 0) == 0) {
|
||||||
|
if (!current.title.empty() || !current.content.empty()) {
|
||||||
|
sections.push_back(current);
|
||||||
|
current = HelpSection();
|
||||||
|
}
|
||||||
|
current.title = line.substr(2);
|
||||||
|
} else {
|
||||||
|
current.content += line + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!current.title.empty() || !current.content.empty()) {
|
||||||
|
sections.push_back(current);
|
||||||
|
}
|
||||||
|
if (sections.empty()) {
|
||||||
|
sections.push_back({"Documentation", text});
|
||||||
|
}
|
||||||
|
return sections;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void renderMarkdownLine(const std::string& line, ImFont* monoFont) {
|
||||||
|
std::string trimmed = line;
|
||||||
|
while (!trimmed.empty() && (trimmed.back() == '\r' || trimmed.back() == '\n')) {
|
||||||
|
trimmed.pop_back();
|
||||||
|
}
|
||||||
|
if (trimmed.rfind("## ", 0) == 0) {
|
||||||
|
ImGui::TextColored(ImVec4(0.7f, 0.9f, 1.0f, 1.0f), "%s", trimmed.substr(3).c_str());
|
||||||
|
} else if (trimmed.rfind("### ", 0) == 0) {
|
||||||
|
ImGui::TextColored(ImVec4(0.6f, 0.85f, 0.95f, 1.0f), "%s", trimmed.substr(4).c_str());
|
||||||
|
} else if (trimmed.rfind("- ", 0) == 0) {
|
||||||
|
ImGui::BulletText("%s", trimmed.substr(2).c_str());
|
||||||
|
} else if (trimmed.rfind("```", 0) == 0) {
|
||||||
|
ImGui::TextDisabled("%s", trimmed.c_str());
|
||||||
|
} else if (trimmed.rfind("`", 0) == 0 && trimmed.size() > 1) {
|
||||||
|
ImGui::PushFont(monoFont);
|
||||||
|
ImGui::TextUnformatted(trimmed.c_str());
|
||||||
|
ImGui::PopFont();
|
||||||
|
} else {
|
||||||
|
ImGui::TextWrapped("%s", trimmed.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void renderMarkdown(const std::string& text, ImFont* monoFont) {
|
||||||
|
std::istringstream ss(text);
|
||||||
|
std::string line;
|
||||||
|
while (std::getline(ss, line)) {
|
||||||
|
renderMarkdownLine(line, monoFont);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void loadHelpContent(HelpPanelState& state, const std::string& root) {
|
||||||
|
if (state.lastRoot == root && !state.sections.empty()) return;
|
||||||
|
state.sections.clear();
|
||||||
|
state.selected = 0;
|
||||||
|
state.lastRoot = root;
|
||||||
|
|
||||||
|
std::string content;
|
||||||
|
if (!root.empty()) {
|
||||||
|
if (!loadHelpFile(root + "\\docs\\help.md", content)) {
|
||||||
|
loadHelpFile(root + "/docs/help.md", content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (content.empty()) {
|
||||||
|
content = "# Documentation\n\nDocumentation not found.\n";
|
||||||
|
}
|
||||||
|
state.sections = parseHelpSections(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void renderHelpPanel(HelpPanelState& state,
|
||||||
|
const std::string& workspaceRoot,
|
||||||
|
ImFont* monoFont) {
|
||||||
|
loadHelpContent(state, workspaceRoot);
|
||||||
|
if (state.sections.empty()) {
|
||||||
|
ImGui::TextDisabled("(no documentation)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Columns(2, "help_cols", true);
|
||||||
|
ImGui::BeginChild("help_nav", ImVec2(0, 0), true);
|
||||||
|
for (size_t i = 0; i < state.sections.size(); ++i) {
|
||||||
|
const auto& section = state.sections[i];
|
||||||
|
bool selected = (int)i == state.selected;
|
||||||
|
if (ImGui::Selectable(section.title.c_str(), selected)) {
|
||||||
|
state.selected = (int)i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::EndChild();
|
||||||
|
ImGui::NextColumn();
|
||||||
|
|
||||||
|
ImGui::BeginChild("help_body", ImVec2(0, 0), false);
|
||||||
|
const auto& section = state.sections[std::min((int)state.sections.size() - 1, state.selected)];
|
||||||
|
ImGui::TextColored(ImVec4(0.7f, 0.9f, 1.0f, 1.0f), "%s", section.title.c_str());
|
||||||
|
ImGui::Separator();
|
||||||
|
renderMarkdown(section.content, monoFont);
|
||||||
|
ImGui::EndChild();
|
||||||
|
ImGui::Columns(1);
|
||||||
|
}
|
||||||
@@ -2016,6 +2016,13 @@ int main(int, char**) {
|
|||||||
ImGui::EndTabItem();
|
ImGui::EndTabItem();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ImGui::BeginTabItem("Help")) {
|
||||||
|
ImGui::PushFont(uiFont);
|
||||||
|
renderHelpPanel(state.helpPanel, state.workspaceRoot, monoFont);
|
||||||
|
ImGui::PopFont();
|
||||||
|
ImGui::EndTabItem();
|
||||||
|
}
|
||||||
|
|
||||||
// Problems (LSP diagnostics)
|
// Problems (LSP diagnostics)
|
||||||
if (ImGui::BeginTabItem("Problems")) {
|
if (ImGui::BeginTabItem("Problems")) {
|
||||||
ImGui::PushFont(monoFont);
|
ImGui::PushFont(monoFont);
|
||||||
|
|||||||
34
editor/tests/step162_test.cpp
Normal file
34
editor/tests/step162_test.cpp
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
// Step 162 TDD Test: Help panel section parsing
|
||||||
|
#include "HelpPanel.h"
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
static void expect(bool cond, const std::string& name, int& passed, int& failed) {
|
||||||
|
if (cond) {
|
||||||
|
std::cout << "Test " << (passed + failed + 1) << " PASS: " << name << "\n";
|
||||||
|
++passed;
|
||||||
|
} else {
|
||||||
|
std::cout << "Test " << (passed + failed + 1) << " FAIL: " << name << "\n";
|
||||||
|
++failed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
int passed = 0;
|
||||||
|
int failed = 0;
|
||||||
|
|
||||||
|
std::string text =
|
||||||
|
"# Intro\n"
|
||||||
|
"Hello.\n"
|
||||||
|
"# Usage\n"
|
||||||
|
"Details.\n";
|
||||||
|
auto sections = parseHelpSections(text);
|
||||||
|
expect(sections.size() == 2, "section count", passed, failed);
|
||||||
|
expect(sections[0].title == "Intro", "first title", passed, failed);
|
||||||
|
expect(sections[1].title == "Usage", "second title", passed, failed);
|
||||||
|
expect(sections[0].content.find("Hello") != std::string::npos,
|
||||||
|
"content captured", passed, failed);
|
||||||
|
|
||||||
|
std::cout << "\n=== Step 162 Results: " << passed << " passed, "
|
||||||
|
<< failed << " failed ===\n";
|
||||||
|
return failed == 0 ? 0 : 1;
|
||||||
|
}
|
||||||
@@ -361,7 +361,7 @@ Final polish, documentation, and ecosystem features.
|
|||||||
library (.dll/.so) with a defined entry point.
|
library (.dll/.so) with a defined entry point.
|
||||||
*New:* `PluginAPI.h`, `PluginLoader.h`
|
*New:* `PluginAPI.h`, `PluginLoader.h`
|
||||||
|
|
||||||
- [ ] **Step 162: User documentation**
|
- [x] **Step 162: User documentation**
|
||||||
In-editor help system. Help > Documentation opens a panel with:
|
In-editor help system. Help > Documentation opens a panel with:
|
||||||
- Getting Started guide
|
- Getting Started guide
|
||||||
- Annotation reference (all annotation types with examples)
|
- Annotation reference (all annotation types with examples)
|
||||||
|
|||||||
Reference in New Issue
Block a user