From d0f5eeeab763bf5c693c15c48aa3ed0f478af5c2 Mon Sep 17 00:00:00 2001 From: Kuba Sunderland-Ober Date: Thu, 14 May 2026 17:18:45 +0200 Subject: [PATCH 1/2] Update the WIP. --- WIP.md | 328 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 323 insertions(+), 5 deletions(-) diff --git a/WIP.md b/WIP.md index fc9f49f..dd86535 100644 --- a/WIP.md +++ b/WIP.md @@ -4,7 +4,7 @@ Jekyll site (`just-the-docs` theme) deploying to `docs.twinbasic.com`. Source un ## Status -Reference documentation is **complete**. All ten packages have full reference coverage adapted from primary sources (Microsoft VBA-Docs CC-BY-4.0 for the runtime library, `.twin` source for the twinBASIC-specific packages); the CEF and WebView2 packages also carry a tutorial set. +Reference documentation is **complete for ten packages** out of eleven. All packages except `tbIDE` have full reference coverage adapted from primary sources (Microsoft VBA-Docs CC-BY-4.0 for the runtime library, `.twin` source for the twinBASIC-specific packages); the CEF and WebView2 packages also carry a tutorial set. **`tbIDE` is in progress** — addin extensibility package; see [tbIDE](#tbide) below. | Package | Reference | Tutorials | |--------------------------------------|-----------|-----------| @@ -18,6 +18,7 @@ Reference documentation is **complete**. All ten packages have full reference co | WinEventLogLib | done | — | | WinNamedPipesLib | done | — | | WinServicesLib | done | — | +| tbIDE | **WIP** | — | The rest of this file is the maintenance guide for adding new pages or updating existing ones — primary-source paths, page templates, cross-section linking conventions, the per-symbol workflow, and the integrity check. @@ -36,6 +37,7 @@ When working from a primary source: always read it first — **never paraphrase - `docs/Reference/WinEventLogLib/` — Windows Event Log package: the generic `EventLog(Of T1, T2)` class and the `EventLogHelperPublic` module with its single `RegisterEventLogInternal` helper. Three pages total — `index.md`, `EventLog.md`, `EventLogHelperPublic.md`. - `docs/Reference/WinNamedPipesLib/` — Windows Named Pipes package: the IOCP-based async pipe framework — `NamedPipeServer` + `NamedPipeServerConnection` on the server side, `NamedPipeClientManager` + `NamedPipeClientConnection` on the client side. Five pages total (`index.md` + one per class). - `docs/Reference/WinServicesLib/` — Windows Services package: a thin OS-services wrapper. `Services` (predeclared singleton) coordinates one or more `ServiceManager` configurations; `ServiceCreator(Of T)` is the generic factory the dispatcher uses to instantiate each user-defined `ITbService` class; `ServiceState` is a read-only state snapshot for an installed service. Four public enums (`ServiceTypeConstants`, `ServiceStartConstants`, `ServiceControlCodeConstants`, `ServiceStatusConstants`) live under `Enumerations/`. +- `docs/Reference/tbIDE/` — IDE Extensibility package (this is the **addin SDK**). The package is type-only — it ships **public interfaces + CoClasses** that an addin DLL binds to; every implementation behind them lives in the twinBASIC IDE itself. The user-facing surface is one entry-point factory (`tbCreateCompilerAddin`) plus ~20 CoClasses grouped by role: the addin contract (`AddIn`), the root API (`Host`), the loaded `Project`, the editors collection (`Editor` / `CodeEditor` / `Editors`), the virtual file system (`FileSystem` / `FileSystemItem` / `Folder` / `File`), the in-IDE UI surface (`Toolbar` / `Toolbars` / `Button` / `ToolWindow` / `ToolWindows`), the HTML DOM inside a tool window (`HtmlElement` / `HtmlElements` / `HtmlElementProperty` / `HtmlElementProperties` / `HtmlEventProperty` / `HtmlEventProperties`), the `DebugConsole`, `KeyboardShortcuts`, `Themes`, and the single concrete user-instantiable helper class `AddinTimer`. Flat layout — one page per CoClass / Class plus the index landing. - `docs/Reference/Statements.md` — alphabetical index of language statements. - `docs/Reference/Procedures and Functions.md` — alphabetical index of procedures/functions. - `docs/_includes/footer_custom.html` — overrides the theme's footer slot; renders the copyright line and, when `vba_attribution: true` is set in a page's frontmatter, an additional CC-BY-4.0 attribution line beneath it. @@ -73,6 +75,27 @@ All of twinbasic's package sources are at: etc. ``` +For the **tbIDE** package, the sources are not in `..\tb-export\`. They live inside one of the six addin sample projects (any will do — the package is a binary-only compiler package and ships its `.twin` declarations alongside each sample). Use sample10's copy as the canonical path: + +``` +..\tbrepro\sample10\WaynesWorldAddInTest1\Packages\tbIDE\Sources\ ← 24 flat .twin files +..\tbrepro\sample10\WaynesWorldAddInTest1\Packages\tbIDE\LICENCE.md ← MIT, Wayne Phillips, 2022 +..\tbrepro\sample10\WaynesWorldAddInTest1\Packages\tbIDE\Settings ← project.name = "tbIDE", buildType = Package TWINPACK +``` + +The six matching consumer-side example addins live at: + +``` +..\tbrepro\sample10\WaynesWorldAddInTest1\Sources\MainModule.twin ← kitchen-sink: toolbar / toolwindow / DOM / events / Evaluate / ActiveEditors +..\tbrepro\sample11\WaynesWorldCPUMonitorTest1\Sources\MainModule.twin ← AddinTimer + chartjs custom-element + onClose cleanup +..\tbrepro\sample12\WaynesWorldMonacoEditorTest1\Sources\MainModule.twin ← monaco custom-element + .editor.AddEventListener +..\tbrepro\sample13\WaynesListViewAddIn\Sources\MainModule.twin ← listview custom-element + ApplyCss + raiseEvent() from inline HTML +..\tbrepro\sample14\WaynesVirtualListViewAddIn\Sources\MainModule.twin ← virtuallistview + onAsyncGetItemHTML + setAsyncResult + notifyChangedItem +..\tbrepro\sample15\tbGlobalSearchAddIn1\Sources\MainModule.twin ← real-world: FS traversal + ReadText + ActiveEditors.Open + persistent settings +``` + +Read them in roughly that order — sample10 introduces the addin idioms one by one, samples 11–14 each focus on a single advanced custom-element widget (chartjs / monaco / listview / virtuallistview), and sample15 is a complete, polished addin that exercises the file system + editor-navigation surface. + For the CEF package, the examples live in a different folder: ``` @@ -945,6 +968,275 @@ The `index.md` should be substantial and walk the reader through: **License:** MIT (copyright Wayne Phillips T/A iTech Masters, 2025; first release v0.1, 04-FEB-2025) — same situation as every other Wayne Phillips package. Pages are fully original content; **omit** the `vba_attribution: true` flag. +### tbIDE + +The **addin SDK** — a type-only compiler package. Every public symbol is an interface or a CoClass; the actual implementations live in the IDE binary, and an addin DLL binds against the type declarations and lets the IDE marshal calls into its implementations at run time. Twenty-four flat `.twin` files in `..\tbrepro\sample10\WaynesWorldAddInTest1\Packages\tbIDE\Sources\`, each 8–57 lines (501 lines total) — there is no plumbing to skip, every file declares user-facing types. + +The `CHANGELOG.md` shipped in the package is a leftover copy-paste from a different package ("twinBASIC WinNativeForms") and is **not** about tbIDE — **do not** propagate that history onto the docs. + +#### How an addin is built and loaded + +From any of the six sample `Settings` files (the structure is identical across them): + +- `project.buildType`: **Standard DLL** — addins are not packages, they are DLLs that the IDE loads. +- `project.buildPath`: `${IdePath}\addins\${Architecture}\${ProjectName}.${FileExtension}` — the build output drops directly into `\addins\Win32\` or `\addins\Win64\`. The IDE scans this folder on startup. +- `project.references` includes the tbIDE compiler package: `id: {99DEC38C-75F6-4488-8EE7-2D52D83881D2}`, `isCompilerPackage: true`, `publisher: TWINBASIC-COMPILER`, `symbolId: tbIDE`. Same shape that `CustomControls` uses. + +The DLL **must** export a single factory function the IDE will call: + +```tb +Module MainModule + [DllExport] + Public Function tbCreateCompilerAddin(ByVal Host As Host) As AddIn + Return New MyAddinClass(Host) + End Function +End Module +``` + +The returned object must implement the [`AddIn`](#addin) interface (a single read-only `Name` property). The IDE releases the object when the addin is disabled or the IDE shuts down. Every sample uses this exact `tbCreateCompilerAddin` skeleton — surface it on the index landing as the canonical entry point. + +#### Public user-facing surface + +Twenty-four files declaring twenty-three public CoClasses + one concrete `Class` + one interface-only declaration: + +| File | Public symbols | Role | +|------------------------------|-----------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------| +| `Addin.twin` | `IAddInV1` + `AddIn` CoClass | The contract every addin's main class implements. One member: `Property Get Name() As String`. | +| `Host.twin` | `IHostV1` + `ItbHostEventsV1/V2/V3` + `Host` CoClass | Root of the API — passed to `tbCreateCompilerAddin`. Versioned events (see "Versioned event interfaces" below). | +| `AddinTimer.twin` | `Class AddinTimer` (no CoClass) | **The only concrete instantiable class** in the package. `New AddinTimer`; sets `Interval` (ms) + `Enabled`; fires `Timer` event. Internally wraps `SetTimer`/`KillTimer`. | +| `Button.twin` | `IButtonV1` + `IButtonEventsV1` + `Button` CoClass | Toolbar button. Returned by `Toolbar.AddButton`. `OnClick` event. | +| `CodeEditor.twin` | `ICodeEditorV1 Extends IEditorV1` + `CodeEditor` CoClass | A code-pane editor — selection, text, Monaco passthrough, `AddMonacoWidget` for inline overlay HTML. Nested `RevealArea` enum. | +| `DebugConsole.twin` | `IDebugConsoleV1` + `DebugConsole` CoClass | The DEBUG CONSOLE pane. `PrintText` (with optional colour), `Clear`, `SetFocus`. | +| `Editor.twin` | `IEditorV1` + `Editor` CoClass | Base interface for editors. Source-side comment: *"Castable to CodeEditor etc."* — i.e. `Dim ce As CodeEditor = editor` works because the underlying object implements both. | +| `Editors.twin` | `IEditorsV1` + `Editors` CoClass | Collection of active editors. `Item(Index)` default member, `Count`, `Open(Path, Line, Col, Options)`. Source-side note: *"There is currently only ONE active editor, accessible via Editors(0) syntax"*. | +| `File.twin` | `IFileV1` + `IFileV2 Extends IFileV1` + `File` CoClass | A virtual-FS file. V1: `Data` / `DataLen` / `Text` / `IsDirty`. V2 adds `ReadText(ReadTextFlags)`. Nested `ReadTextFlags` enum (one flag: `CommentsToWhitespace`). | +| `FileSystem.twin` | `IFileSystemV1` + `FileSystem` CoClass | Tiny — `RootFolder` and `ResolvePath(Path)` (path needs the `twinbasic:/` prefix). | +| `FileSystemItem.twin` | `IFileSystemItemV1` + `FileSystemItem` CoClass | Base for `File` and `Folder`. `Name`, `Path`, `Type`, `Parent`. Nested `FileSystemItemType` enum (`Folder`, `FileVIRTUALDOC`, `FileOTHER`, `FileTWIN`, `FileBAS`, `FileCLS`, `FileUIDESIGNER`, `FileJSON`). | +| `Folder.twin` | `IFolderV1 Extends IFileSystemItemV1` + `Folder` CoClass | `Count`, `Item(IndexOrName)`, `IsPackagesFolder`, plus for-each enumeration over `FileSystemItem` children. | +| `HtmlElement.twin` | `IHtmlElementV1` + `HtmlElement` CoClass | A DOM element inside a tool window. `Properties` (default member — see below), `ChildDomElements`, `Remove`, `AddEventListener(DomEventName, CallbackFunc, Optional Data)`. Plus one `[Hidden]` legacy `AddEventListenerOLD1`. | +| `HtmlElementProperties.twin` | `IHtmlElementPropertiesV1` (`[COMExtensible(True)]`) + CoClass | The dynamic property bag on a DOM element. `Item(DomPropertyName)` is the default member; the `[COMExtensible(True)]` attribute is what makes `.style.display = "flex"` resolve through `Item("style").Item("display")` at run time. | +| `HtmlElementProperty.twin` | `IHtmlElementPropertyV1` (`[COMExtensible(True)]`) + CoClass | One value in the bag. `Value` (Get/Let, default member), plus nested `Properties` for chained access (`.style.color = "white"`). | +| `HtmlElements.twin` | `IHtmlElementsV1` + `HtmlElements` CoClass | The `ChildDomElements` collection. `Item(ID)` default, `Add(ElementID, TagName)` returns the new child. Note `TagName` accepts both standard HTML tags AND the IDE's custom widget tags `chartjs` / `monaco` / `listview` / `virtuallistview`. | +| `IHtmlEventProperties.twin` | `IHtmlEventPropertiesV1` (`[COMExtensible(True)]`) + `HtmlEventProperties` CoClass | The event-payload bag. Like `HtmlElementProperties` but read-only and used inside event handler callbacks. | +| `IHtmlEventProperty.twin` | `IHtmlEventPropertyV1` (`[COMExtensible(True)]`) + `HtmlEventProperty` CoClass | One value in the event bag. | +| `KeyboardShortcuts.twin` | `IKeyboardShortcutsV1` + `KeyboardShortcuts` CoClass | Single member: `Add(keyString, Callback As LongPtr)`. `keyString` is a literal key like `"{CTRL}{SHIFT}d"` (prefixes `{CTRL}` / `{SHIFT}` / `{ALT}`). | +| `Project.twin` | `IProjectV1` + `Project` CoClass | The currently-loaded project. Lifecycle (`Save`, `Close`, `Build`, `Clean`), introspection (`Path`, `Name`, `BaseFolderPath`, `ProjectID`, version + architecture + build-output info), `Evaluate(ExprString)` (debug-console-style expression evaluation), `RootFolder` (entry into the virtual FS), and `LoadMetaData`/`SaveMetaData` (persistent per-addin key/value storage inside the `.twinproj`). Nested `VbBuildType` enum. | +| `Themes.twin` | `IThemesV1` + `Themes` CoClass | `ActiveThemeName` ("Classic" / "Dark" / "Light"), `ActiveThemeNameGroup` ("dark" / "light"). The `Host` events interface fires `OnChangedTheme` when this flips. | +| `ToolWindow.twin` | `IToolWindowV1` + `IToolWindowEventsV1` + `ToolWindow` CoClass | A dockable / floating tool window. `Title`, `Visible`, `Resizable`, `Close`, `ApplyCss(stylesString)`, `RootDomElement` (default member — the entry into the DOM tree). `OnClose` event. | +| `ToolWindows.twin` | `IToolWindowsV1` + `ToolWindows` CoClass | Single member: `Add(Name, Optional UniqueIdForPositionPersistance) As ToolWindow`. The second argument lets the IDE remember the floating-window position across IDE restarts. | +| `Toolbar.twin` | `IToolbarV1` + `Toolbar` CoClass | `AddSplitter` (vertical-bar separator), `AddButton(Id, Caption, Optional IconData)`. | +| `Toolbars.twin` | `IToolbarsV1` + `Toolbars` CoClass | `Item(Index)` default, `Count`. Source-side note: *"There is currently only ONE toolbar, accessible via the Toolbars(0) syntax"*. | + +#### The interface/CoClass split — what it means for the doc layout + +Almost every `.twin` declares one or two `Public Interface IV1 Extends stdole.IUnknown` followed by `Public CoClass ` with `[Default] Interface IV1` (and optionally `[Default, Source] Interface IEventsV1`). The pattern is the standard COM idiom for late-binding-friendly extensibility — the IDE implements the interfaces, the addin holds references typed at the CoClass. + +**The interfaces themselves get no doc page.** Users type their variables `As Host` / `As Button` / `As ToolWindow` (the CoClass), not `As IHostV1`. Fold the interface's members onto the CoClass's page; do not list both. Same convention CustomControls uses for its `_…` default interfaces. + +**Versioning is conveyed by interface chains.** Two cases visible in the source: + +- `IFileV1` → `IFileV2 Extends IFileV1` (V2 adds `ReadText(ReadTextFlags)`). The `File` CoClass declares `[Default] Interface IFileV2`. Document the V2 surface as the canonical `File` page; do not split V1 vs V2. (Mention in passing that `ReadText` is V2-only and consequently won't bind against very early IDE builds — though in practice every shipping IDE is V2+.) +- `IHostV1` → `ItbHostEventsV1` → `ItbHostEventsV2 Extends V1` → `ItbHostEventsV3 Extends V2`. The `Host` CoClass declares `[Default, Source] Interface ItbHostEventsV3`. The new members on V2 / V3 (`OnChangedActiveEditor`, `OnChangedTheme`) are each tagged **`[AllowUnpopulatedVtableEntry]`**, which is the mechanism that lets a newer addin compile against `ItbHostEventsV3` and still load against an older IDE that only implements `V1` — the IDE doesn't have to provide the V2/V3 entries. + +Document all `Host` events together on the `Host.md` page (the per-version split is a compatibility detail, not a user-facing concept). + +#### `AddinTimer` is the only user-instantiable class + +Every other public symbol is a CoClass exposed *to* the addin by the IDE — the addin receives instances via `Host`, never constructs them. `AddinTimer` is the exception: it's a concrete `Class AddinTimer` (not a CoClass) and the addin instantiates it with `New AddinTimer`. Internally it wraps `SetTimer` / `KillTimer` with a private `TimerCallback`, exposes `Interval` (ms) + `Enabled`, and fires a `Timer` event. + +Sample 11's CPU-monitor demonstrates the typical pattern: + +```tb +Private WithEvents Timer As AddinTimer +… +Set Timer = New AddinTimer +Timer.Interval = 500 +Timer.Enabled = True +… +Private Sub Timer_Timer() + ' fires every 500 ms +End Sub +Private Sub myToolWindow_OnClose() + Set Timer = Nothing ' stop the timer when the window closes +End Sub +``` + +The class uses the `Handles` syntax internally (`Private Sub Changed() Handles Enabled.OnPropertyLet, Interval.OnPropertyLet`) so any change to `Enabled` or `Interval` re-arms the underlying timer — surface this as *"set `Enabled = False` to stop, change `Interval` at any time"*, not as an implementation detail. + +`Class_Terminate` calls `KillTimer` so a dropped reference is sufficient to stop. Sample 15 demonstrates that direct Win32 `SetTimer` / `KillTimer` is also fine if `AddinTimer` doesn't fit — both patterns are valid; the package doesn't *require* the helper. + +#### The HTML / DOM surface + +Tool windows are rendered as HTML inside the IDE (the same browser surface the IDE uses for its own panes). The `HtmlElement` / `HtmlElements` / `HtmlElementProperty` / `HtmlElementProperties` quartet is the addin's keyhole into the DOM. + +Three things make this surface unusual and have to be surfaced on the docs: + +1. **`[COMExtensible(True)]` on `HtmlElementProperties` / `HtmlElementProperty` / `HtmlEventProperties` / `HtmlEventProperty`.** The attribute opts the interface into IDispatch dynamic-member resolution, which is what makes `.style.display = "flex"` work — at compile time the right-hand `.style.display` resolves to `Item("style").Item("display").Value = "flex"` (the default-member dance), and the IDE resolves the names against the live DOM at run time. No member named `style` is *declared* on `IHtmlElementPropertiesV1`. Surface this on each of the four pages with a `> [!IMPORTANT]` callout: the property names accepted are **every DOM property of the underlying tag** (standard HTML attributes, CSS-style properties under `.style.…`, plus any custom-element-specific surface like `.chart.data.datasets(0).borderWidth` on a `chartjs` element or `.editor.setOption(...)` on a `monaco` element). The docs cannot enumerate them — refer the reader to the relevant DOM / library reference. +2. **The custom-element tags.** `HtmlElements.Add(id, tagName)` accepts standard HTML tags (`"div"`, `"input"`, `"span"`, `"h1"`, …) AND four IDE-specific widget tags: `"chartjs"` (Chart.js wrapper — surfaces a `.chart` property), `"monaco"` (the Monaco editor — surfaces a `.editor` property), `"listview"` (the IDE's listview widget — surfaces a `.listview` property with `addItem` / `removeItem` / etc.), and `"virtuallistview"` (the same with `setItemCount` + the `onAsyncGetItemHTML` event). All four are demonstrated in samples 11–14. Surface as *"the tag name is forwarded to the IDE's tool-window renderer, which understands the standard HTML tags plus the custom widget tags … see sample 11 / 12 / 13 / 14"*. +3. **`AddEventListener(DomEventName As String, CallbackFunc As LongPtr, Optional Data As Variant)`.** The callback is passed as `AddressOf SomeSub`, and `SomeSub` must have the signature `Sub(ByVal eventInfo As HtmlEventProperties)`. The `eventInfo` parameter is the IDE-marshalled equivalent of the JavaScript `Event` object — `eventInfo.key` / `eventInfo.target.value` / `eventInfo.target.id` are the usual fields, but again the property names are resolved against the *actual* event object at run time, not declared statically. Sample 13 also demonstrates **custom event names raised from inline HTML** via the IDE-side `raiseEvent("name", event, stopPropagation, …customData)` JavaScript helper; the custom-data values become `eventInfo.customData0`, `eventInfo.customData1`, … and are demonstrated in sample 15's `ClickedMatch` handler. Sample 14 demonstrates **async events** via `eventInfo.setAsyncResult("")` (the listener returns the requested HTML asynchronously back into the virtual list view's render cycle). + +Document the four `Html*` classes as the *contract* (`Item` default member, the `Value` accessor, the `Properties` chaining) and use the samples to illustrate the dynamic-resolution mechanism. Do not try to enumerate the resolved property surface — it's open-ended. + +The `HtmlEvent*` half of the quartet declares `Value` as **read-only Get** (vs `HtmlElementProperty`'s `Value` which has Get + Let) — that's the contract distinction between an inbound event payload and an outbound DOM property setter. Note this on each page. + +#### ToolWindow as a jQuery-style selector + +`ToolWindow` is the *root* of a tool window's DOM and **also doubles as a member-by-ID accessor**: `myToolWindow("#dataEntry").Value` (see sample 13) returns the `Value` of the child element whose `id` is `dataEntry`. There is no explicit member on `IToolWindowV1` that takes a string argument — the source-side `RootDomElement` carries `[DefaultMember]`, so `myToolWindow("#dataEntry")` resolves to `RootDomElement.Properties.Item("#dataEntry")`, which the dynamic resolver then interprets as a CSS-style selector against the rendered DOM. Surface this as *"the tool window's default member is `RootDomElement`, which is COM-extensible — string indexing accepts CSS selectors"* and call out the `#id` (single element by ID) form as the most common case. + +`ApplyCss(styles As String)` injects a `