mirror of
https://github.com/nejimakibird/model-weave.git
synced 2026-07-22 06:51:24 +00:00
feat: add business flow direction settings
This commit is contained in:
parent
6201b1c9a2
commit
b7f5cbbaef
15 changed files with 609 additions and 11 deletions
|
|
@ -135,6 +135,7 @@ type: app_process
|
|||
id: PROC-INVENTORY-SEARCH
|
||||
name: Inventory Search Process
|
||||
kind: server_process
|
||||
flow_direction: LR
|
||||
tags:
|
||||
- AppProcess
|
||||
---
|
||||
|
|
@ -177,6 +178,7 @@ type: app_process
|
|||
id: PROC-INVENTORY-INQUIRY
|
||||
name: Inventory Inquiry Business Flow
|
||||
kind: server_process
|
||||
flow_direction: LR
|
||||
tags:
|
||||
- AppProcess
|
||||
- BusinessFlow
|
||||
|
|
@ -212,6 +214,7 @@ type: app_process
|
|||
id: PROC-ORDER-ENTRY-FLOW
|
||||
name: Order Entry Business Flow
|
||||
kind: server_process
|
||||
flow_direction: LR
|
||||
tags:
|
||||
- AppProcess
|
||||
- BusinessFlow
|
||||
|
|
@ -265,6 +268,7 @@ type: app_process
|
|||
id: PROC-SAMPLE-ORDER-ENTRY-FLOW
|
||||
name: Sample Order Entry Business Flow
|
||||
kind: server_process
|
||||
flow_direction: LR
|
||||
tags:
|
||||
- AppProcess
|
||||
- BusinessFlow
|
||||
|
|
@ -362,6 +366,7 @@ Optional fields:
|
|||
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `kind` | Process kind. Free text such as `server_process`, `batch`, `api`, `job`, `event_handler`, or `business_flow`. |
|
||||
| `render_mode` | Optional. If specified, it overrides the initial renderer for this file. If omitted, the format-specific default render mode from the settings is used. |
|
||||
| `flow_direction` | Optional Business Flow preview direction. Supported values are `LR` and `TD`. If omitted, the setting default is used. |
|
||||
| `tags` | Obsidian / Markdown tags. |
|
||||
|
||||
Example:
|
||||
|
|
@ -372,12 +377,31 @@ type: app_process
|
|||
id: PROC-ORDER-ENTRY-FLOW
|
||||
name: Order Entry Business Flow
|
||||
kind: server_process
|
||||
flow_direction: LR
|
||||
tags:
|
||||
- AppProcess
|
||||
- BusinessFlow
|
||||
---
|
||||
```
|
||||
|
||||
## Business Flow direction
|
||||
|
||||
`flow_direction` controls the initial Mermaid direction for the app_process Business Flow preview.
|
||||
|
||||
Supported values:
|
||||
|
||||
* `LR`: left to right.
|
||||
* `TD`: top down.
|
||||
|
||||
Direction resolution priority:
|
||||
|
||||
1. Viewer toolbar override.
|
||||
2. `frontmatter.flow_direction`.
|
||||
3. The plugin setting default App Process Business Flow direction.
|
||||
4. Built-in `LR` fallback.
|
||||
|
||||
Changing the toolbar direction is temporary viewer state and does not rewrite Markdown frontmatter. `flow_direction` is separate from `render_mode`.
|
||||
|
||||
## Sections
|
||||
|
||||
Recommended structure:
|
||||
|
|
|
|||
|
|
@ -135,6 +135,7 @@ type: app_process
|
|||
id: PROC-INVENTORY-SEARCH
|
||||
name: Inventory Search Process
|
||||
kind: server_process
|
||||
flow_direction: LR
|
||||
tags:
|
||||
- AppProcess
|
||||
---
|
||||
|
|
@ -177,6 +178,7 @@ type: app_process
|
|||
id: PROC-INVENTORY-INQUIRY
|
||||
name: Inventory Inquiry Business Flow
|
||||
kind: server_process
|
||||
flow_direction: LR
|
||||
tags:
|
||||
- AppProcess
|
||||
- BusinessFlow
|
||||
|
|
@ -212,6 +214,7 @@ type: app_process
|
|||
id: PROC-ORDER-ENTRY-FLOW
|
||||
name: Order Entry Business Flow
|
||||
kind: server_process
|
||||
flow_direction: LR
|
||||
tags:
|
||||
- AppProcess
|
||||
- BusinessFlow
|
||||
|
|
@ -265,6 +268,7 @@ type: app_process
|
|||
id: PROC-SAMPLE-ORDER-ENTRY-FLOW
|
||||
name: Sample Order Entry Business Flow
|
||||
kind: server_process
|
||||
flow_direction: LR
|
||||
tags:
|
||||
- AppProcess
|
||||
- BusinessFlow
|
||||
|
|
@ -362,6 +366,7 @@ Demonstrates table-based app_process Steps and Flows for the Business Flow previ
|
|||
| ------------- | ------------------------------------------------------------------------------------ |
|
||||
| `kind` | 処理種別です。例: `server_process`, `batch`, `api`, `job`, `event_handler`, `business_flow`。 |
|
||||
| `render_mode` | 任意です。指定した場合、そのファイルの初期表示レンダラを上書きします。未指定の場合は、設定画面のフォーマット別初期表示モードに従います。 |
|
||||
| `flow_direction` | 任意です。Business Flow preview の方向です。指定できる値は `LR` と `TD` です。未指定の場合は設定画面のデフォルトを使います。 |
|
||||
| `tags` | Obsidian / Markdown のタグです。 |
|
||||
|
||||
例:
|
||||
|
|
@ -372,12 +377,31 @@ type: app_process
|
|||
id: PROC-ORDER-ENTRY-FLOW
|
||||
name: Order Entry Business Flow
|
||||
kind: server_process
|
||||
flow_direction: LR
|
||||
tags:
|
||||
- AppProcess
|
||||
- BusinessFlow
|
||||
---
|
||||
```
|
||||
|
||||
## Business Flow 方向
|
||||
|
||||
`flow_direction` は app_process Business Flow preview の Mermaid 方向を指定します。
|
||||
|
||||
指定できる値:
|
||||
|
||||
* `LR`: 左から右。
|
||||
* `TD`: 上から下。
|
||||
|
||||
方向の解決優先順位:
|
||||
|
||||
1. Viewer toolbar の一時上書き。
|
||||
2. `frontmatter.flow_direction`。
|
||||
3. 設定画面の App Process Business Flow 初期方向。
|
||||
4. 組み込みの `LR` fallback。
|
||||
|
||||
Toolbar で方向を変更しても Markdown frontmatter は書き換えません。`flow_direction` は `render_mode` とは別の設定です。
|
||||
|
||||
## セクション
|
||||
|
||||
推奨構成:
|
||||
|
|
|
|||
165
main.js
165
main.js
|
|
@ -9390,6 +9390,21 @@ function waitForAnimationFrame() {
|
|||
});
|
||||
}
|
||||
|
||||
// src/core/app-process-business-flow-direction.ts
|
||||
function normalizeAppProcessBusinessFlowDirection(value) {
|
||||
if (typeof value !== "string") {
|
||||
return void 0;
|
||||
}
|
||||
const normalized = value.trim().toUpperCase();
|
||||
return normalized === "LR" || normalized === "TD" ? normalized : void 0;
|
||||
}
|
||||
function normalizeAppProcessBusinessFlowDirectionWithFallback(value) {
|
||||
return normalizeAppProcessBusinessFlowDirection(value) ?? "LR";
|
||||
}
|
||||
function resolveAppProcessBusinessFlowDirection(input) {
|
||||
return normalizeAppProcessBusinessFlowDirection(input.toolbarOverride) ?? normalizeAppProcessBusinessFlowDirection(input.frontmatterDirection) ?? normalizeAppProcessBusinessFlowDirection(input.settingsDefaultDirection) ?? "LR";
|
||||
}
|
||||
|
||||
// src/settings/model-weave-settings.ts
|
||||
var DOMAIN_VIEW_MODE_SETTING_OPTIONS = [
|
||||
{ value: "mindmap", label: "Mindmap" },
|
||||
|
|
@ -9401,6 +9416,7 @@ var DEFAULT_MODEL_WEAVE_SETTINGS = {
|
|||
defaultErRenderMode: "custom",
|
||||
defaultDfdRenderMode: "mermaid",
|
||||
defaultProcessRenderMode: "custom",
|
||||
defaultBusinessFlowDirection: "LR",
|
||||
defaultScreenRenderMode: "custom",
|
||||
defaultDomainsViewMode: "mindmap",
|
||||
defaultDomainDiagramViewMode: "mindmap",
|
||||
|
|
@ -9476,6 +9492,9 @@ function normalizeModelWeaveSettings(value) {
|
|||
PROCESS_RENDER_MODES,
|
||||
DEFAULT_MODEL_WEAVE_SETTINGS.defaultProcessRenderMode
|
||||
),
|
||||
defaultBusinessFlowDirection: normalizeAppProcessBusinessFlowDirectionWithFallback(
|
||||
raw.defaultBusinessFlowDirection
|
||||
),
|
||||
defaultScreenRenderMode: normalizeEnumValue(
|
||||
raw.defaultScreenRenderMode,
|
||||
SCREEN_RENDER_MODES,
|
||||
|
|
@ -11787,6 +11806,7 @@ function parseAppProcessFile(markdown, path2) {
|
|||
const id = typeof frontmatter.id === "string" ? frontmatter.id.trim() : "";
|
||||
const name = typeof frontmatter.name === "string" ? frontmatter.name.trim() : "";
|
||||
const kind = typeof frontmatter.kind === "string" ? frontmatter.kind.trim() : "";
|
||||
const flowDirection = normalizeAppProcessBusinessFlowDirection(frontmatter.flow_direction);
|
||||
if (frontmatter.type !== "app_process") {
|
||||
warnings.push(createWarning12(path2, "type", 'expected type "app_process"'));
|
||||
}
|
||||
|
|
@ -11863,6 +11883,7 @@ function parseAppProcessFile(markdown, path2) {
|
|||
id,
|
||||
name: fallbackName,
|
||||
kind: kind || void 0,
|
||||
flowDirection,
|
||||
summary: joinSectionLines5(sections.Summary),
|
||||
inputs: inputsTable.rows.map((row) => ({
|
||||
id: row.id?.trim() ?? "",
|
||||
|
|
@ -17912,7 +17933,8 @@ function renderAppProcessBusinessFlow(model, options = {}) {
|
|||
);
|
||||
const source = buildAppProcessBusinessFlowMermaidSource(
|
||||
model,
|
||||
options.colorScheme
|
||||
options.colorScheme,
|
||||
options.flowDirection
|
||||
);
|
||||
const ready = renderMermaidSourceIntoShell(shell3, {
|
||||
source,
|
||||
|
|
@ -17974,7 +17996,7 @@ function buildAppProcessBusinessFlowInteractionTargets(model, sourcePath) {
|
|||
modelType: "app-process"
|
||||
}));
|
||||
}
|
||||
function buildAppProcessBusinessFlowMermaidSource(model, colorScheme) {
|
||||
function buildAppProcessBusinessFlowMermaidSource(model, colorScheme, flowDirection) {
|
||||
const stepNodeIds = /* @__PURE__ */ new Map();
|
||||
const stepNodeIdsByStepId = /* @__PURE__ */ new Map();
|
||||
model.steps.forEach((step, index) => {
|
||||
|
|
@ -17984,7 +18006,8 @@ function buildAppProcessBusinessFlowMermaidSource(model, colorScheme) {
|
|||
stepNodeIdsByStepId.set(step.id, nodeId);
|
||||
}
|
||||
});
|
||||
const lines = ["flowchart LR"];
|
||||
const normalizedDirection = normalizeAppProcessBusinessFlowDirectionWithFallback(flowDirection);
|
||||
const lines = [`flowchart ${normalizedDirection}`];
|
||||
const colorClasses = /* @__PURE__ */ new Map();
|
||||
const domainStyles = [];
|
||||
const nodeClasses = [];
|
||||
|
|
@ -18676,6 +18699,9 @@ var EN_MESSAGES = {
|
|||
"domains.preview.area": "Area",
|
||||
"domains.preview.treeMode": "Tree",
|
||||
"domains.preview.viewMode": "Domain view mode",
|
||||
"appProcess.businessFlow.direction": "Business flow direction",
|
||||
"appProcess.businessFlow.direction.lr": "Left to right",
|
||||
"appProcess.businessFlow.direction.td": "Top down",
|
||||
"domains.preview.diagramEmpty": "No domain hierarchy to display.",
|
||||
"domains.preview.diagramRenderFailed": "Domain hierarchy diagram could not be rendered.",
|
||||
"domains.preview.empty": "No domains defined.",
|
||||
|
|
@ -18921,6 +18947,10 @@ var EN_MESSAGES = {
|
|||
"settings.defaultDfdRenderMode.desc": "Used for dfd_diagram files when frontmatter.render_mode is not set.",
|
||||
"settings.defaultProcessRenderMode.name": "Default process render mode",
|
||||
"settings.defaultProcessRenderMode.desc": "Used for app_process files when frontmatter.render_mode is not set.",
|
||||
"settings.defaultBusinessFlowDirection.name": "Default business flow direction",
|
||||
"settings.defaultBusinessFlowDirection.desc": "Used for app_process business flow previews when frontmatter.flow_direction is not set.",
|
||||
"settings.defaultBusinessFlowDirection.lr": "Left to right",
|
||||
"settings.defaultBusinessFlowDirection.td": "Top down",
|
||||
"settings.defaultScreenRenderMode.name": "Default screen render mode",
|
||||
"settings.defaultScreenRenderMode.desc": "Used for screen files when frontmatter.render_mode is not set.",
|
||||
// eslint-disable-next-line obsidianmd/ui/sentence-case-locale-module
|
||||
|
|
@ -19036,6 +19066,9 @@ var JA_MESSAGES = {
|
|||
"domains.preview.area": "\u9818\u57DF",
|
||||
"domains.preview.treeMode": "\u30C4\u30EA\u30FC",
|
||||
"domains.preview.viewMode": "Domain \u8868\u793A\u30E2\u30FC\u30C9",
|
||||
"appProcess.businessFlow.direction": "Business Flow \u65B9\u5411",
|
||||
"appProcess.businessFlow.direction.lr": "\u5DE6\u304B\u3089\u53F3",
|
||||
"appProcess.businessFlow.direction.td": "\u4E0A\u304B\u3089\u4E0B",
|
||||
"domains.preview.diagramEmpty": "\u8868\u793A\u3067\u304D\u308B Domain \u968E\u5C64\u304C\u3042\u308A\u307E\u305B\u3093\u3002",
|
||||
"domains.preview.diagramRenderFailed": "Domain \u968E\u5C64\u56F3\u3092\u63CF\u753B\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\u3002",
|
||||
"domains.preview.empty": "Domain \u306F\u5B9A\u7FA9\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3002",
|
||||
|
|
@ -19241,6 +19274,10 @@ var JA_MESSAGES = {
|
|||
"settings.defaultDfdRenderMode.desc": "dfd_diagram \u30D5\u30A1\u30A4\u30EB\u3067 frontmatter.render_mode \u304C\u672A\u8A2D\u5B9A\u306E\u5834\u5408\u306B\u4F7F\u7528\u3057\u307E\u3059\u3002",
|
||||
"settings.defaultProcessRenderMode.name": "Process \u306E\u521D\u671F render_mode",
|
||||
"settings.defaultProcessRenderMode.desc": "app_process \u30D5\u30A1\u30A4\u30EB\u3067 frontmatter.render_mode \u304C\u672A\u8A2D\u5B9A\u306E\u5834\u5408\u306B\u4F7F\u7528\u3057\u307E\u3059\u3002",
|
||||
"settings.defaultBusinessFlowDirection.name": "Business Flow \u306E\u521D\u671F\u65B9\u5411",
|
||||
"settings.defaultBusinessFlowDirection.desc": "app_process Business Flow preview \u3067 frontmatter.flow_direction \u304C\u672A\u8A2D\u5B9A\u306E\u5834\u5408\u306B\u4F7F\u7528\u3057\u307E\u3059\u3002",
|
||||
"settings.defaultBusinessFlowDirection.lr": "\u5DE6\u304B\u3089\u53F3",
|
||||
"settings.defaultBusinessFlowDirection.td": "\u4E0A\u304B\u3089\u4E0B",
|
||||
"settings.defaultScreenRenderMode.name": "Screen \u306E\u521D\u671F render_mode",
|
||||
"settings.defaultScreenRenderMode.desc": "screen \u30D5\u30A1\u30A4\u30EB\u3067 frontmatter.render_mode \u304C\u672A\u8A2D\u5B9A\u306E\u5834\u5408\u306B\u4F7F\u7528\u3057\u307E\u3059\u3002",
|
||||
"settings.defaultDomainsViewMode.name": "Domains \u306E\u521D\u671F\u8868\u793A\u30E2\u30FC\u30C9",
|
||||
|
|
@ -20282,6 +20319,7 @@ var DEFAULT_VIEWER_PREFERENCES = {
|
|||
nodeDensity: "normal",
|
||||
defaultDomainsViewMode: "mindmap",
|
||||
defaultDomainDiagramViewMode: "mindmap",
|
||||
defaultBusinessFlowDirection: "LR",
|
||||
localSourceRoot: "",
|
||||
uiLanguage: "auto",
|
||||
showMermaidRenderDebug: false
|
||||
|
|
@ -20335,6 +20373,8 @@ var ModelingPreviewView = class extends import_obsidian7.ItemView {
|
|||
this.splitRatioByKey = /* @__PURE__ */ new Map();
|
||||
this.domainsDiagramMode = "mindmap";
|
||||
this.domainsDiagramModeFilePath = null;
|
||||
this.appProcessBusinessFlowDirectionOverride = null;
|
||||
this.appProcessBusinessFlowDirectionFilePath = null;
|
||||
this.domainsDiagramModeState = null;
|
||||
this.activeScrollContainer = null;
|
||||
this.focusModeEnabled = false;
|
||||
|
|
@ -20492,6 +20532,7 @@ var ModelingPreviewView = class extends import_obsidian7.ItemView {
|
|||
this.persistActiveViewportState();
|
||||
this.persistCurrentScrollPosition();
|
||||
this.prepareDomainsDiagramMode(state, nextFilePath);
|
||||
this.prepareAppProcessBusinessFlowDirection(state, nextFilePath);
|
||||
this.prepareViewportState(state, reason);
|
||||
this.state = state;
|
||||
this.renderCurrentState();
|
||||
|
|
@ -20619,6 +20660,18 @@ var ModelingPreviewView = class extends import_obsidian7.ItemView {
|
|||
state.hasUserInteracted = false;
|
||||
}
|
||||
}
|
||||
prepareAppProcessBusinessFlowDirection(state, nextFilePath) {
|
||||
if (state.mode !== "summary" || (state.businessFlow?.steps.length ?? 0) === 0) {
|
||||
this.appProcessBusinessFlowDirectionOverride = null;
|
||||
this.appProcessBusinessFlowDirectionFilePath = null;
|
||||
return;
|
||||
}
|
||||
if (this.appProcessBusinessFlowDirectionFilePath === nextFilePath) {
|
||||
return;
|
||||
}
|
||||
this.appProcessBusinessFlowDirectionOverride = null;
|
||||
this.appProcessBusinessFlowDirectionFilePath = nextFilePath;
|
||||
}
|
||||
prepareDomainsDiagramMode(state, nextFilePath) {
|
||||
if (state.mode !== "domains" && state.mode !== "domain-diagram") {
|
||||
this.domainsDiagramModeFilePath = null;
|
||||
|
|
@ -20769,7 +20822,8 @@ var ModelingPreviewView = class extends import_obsidian7.ItemView {
|
|||
render: () => renderAppProcessBusinessFlow(state.businessFlow, {
|
||||
forExport: true,
|
||||
debug: false,
|
||||
colorScheme: state.colorScheme
|
||||
colorScheme: state.colorScheme,
|
||||
flowDirection: this.getAppProcessBusinessFlowDirection(state)
|
||||
})
|
||||
};
|
||||
}
|
||||
|
|
@ -21668,6 +21722,86 @@ var ModelingPreviewView = class extends import_obsidian7.ItemView {
|
|||
const rightGroup = toolbar.querySelector(".model-weave-zoom-toolbar-right") ?? toolbar;
|
||||
rightGroup.appendChild(wrapper);
|
||||
}
|
||||
appendAppProcessBusinessFlowDirectionSelector(container, filePath) {
|
||||
const toolbar = container.querySelector(".mdspec-zoom-toolbar");
|
||||
if (!toolbar) {
|
||||
return;
|
||||
}
|
||||
toolbar.addClass("model-weave-render-mode-toolbar-host");
|
||||
toolbar.querySelector(".model-weave-business-flow-direction-select-group")?.remove();
|
||||
const doc = container.ownerDocument;
|
||||
const wrapper = doc.createElement("div");
|
||||
wrapper.className = "model-weave-business-flow-direction-select-group model-weave-render-mode-row";
|
||||
const label = doc.createElement("span");
|
||||
label.addClass("model-weave-render-mode-label");
|
||||
label.textContent = this.t("appProcess.businessFlow.direction");
|
||||
wrapper.appendChild(label);
|
||||
const select = doc.createElement("select");
|
||||
select.addClass("model-weave-business-flow-direction-select");
|
||||
for (const direction of ["LR", "TD"]) {
|
||||
const option = doc.createElement("option");
|
||||
option.value = direction;
|
||||
option.textContent = this.getAppProcessBusinessFlowDirectionLabel(direction);
|
||||
option.selected = this.getAppProcessBusinessFlowDirectionForFile(filePath) === direction;
|
||||
select.appendChild(option);
|
||||
}
|
||||
select.addEventListener("change", () => {
|
||||
this.setAppProcessBusinessFlowDirection(select.value, filePath);
|
||||
});
|
||||
wrapper.appendChild(select);
|
||||
const rightGroup = toolbar.querySelector(".model-weave-zoom-toolbar-right") ?? toolbar;
|
||||
rightGroup.appendChild(wrapper);
|
||||
}
|
||||
getAppProcessBusinessFlowDirectionLabel(direction) {
|
||||
return direction === "TD" ? this.t("appProcess.businessFlow.direction.td") : this.t("appProcess.businessFlow.direction.lr");
|
||||
}
|
||||
getAppProcessBusinessFlowDirection(state) {
|
||||
return resolveAppProcessBusinessFlowDirection({
|
||||
toolbarOverride: this.getAppProcessBusinessFlowDirectionOverride(state.filePath),
|
||||
frontmatterDirection: state.businessFlowDirection,
|
||||
settingsDefaultDirection: this.viewerPreferences.defaultBusinessFlowDirection
|
||||
});
|
||||
}
|
||||
getAppProcessBusinessFlowDirectionForFile(filePath) {
|
||||
const summaryState = this.state.mode === "summary" && this.state.filePath === filePath ? this.state : null;
|
||||
return resolveAppProcessBusinessFlowDirection({
|
||||
toolbarOverride: this.getAppProcessBusinessFlowDirectionOverride(filePath),
|
||||
frontmatterDirection: summaryState?.businessFlowDirection,
|
||||
settingsDefaultDirection: this.viewerPreferences.defaultBusinessFlowDirection
|
||||
});
|
||||
}
|
||||
getAppProcessBusinessFlowDirectionOverride(filePath) {
|
||||
return this.appProcessBusinessFlowDirectionFilePath === filePath ? this.appProcessBusinessFlowDirectionOverride : null;
|
||||
}
|
||||
setAppProcessBusinessFlowDirection(direction, filePath) {
|
||||
const nextDirection = normalizeAppProcessBusinessFlowDirection(direction);
|
||||
if (!nextDirection) {
|
||||
return;
|
||||
}
|
||||
if (this.appProcessBusinessFlowDirectionOverride === nextDirection && this.appProcessBusinessFlowDirectionFilePath === filePath) {
|
||||
return;
|
||||
}
|
||||
const shouldRestoreViewOnly = this.viewOnlyEnabled && this.viewOnlyTarget?.classList.contains("model-weave-app-process-business-flow");
|
||||
this.appProcessBusinessFlowDirectionOverride = nextDirection;
|
||||
this.appProcessBusinessFlowDirectionFilePath = filePath;
|
||||
this.viewportStateCache.delete(filePath);
|
||||
resetGraphViewportState(this.screenPreviewViewportState);
|
||||
this.renderCurrentState();
|
||||
this.restoreCurrentScrollPosition();
|
||||
if (shouldRestoreViewOnly) {
|
||||
const view = this.contentEl.ownerDocument.defaultView;
|
||||
view?.requestAnimationFrame(() => {
|
||||
view.requestAnimationFrame(() => {
|
||||
const nextTarget = this.contentEl.querySelector(
|
||||
".model-weave-app-process-business-flow"
|
||||
);
|
||||
if (nextTarget) {
|
||||
this.setViewOnlyMode(true, { target: nextTarget });
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
getDomainDiagramModeLabel(mode) {
|
||||
if (mode === "mindmap") {
|
||||
return this.t("domains.preview.mindmap");
|
||||
|
|
@ -21712,6 +21846,7 @@ var ModelingPreviewView = class extends import_obsidian7.ItemView {
|
|||
onExportAndOpenPng: () => this.exportCurrentDiagramAsPngAndOpenWithNotice(),
|
||||
showMermaidRenderDebug: this.viewerPreferences.showMermaidRenderDebug,
|
||||
colorScheme: state.colorScheme,
|
||||
flowDirection: this.getAppProcessBusinessFlowDirection(state),
|
||||
app: this.app,
|
||||
interactionSourcePath: state.filePath,
|
||||
viewportState: this.screenPreviewViewportState,
|
||||
|
|
@ -21721,6 +21856,7 @@ var ModelingPreviewView = class extends import_obsidian7.ItemView {
|
|||
});
|
||||
ensureGraphIdentityTitle(businessFlowRoot, buildSummaryGraphTitle(state));
|
||||
this.appendViewerToolbarControls(businessFlowRoot);
|
||||
this.appendAppProcessBusinessFlowDirectionSelector(businessFlowRoot, state.filePath);
|
||||
shell3.topPane.appendChild(businessFlowRoot);
|
||||
this.renderSummaryDetails(shell3.bottomPane, state, {
|
||||
suppressBusinessFlowChart: true
|
||||
|
|
@ -21820,6 +21956,7 @@ var ModelingPreviewView = class extends import_obsidian7.ItemView {
|
|||
const businessFlowRoot = renderAppProcessBusinessFlow(state.businessFlow, {
|
||||
viewportState: this.screenPreviewViewportState,
|
||||
colorScheme: state.colorScheme,
|
||||
flowDirection: this.getAppProcessBusinessFlowDirection(state),
|
||||
app: this.app,
|
||||
interactionSourcePath: state.filePath,
|
||||
...getGraphExportLabels(this.t),
|
||||
|
|
@ -21831,6 +21968,7 @@ var ModelingPreviewView = class extends import_obsidian7.ItemView {
|
|||
});
|
||||
ensureGraphIdentityTitle(businessFlowRoot, buildSummaryGraphTitle(state));
|
||||
this.appendViewerToolbarControls(businessFlowRoot);
|
||||
this.appendAppProcessBusinessFlowDirectionSelector(businessFlowRoot, state.filePath);
|
||||
section.appendChild(businessFlowRoot);
|
||||
}
|
||||
if (state.sections.length > 0) {
|
||||
|
|
@ -24462,6 +24600,10 @@ var PROCESS_RENDER_MODE_OPTIONS = [
|
|||
var SCREEN_RENDER_MODE_OPTIONS = [
|
||||
"custom"
|
||||
];
|
||||
var BUSINESS_FLOW_DIRECTION_OPTIONS = [
|
||||
"LR",
|
||||
"TD"
|
||||
];
|
||||
var DOMAIN_VIEW_MODE_OPTIONS = [
|
||||
"mindmap",
|
||||
"area",
|
||||
|
|
@ -24479,6 +24621,9 @@ function isDfdRenderModeOption(value) {
|
|||
function isProcessRenderModeOption(value) {
|
||||
return PROCESS_RENDER_MODE_OPTIONS.some((candidate) => candidate === value);
|
||||
}
|
||||
function isBusinessFlowDirectionOption(value) {
|
||||
return BUSINESS_FLOW_DIRECTION_OPTIONS.some((candidate) => candidate === value);
|
||||
}
|
||||
function isScreenRenderModeOption(value) {
|
||||
return SCREEN_RENDER_MODE_OPTIONS.some((candidate) => candidate === value);
|
||||
}
|
||||
|
|
@ -24917,6 +25062,7 @@ var ModelWeavePlugin = class extends import_obsidian8.Plugin {
|
|||
nodeDensity: this.settings.nodeDensity,
|
||||
defaultDomainsViewMode: this.settings.defaultDomainsViewMode,
|
||||
defaultDomainDiagramViewMode: this.settings.defaultDomainDiagramViewMode,
|
||||
defaultBusinessFlowDirection: this.settings.defaultBusinessFlowDirection,
|
||||
localSourceRoot: this.settings.localSourceRoot,
|
||||
uiLanguage: this.settings.uiLanguage,
|
||||
showMermaidRenderDebug: this.settings.showMermaidRenderDebug
|
||||
|
|
@ -25531,6 +25677,7 @@ var ModelWeavePlugin = class extends import_obsidian8.Plugin {
|
|||
textSections: this.buildAppProcessTextSections(model),
|
||||
tables: this.buildAppProcessSummaryTables(model, file.path),
|
||||
appProcessDomainPlacement: domainPlacement,
|
||||
businessFlowDirection: model.flowDirection,
|
||||
businessFlow: (model.steps?.length ?? 0) > 0 ? {
|
||||
title: model.name || model.id,
|
||||
steps: model.steps ?? [],
|
||||
|
|
@ -27445,6 +27592,16 @@ var ModelWeaveSettingTab = class extends import_obsidian8.PluginSettingTab {
|
|||
});
|
||||
});
|
||||
});
|
||||
new import_obsidian8.Setting(containerEl).setName(t("settings.defaultBusinessFlowDirection.name")).setDesc(t("settings.defaultBusinessFlowDirection.desc")).addDropdown((dropdown) => {
|
||||
dropdown.addOption("LR", t("settings.defaultBusinessFlowDirection.lr")).addOption("TD", t("settings.defaultBusinessFlowDirection.td")).setValue(settings.defaultBusinessFlowDirection).onChange(async (value) => {
|
||||
if (!isBusinessFlowDirectionOption(value)) {
|
||||
return;
|
||||
}
|
||||
await this.plugin.updateSettings({
|
||||
defaultBusinessFlowDirection: value
|
||||
});
|
||||
});
|
||||
});
|
||||
new import_obsidian8.Setting(containerEl).setName(t("settings.defaultScreenRenderMode.name")).setDesc(t("settings.defaultScreenRenderMode.desc")).addDropdown((dropdown) => {
|
||||
dropdown.addOption("custom", t("settings.option.custom")).setValue(settings.defaultScreenRenderMode).onChange(async (value) => {
|
||||
if (!isScreenRenderModeOption(value)) {
|
||||
|
|
|
|||
37
src/core/app-process-business-flow-direction.ts
Normal file
37
src/core/app-process-business-flow-direction.ts
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
export type AppProcessBusinessFlowDirection = "LR" | "TD";
|
||||
|
||||
export interface ResolveAppProcessBusinessFlowDirectionInput {
|
||||
toolbarOverride?: unknown;
|
||||
frontmatterDirection?: unknown;
|
||||
settingsDefaultDirection?: unknown;
|
||||
}
|
||||
|
||||
export function normalizeAppProcessBusinessFlowDirection(
|
||||
value: unknown
|
||||
): AppProcessBusinessFlowDirection | undefined {
|
||||
if (typeof value !== "string") {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const normalized = value.trim().toUpperCase();
|
||||
return normalized === "LR" || normalized === "TD"
|
||||
? normalized
|
||||
: undefined;
|
||||
}
|
||||
|
||||
export function normalizeAppProcessBusinessFlowDirectionWithFallback(
|
||||
value: unknown
|
||||
): AppProcessBusinessFlowDirection {
|
||||
return normalizeAppProcessBusinessFlowDirection(value) ?? "LR";
|
||||
}
|
||||
|
||||
export function resolveAppProcessBusinessFlowDirection(
|
||||
input: ResolveAppProcessBusinessFlowDirectionInput
|
||||
): AppProcessBusinessFlowDirection {
|
||||
return (
|
||||
normalizeAppProcessBusinessFlowDirection(input.toolbarOverride) ??
|
||||
normalizeAppProcessBusinessFlowDirection(input.frontmatterDirection) ??
|
||||
normalizeAppProcessBusinessFlowDirection(input.settingsDefaultDirection) ??
|
||||
"LR"
|
||||
);
|
||||
}
|
||||
|
|
@ -77,6 +77,9 @@ export const EN_MESSAGES: ModelWeaveMessageDictionary = {
|
|||
"domains.preview.area": "Area",
|
||||
"domains.preview.treeMode": "Tree",
|
||||
"domains.preview.viewMode": "Domain view mode",
|
||||
"appProcess.businessFlow.direction": "Business flow direction",
|
||||
"appProcess.businessFlow.direction.lr": "Left to right",
|
||||
"appProcess.businessFlow.direction.td": "Top down",
|
||||
"domains.preview.diagramEmpty": "No domain hierarchy to display.",
|
||||
"domains.preview.diagramRenderFailed": "Domain hierarchy diagram could not be rendered.",
|
||||
"domains.preview.empty": "No domains defined.",
|
||||
|
|
@ -322,6 +325,10 @@ export const EN_MESSAGES: ModelWeaveMessageDictionary = {
|
|||
"settings.defaultDfdRenderMode.desc": "Used for dfd_diagram files when frontmatter.render_mode is not set.",
|
||||
"settings.defaultProcessRenderMode.name": "Default process render mode",
|
||||
"settings.defaultProcessRenderMode.desc": "Used for app_process files when frontmatter.render_mode is not set.",
|
||||
"settings.defaultBusinessFlowDirection.name": "Default business flow direction",
|
||||
"settings.defaultBusinessFlowDirection.desc": "Used for app_process business flow previews when frontmatter.flow_direction is not set.",
|
||||
"settings.defaultBusinessFlowDirection.lr": "Left to right",
|
||||
"settings.defaultBusinessFlowDirection.td": "Top down",
|
||||
"settings.defaultScreenRenderMode.name": "Default screen render mode",
|
||||
"settings.defaultScreenRenderMode.desc": "Used for screen files when frontmatter.render_mode is not set.",
|
||||
// eslint-disable-next-line obsidianmd/ui/sentence-case-locale-module
|
||||
|
|
|
|||
|
|
@ -71,6 +71,9 @@ export const JA_MESSAGES: ModelWeaveMessageDictionary = {
|
|||
"domains.preview.area": "領域",
|
||||
"domains.preview.treeMode": "ツリー",
|
||||
"domains.preview.viewMode": "Domain 表示モード",
|
||||
"appProcess.businessFlow.direction": "Business Flow 方向",
|
||||
"appProcess.businessFlow.direction.lr": "左から右",
|
||||
"appProcess.businessFlow.direction.td": "上から下",
|
||||
"domains.preview.diagramEmpty": "表示できる Domain 階層がありません。",
|
||||
"domains.preview.diagramRenderFailed": "Domain 階層図を描画できませんでした。",
|
||||
"domains.preview.empty": "Domain は定義されていません。",
|
||||
|
|
@ -276,6 +279,10 @@ export const JA_MESSAGES: ModelWeaveMessageDictionary = {
|
|||
"settings.defaultDfdRenderMode.desc": "dfd_diagram ファイルで frontmatter.render_mode が未設定の場合に使用します。",
|
||||
"settings.defaultProcessRenderMode.name": "Process の初期 render_mode",
|
||||
"settings.defaultProcessRenderMode.desc": "app_process ファイルで frontmatter.render_mode が未設定の場合に使用します。",
|
||||
"settings.defaultBusinessFlowDirection.name": "Business Flow の初期方向",
|
||||
"settings.defaultBusinessFlowDirection.desc": "app_process Business Flow preview で frontmatter.flow_direction が未設定の場合に使用します。",
|
||||
"settings.defaultBusinessFlowDirection.lr": "左から右",
|
||||
"settings.defaultBusinessFlowDirection.td": "上から下",
|
||||
"settings.defaultScreenRenderMode.name": "Screen の初期 render_mode",
|
||||
"settings.defaultScreenRenderMode.desc": "screen ファイルで frontmatter.render_mode が未設定の場合に使用します。",
|
||||
"settings.defaultDomainsViewMode.name": "Domains の初期表示モード",
|
||||
|
|
|
|||
31
src/main.ts
31
src/main.ts
|
|
@ -161,6 +161,10 @@ const PROCESS_RENDER_MODE_OPTIONS: readonly ModelWeaveSettings["defaultProcessRe
|
|||
const SCREEN_RENDER_MODE_OPTIONS: readonly ModelWeaveSettings["defaultScreenRenderMode"][] = [
|
||||
"custom"
|
||||
];
|
||||
const BUSINESS_FLOW_DIRECTION_OPTIONS: readonly ModelWeaveSettings["defaultBusinessFlowDirection"][] = [
|
||||
"LR",
|
||||
"TD"
|
||||
];
|
||||
const DOMAIN_VIEW_MODE_OPTIONS: readonly ModelWeaveSettings["defaultDomainsViewMode"][] = [
|
||||
"mindmap",
|
||||
"area",
|
||||
|
|
@ -191,6 +195,12 @@ function isProcessRenderModeOption(
|
|||
return PROCESS_RENDER_MODE_OPTIONS.some((candidate) => candidate === value);
|
||||
}
|
||||
|
||||
function isBusinessFlowDirectionOption(
|
||||
value: string
|
||||
): value is ModelWeaveSettings["defaultBusinessFlowDirection"] {
|
||||
return BUSINESS_FLOW_DIRECTION_OPTIONS.some((candidate) => candidate === value);
|
||||
}
|
||||
|
||||
function isScreenRenderModeOption(
|
||||
value: string
|
||||
): value is ModelWeaveSettings["defaultScreenRenderMode"] {
|
||||
|
|
@ -719,6 +729,7 @@ export default class ModelWeavePlugin extends Plugin {
|
|||
nodeDensity: this.settings.nodeDensity,
|
||||
defaultDomainsViewMode: this.settings.defaultDomainsViewMode,
|
||||
defaultDomainDiagramViewMode: this.settings.defaultDomainDiagramViewMode,
|
||||
defaultBusinessFlowDirection: this.settings.defaultBusinessFlowDirection,
|
||||
localSourceRoot: this.settings.localSourceRoot,
|
||||
uiLanguage: this.settings.uiLanguage,
|
||||
showMermaidRenderDebug: this.settings.showMermaidRenderDebug
|
||||
|
|
@ -1470,6 +1481,7 @@ export default class ModelWeavePlugin extends Plugin {
|
|||
textSections: this.buildAppProcessTextSections(model),
|
||||
tables: this.buildAppProcessSummaryTables(model, file.path),
|
||||
appProcessDomainPlacement: domainPlacement,
|
||||
businessFlowDirection: model.flowDirection,
|
||||
businessFlow:
|
||||
(model.steps?.length ?? 0) > 0
|
||||
? {
|
||||
|
|
@ -3992,6 +4004,25 @@ class ModelWeaveSettingTab extends PluginSettingTab {
|
|||
});
|
||||
});
|
||||
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName(t("settings.defaultBusinessFlowDirection.name"))
|
||||
.setDesc(t("settings.defaultBusinessFlowDirection.desc"))
|
||||
.addDropdown((dropdown) => {
|
||||
dropdown
|
||||
.addOption("LR", t("settings.defaultBusinessFlowDirection.lr"))
|
||||
.addOption("TD", t("settings.defaultBusinessFlowDirection.td"))
|
||||
.setValue(settings.defaultBusinessFlowDirection)
|
||||
.onChange(async (value) => {
|
||||
if (!isBusinessFlowDirectionOption(value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.plugin.updateSettings({
|
||||
defaultBusinessFlowDirection: value
|
||||
});
|
||||
});
|
||||
});
|
||||
new Setting(containerEl)
|
||||
.setName(t("settings.defaultScreenRenderMode.name"))
|
||||
.setDesc(t("settings.defaultScreenRenderMode.desc"))
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { extractMarkdownSections } from "./markdown-sections";
|
|||
import { parseSourceLinks } from "./source-links-parser";
|
||||
import { parseDomainEntries, validateDomainEntries } from "./domains-parser";
|
||||
import { parseDomainSourcesTable } from "./domain-sources-parser";
|
||||
import { normalizeAppProcessBusinessFlowDirection } from "../core/app-process-business-flow-direction";
|
||||
import type {
|
||||
AppProcessModel,
|
||||
ValidationWarning
|
||||
|
|
@ -36,6 +37,7 @@ export function parseAppProcessFile(
|
|||
const id = typeof frontmatter.id === "string" ? frontmatter.id.trim() : "";
|
||||
const name = typeof frontmatter.name === "string" ? frontmatter.name.trim() : "";
|
||||
const kind = typeof frontmatter.kind === "string" ? frontmatter.kind.trim() : "";
|
||||
const flowDirection = normalizeAppProcessBusinessFlowDirection(frontmatter.flow_direction);
|
||||
|
||||
if (frontmatter.type !== "app_process") {
|
||||
warnings.push(createWarning(path, "type", 'expected type "app_process"'));
|
||||
|
|
@ -128,6 +130,7 @@ export function parseAppProcessFile(
|
|||
id,
|
||||
name: fallbackName,
|
||||
kind: kind || undefined,
|
||||
flowDirection,
|
||||
summary: joinSectionLines(sections.Summary),
|
||||
inputs: inputsTable.rows
|
||||
.map((row) => ({
|
||||
|
|
|
|||
|
|
@ -23,6 +23,10 @@ import {
|
|||
} from "./mermaid-helpers";
|
||||
import { decodeEscapedDisplayText } from "../utils/display-text";
|
||||
import { modelWeaveText } from "../i18n/language";
|
||||
import {
|
||||
normalizeAppProcessBusinessFlowDirectionWithFallback,
|
||||
type AppProcessBusinessFlowDirection
|
||||
} from "../core/app-process-business-flow-direction";
|
||||
import { attachMermaidNodeInteractions, type GraphInteractionTarget } from "../views/mermaid-node-interactions";
|
||||
|
||||
export interface AppProcessBusinessFlowModel {
|
||||
|
|
@ -50,6 +54,7 @@ export interface AppProcessBusinessFlowRenderOptions {
|
|||
exportAndOpenPngLabel?: string;
|
||||
exportAndOpenPngTitle?: string;
|
||||
colorScheme?: ResolvedColorScheme;
|
||||
flowDirection?: AppProcessBusinessFlowDirection;
|
||||
app?: App;
|
||||
interactionSourcePath?: string;
|
||||
}
|
||||
|
|
@ -77,7 +82,8 @@ export function renderAppProcessBusinessFlow(
|
|||
);
|
||||
const source = buildAppProcessBusinessFlowMermaidSource(
|
||||
model,
|
||||
options.colorScheme
|
||||
options.colorScheme,
|
||||
options.flowDirection
|
||||
);
|
||||
const ready = renderMermaidSourceIntoShell(shell, {
|
||||
source,
|
||||
|
|
@ -155,7 +161,8 @@ function buildAppProcessBusinessFlowInteractionTargets(
|
|||
|
||||
export function buildAppProcessBusinessFlowMermaidSource(
|
||||
model: AppProcessBusinessFlowModel,
|
||||
colorScheme?: ResolvedColorScheme
|
||||
colorScheme?: ResolvedColorScheme,
|
||||
flowDirection?: unknown
|
||||
): string {
|
||||
const stepNodeIds = new Map<AppProcessStep, string>();
|
||||
const stepNodeIdsByStepId = new Map<string, string>();
|
||||
|
|
@ -167,7 +174,8 @@ export function buildAppProcessBusinessFlowMermaidSource(
|
|||
}
|
||||
});
|
||||
|
||||
const lines = ["flowchart LR"];
|
||||
const normalizedDirection = normalizeAppProcessBusinessFlowDirectionWithFallback(flowDirection);
|
||||
const lines = [`flowchart ${normalizedDirection}`];
|
||||
const colorClasses = new Map<string, ResolvedColorStyle>();
|
||||
const domainStyles: string[] = [];
|
||||
const nodeClasses: string[] = [];
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
import type { RenderMode } from "../core/render-mode";
|
||||
import {
|
||||
normalizeAppProcessBusinessFlowDirectionWithFallback,
|
||||
type AppProcessBusinessFlowDirection
|
||||
} from "../core/app-process-business-flow-direction";
|
||||
import type { ModelWeaveUiLanguage } from "../i18n/messages";
|
||||
|
||||
export type ModelWeaveDefaultZoom = "fit" | "100";
|
||||
|
|
@ -20,6 +24,7 @@ export interface ModelWeaveSettings {
|
|||
defaultErRenderMode: RenderMode;
|
||||
defaultDfdRenderMode: RenderMode;
|
||||
defaultProcessRenderMode: RenderMode;
|
||||
defaultBusinessFlowDirection: AppProcessBusinessFlowDirection;
|
||||
defaultScreenRenderMode: RenderMode;
|
||||
defaultDomainsViewMode: ModelWeaveDomainViewMode;
|
||||
defaultDomainDiagramViewMode: ModelWeaveDomainViewMode;
|
||||
|
|
@ -36,6 +41,7 @@ export interface ModelWeaveSettings {
|
|||
export type ModelWeaveViewerPreferences = Pick<
|
||||
ModelWeaveSettings,
|
||||
| "defaultZoom"
|
||||
| "defaultBusinessFlowDirection"
|
||||
| "defaultDomainsViewMode"
|
||||
| "defaultDomainDiagramViewMode"
|
||||
| "fontSize"
|
||||
|
|
@ -51,6 +57,7 @@ export const DEFAULT_MODEL_WEAVE_SETTINGS: ModelWeaveSettings = {
|
|||
defaultErRenderMode: "custom",
|
||||
defaultDfdRenderMode: "mermaid",
|
||||
defaultProcessRenderMode: "custom",
|
||||
defaultBusinessFlowDirection: "LR",
|
||||
defaultScreenRenderMode: "custom",
|
||||
defaultDomainsViewMode: "mindmap",
|
||||
defaultDomainDiagramViewMode: "mindmap",
|
||||
|
|
@ -131,6 +138,9 @@ export function normalizeModelWeaveSettings(
|
|||
PROCESS_RENDER_MODES,
|
||||
DEFAULT_MODEL_WEAVE_SETTINGS.defaultProcessRenderMode
|
||||
),
|
||||
defaultBusinessFlowDirection: normalizeAppProcessBusinessFlowDirectionWithFallback(
|
||||
raw.defaultBusinessFlowDirection
|
||||
),
|
||||
defaultScreenRenderMode: normalizeEnumValue(
|
||||
raw.defaultScreenRenderMode,
|
||||
SCREEN_RENDER_MODES,
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import type {
|
|||
ValidationWarningCode,
|
||||
ValidationWarningSeverity
|
||||
} from "./warnings";
|
||||
import type { AppProcessBusinessFlowDirection } from "../core/app-process-business-flow-direction";
|
||||
|
||||
export type FileType = (typeof FILE_TYPES)[number];
|
||||
export type DfdObjectKind = (typeof DFD_OBJECT_KINDS)[number];
|
||||
|
|
@ -234,6 +235,7 @@ export interface AppProcessModel extends BaseFileModel<"app-process"> {
|
|||
id: string;
|
||||
name: string;
|
||||
kind?: string;
|
||||
flowDirection?: AppProcessBusinessFlowDirection;
|
||||
summary?: string;
|
||||
domains: DomainEntry[];
|
||||
domainSources: DomainSourceRef[];
|
||||
|
|
|
|||
|
|
@ -26,6 +26,11 @@ import {
|
|||
renderAppProcessBusinessFlow,
|
||||
type AppProcessBusinessFlowModel
|
||||
} from "../renderers/app-process-business-flow";
|
||||
import {
|
||||
normalizeAppProcessBusinessFlowDirection,
|
||||
resolveAppProcessBusinessFlowDirection,
|
||||
type AppProcessBusinessFlowDirection
|
||||
} from "../core/app-process-business-flow-direction";
|
||||
import {
|
||||
attachGraphViewportInteractions,
|
||||
resetGraphViewportState,
|
||||
|
|
@ -358,6 +363,7 @@ type PreviewState =
|
|||
}>;
|
||||
}>;
|
||||
businessFlow?: AppProcessBusinessFlowModel;
|
||||
businessFlowDirection?: AppProcessBusinessFlowDirection;
|
||||
appProcessDomainPlacement?: ResolvedAppProcessDomainPlacement;
|
||||
colorScheme?: ResolvedColorScheme;
|
||||
relatedReferences?: Array<{ label: string; line?: number; ch?: number; count?: number }>;
|
||||
|
|
@ -408,6 +414,7 @@ const DEFAULT_VIEWER_PREFERENCES: ModelWeaveViewerPreferences = {
|
|||
nodeDensity: "normal",
|
||||
defaultDomainsViewMode: "mindmap",
|
||||
defaultDomainDiagramViewMode: "mindmap",
|
||||
defaultBusinessFlowDirection: "LR",
|
||||
localSourceRoot: "",
|
||||
uiLanguage: "auto",
|
||||
showMermaidRenderDebug: false
|
||||
|
|
@ -461,6 +468,8 @@ export class ModelingPreviewView extends ItemView {
|
|||
private readonly splitRatioByKey = new Map<string, number>();
|
||||
private domainsDiagramMode: DomainsMermaidMode = "mindmap";
|
||||
private domainsDiagramModeFilePath: string | null = null;
|
||||
private appProcessBusinessFlowDirectionOverride: AppProcessBusinessFlowDirection | null = null;
|
||||
private appProcessBusinessFlowDirectionFilePath: string | null = null;
|
||||
private domainsDiagramModeState: "domains" | "domain-diagram" | null = null;
|
||||
private activeScrollContainer: HTMLElement | null = null;
|
||||
private focusModeEnabled = false;
|
||||
|
|
@ -654,6 +663,7 @@ export class ModelingPreviewView extends ItemView {
|
|||
this.persistActiveViewportState();
|
||||
this.persistCurrentScrollPosition();
|
||||
this.prepareDomainsDiagramMode(state, nextFilePath);
|
||||
this.prepareAppProcessBusinessFlowDirection(state, nextFilePath);
|
||||
this.prepareViewportState(state, reason);
|
||||
this.state = state;
|
||||
this.renderCurrentState();
|
||||
|
|
@ -812,6 +822,26 @@ export class ModelingPreviewView extends ItemView {
|
|||
}
|
||||
}
|
||||
|
||||
private prepareAppProcessBusinessFlowDirection(
|
||||
state: PreviewState,
|
||||
nextFilePath: string | null
|
||||
): void {
|
||||
if (
|
||||
state.mode !== "summary" ||
|
||||
(state.businessFlow?.steps.length ?? 0) === 0
|
||||
) {
|
||||
this.appProcessBusinessFlowDirectionOverride = null;
|
||||
this.appProcessBusinessFlowDirectionFilePath = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.appProcessBusinessFlowDirectionFilePath === nextFilePath) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.appProcessBusinessFlowDirectionOverride = null;
|
||||
this.appProcessBusinessFlowDirectionFilePath = nextFilePath;
|
||||
}
|
||||
private prepareDomainsDiagramMode(
|
||||
state: PreviewState,
|
||||
nextFilePath: string | null
|
||||
|
|
@ -1005,7 +1035,8 @@ export class ModelingPreviewView extends ItemView {
|
|||
renderAppProcessBusinessFlow(state.businessFlow!, {
|
||||
forExport: true,
|
||||
debug: false,
|
||||
colorScheme: state.colorScheme
|
||||
colorScheme: state.colorScheme,
|
||||
flowDirection: this.getAppProcessBusinessFlowDirection(state)
|
||||
})
|
||||
};
|
||||
}
|
||||
|
|
@ -2099,6 +2130,123 @@ export class ModelingPreviewView extends ItemView {
|
|||
rightGroup.appendChild(wrapper);
|
||||
}
|
||||
|
||||
private appendAppProcessBusinessFlowDirectionSelector(
|
||||
container: HTMLElement,
|
||||
filePath: string
|
||||
): void {
|
||||
const toolbar = container.querySelector<HTMLElement>(".mdspec-zoom-toolbar");
|
||||
if (!toolbar) {
|
||||
return;
|
||||
}
|
||||
|
||||
toolbar.addClass("model-weave-render-mode-toolbar-host");
|
||||
toolbar.querySelector(".model-weave-business-flow-direction-select-group")?.remove();
|
||||
|
||||
const doc = container.ownerDocument;
|
||||
const wrapper = doc.createElement("div");
|
||||
wrapper.className =
|
||||
"model-weave-business-flow-direction-select-group model-weave-render-mode-row";
|
||||
|
||||
const label = doc.createElement("span");
|
||||
label.addClass("model-weave-render-mode-label");
|
||||
label.textContent = this.t("appProcess.businessFlow.direction");
|
||||
wrapper.appendChild(label);
|
||||
|
||||
const select = doc.createElement("select");
|
||||
select.addClass("model-weave-business-flow-direction-select");
|
||||
for (const direction of ["LR", "TD"] as const) {
|
||||
const option = doc.createElement("option");
|
||||
option.value = direction;
|
||||
option.textContent = this.getAppProcessBusinessFlowDirectionLabel(direction);
|
||||
option.selected = this.getAppProcessBusinessFlowDirectionForFile(filePath) === direction;
|
||||
select.appendChild(option);
|
||||
}
|
||||
select.addEventListener("change", () => {
|
||||
this.setAppProcessBusinessFlowDirection(select.value, filePath);
|
||||
});
|
||||
wrapper.appendChild(select);
|
||||
|
||||
const rightGroup = toolbar.querySelector<HTMLElement>(".model-weave-zoom-toolbar-right") ?? toolbar;
|
||||
rightGroup.appendChild(wrapper);
|
||||
}
|
||||
|
||||
private getAppProcessBusinessFlowDirectionLabel(
|
||||
direction: AppProcessBusinessFlowDirection
|
||||
): string {
|
||||
return direction === "TD"
|
||||
? this.t("appProcess.businessFlow.direction.td")
|
||||
: this.t("appProcess.businessFlow.direction.lr");
|
||||
}
|
||||
|
||||
private getAppProcessBusinessFlowDirection(
|
||||
state: Extract<PreviewState, { mode: "summary" }>
|
||||
): AppProcessBusinessFlowDirection {
|
||||
return resolveAppProcessBusinessFlowDirection({
|
||||
toolbarOverride: this.getAppProcessBusinessFlowDirectionOverride(state.filePath),
|
||||
frontmatterDirection: state.businessFlowDirection,
|
||||
settingsDefaultDirection: this.viewerPreferences.defaultBusinessFlowDirection
|
||||
});
|
||||
}
|
||||
|
||||
private getAppProcessBusinessFlowDirectionForFile(
|
||||
filePath: string
|
||||
): AppProcessBusinessFlowDirection {
|
||||
const summaryState = this.state.mode === "summary" && this.state.filePath === filePath
|
||||
? this.state
|
||||
: null;
|
||||
return resolveAppProcessBusinessFlowDirection({
|
||||
toolbarOverride: this.getAppProcessBusinessFlowDirectionOverride(filePath),
|
||||
frontmatterDirection: summaryState?.businessFlowDirection,
|
||||
settingsDefaultDirection: this.viewerPreferences.defaultBusinessFlowDirection
|
||||
});
|
||||
}
|
||||
|
||||
private getAppProcessBusinessFlowDirectionOverride(
|
||||
filePath: string
|
||||
): AppProcessBusinessFlowDirection | null {
|
||||
return this.appProcessBusinessFlowDirectionFilePath === filePath
|
||||
? this.appProcessBusinessFlowDirectionOverride
|
||||
: null;
|
||||
}
|
||||
private setAppProcessBusinessFlowDirection(
|
||||
direction: unknown,
|
||||
filePath: string
|
||||
): void {
|
||||
const nextDirection = normalizeAppProcessBusinessFlowDirection(direction);
|
||||
if (!nextDirection) {
|
||||
return;
|
||||
}
|
||||
if (
|
||||
this.appProcessBusinessFlowDirectionOverride === nextDirection &&
|
||||
this.appProcessBusinessFlowDirectionFilePath === filePath
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const shouldRestoreViewOnly =
|
||||
this.viewOnlyEnabled &&
|
||||
this.viewOnlyTarget?.classList.contains("model-weave-app-process-business-flow");
|
||||
this.appProcessBusinessFlowDirectionOverride = nextDirection;
|
||||
this.appProcessBusinessFlowDirectionFilePath = filePath;
|
||||
this.viewportStateCache.delete(filePath);
|
||||
resetGraphViewportState(this.screenPreviewViewportState);
|
||||
this.renderCurrentState();
|
||||
this.restoreCurrentScrollPosition();
|
||||
|
||||
if (shouldRestoreViewOnly) {
|
||||
const view = this.contentEl.ownerDocument.defaultView;
|
||||
view?.requestAnimationFrame(() => {
|
||||
view.requestAnimationFrame(() => {
|
||||
const nextTarget = this.contentEl.querySelector<HTMLElement>(
|
||||
".model-weave-app-process-business-flow"
|
||||
);
|
||||
if (nextTarget) {
|
||||
this.setViewOnlyMode(true, { target: nextTarget });
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
private getDomainDiagramModeLabel(mode: DomainsMermaidMode): string {
|
||||
if (mode === "mindmap") {
|
||||
return this.t("domains.preview.mindmap");
|
||||
|
|
@ -2150,6 +2298,7 @@ export class ModelingPreviewView extends ItemView {
|
|||
onExportAndOpenPng: () => this.exportCurrentDiagramAsPngAndOpenWithNotice(),
|
||||
showMermaidRenderDebug: this.viewerPreferences.showMermaidRenderDebug,
|
||||
colorScheme: state.colorScheme,
|
||||
flowDirection: this.getAppProcessBusinessFlowDirection(state),
|
||||
app: this.app,
|
||||
interactionSourcePath: state.filePath,
|
||||
viewportState: this.screenPreviewViewportState,
|
||||
|
|
@ -2159,6 +2308,7 @@ export class ModelingPreviewView extends ItemView {
|
|||
});
|
||||
ensureGraphIdentityTitle(businessFlowRoot, buildSummaryGraphTitle(state));
|
||||
this.appendViewerToolbarControls(businessFlowRoot);
|
||||
this.appendAppProcessBusinessFlowDirectionSelector(businessFlowRoot, state.filePath);
|
||||
shell.topPane.appendChild(businessFlowRoot);
|
||||
this.renderSummaryDetails(shell.bottomPane, state, {
|
||||
suppressBusinessFlowChart: true
|
||||
|
|
@ -2280,6 +2430,7 @@ export class ModelingPreviewView extends ItemView {
|
|||
const businessFlowRoot = renderAppProcessBusinessFlow(state.businessFlow, {
|
||||
viewportState: this.screenPreviewViewportState,
|
||||
colorScheme: state.colorScheme,
|
||||
flowDirection: this.getAppProcessBusinessFlowDirection(state),
|
||||
app: this.app,
|
||||
interactionSourcePath: state.filePath,
|
||||
...getGraphExportLabels(this.t),
|
||||
|
|
@ -2291,6 +2442,7 @@ export class ModelingPreviewView extends ItemView {
|
|||
});
|
||||
ensureGraphIdentityTitle(businessFlowRoot, buildSummaryGraphTitle(state));
|
||||
this.appendViewerToolbarControls(businessFlowRoot);
|
||||
this.appendAppProcessBusinessFlowDirectionSelector(businessFlowRoot, state.filePath);
|
||||
section.appendChild(businessFlowRoot);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,9 @@ await build({
|
|||
'export { resolveAppProcessDomainPlacement } from "./src/core/app-process-domain-resolver";',
|
||||
'export { buildVaultIndex } from "./src/core/vault-index";',
|
||||
'export { buildCurrentObjectDiagnostics, localizeDiagnosticMessage } from "./src/core/current-file-diagnostics";',
|
||||
'export { buildAppProcessBusinessFlowMermaidSource, getAppProcessBusinessFlowColorSchemeTargets } from "./src/renderers/app-process-business-flow";'
|
||||
'export { buildAppProcessBusinessFlowMermaidSource, getAppProcessBusinessFlowColorSchemeTargets } from "./src/renderers/app-process-business-flow";',
|
||||
'export { resolveAppProcessBusinessFlowDirection } from "./src/core/app-process-business-flow-direction";',
|
||||
'export { normalizeModelWeaveSettings } from "./src/settings/model-weave-settings";'
|
||||
].join("\n"),
|
||||
resolveDir: ".",
|
||||
sourcefile: "test-app-process-domain-placement-entry.ts",
|
||||
|
|
@ -72,9 +74,28 @@ const {
|
|||
buildCurrentObjectDiagnostics,
|
||||
localizeDiagnosticMessage,
|
||||
buildAppProcessBusinessFlowMermaidSource,
|
||||
getAppProcessBusinessFlowColorSchemeTargets
|
||||
getAppProcessBusinessFlowColorSchemeTargets,
|
||||
resolveAppProcessBusinessFlowDirection,
|
||||
normalizeModelWeaveSettings
|
||||
} = await import(`../${outputFile}?t=${Date.now()}`);
|
||||
|
||||
function processMarkdownWithFrontmatter(frontmatter, stepsHeader, stepsRows) {
|
||||
return `---
|
||||
type: app_process
|
||||
id: PROC-DIRECTION
|
||||
name: Direction Process
|
||||
${frontmatter}
|
||||
---
|
||||
|
||||
# Direction Process
|
||||
|
||||
## Steps
|
||||
|
||||
${stepsHeader}
|
||||
${stepsRows}
|
||||
`;
|
||||
}
|
||||
|
||||
function processMarkdown(stepsHeader, stepsRows, domainSources = "") {
|
||||
return `---
|
||||
type: app_process
|
||||
|
|
@ -147,6 +168,75 @@ function parseSteps(stepsHeader, stepsRows, preStepsSections = "") {
|
|||
return result;
|
||||
}
|
||||
|
||||
test("app_process flow_direction TD renders Business Flow top down", () => {
|
||||
const parsed = parseAppProcessFile(
|
||||
processMarkdownWithFrontmatter(
|
||||
"flow_direction: TD",
|
||||
"| id | domain | label | kind | input | output | rule | invoke | screen | notes |\n|---|---|---|---|---|---|---|---|---|---|",
|
||||
"| start | | Start | start | | | | | | |"
|
||||
),
|
||||
"model/app_process/PROC-DIRECTION.md"
|
||||
);
|
||||
|
||||
assert.equal(parsed.file?.flowDirection, "TD");
|
||||
const source = buildAppProcessBusinessFlowMermaidSource(
|
||||
{
|
||||
title: parsed.file?.name ?? "Direction Process",
|
||||
steps: parsed.file?.steps ?? [],
|
||||
flows: parsed.file?.flows ?? [],
|
||||
hasExplicitFlows: Boolean(parsed.file?.hasExplicitFlows)
|
||||
},
|
||||
undefined,
|
||||
parsed.file?.flowDirection
|
||||
);
|
||||
assert.match(source, /^flowchart TD/);
|
||||
});
|
||||
|
||||
test("app_process invalid flow_direction normalizes to undefined", () => {
|
||||
const parsed = parseAppProcessFile(
|
||||
processMarkdownWithFrontmatter(
|
||||
"flow_direction: RL",
|
||||
"| id | domain | label | kind | input | output | rule | invoke | screen | notes |\n|---|---|---|---|---|---|---|---|---|---|",
|
||||
"| start | | Start | start | | | | | | |"
|
||||
),
|
||||
"model/app_process/PROC-DIRECTION.md"
|
||||
);
|
||||
|
||||
assert.equal(parsed.file?.flowDirection, undefined);
|
||||
});
|
||||
|
||||
test("app_process Business Flow direction uses settings default when frontmatter is absent", () => {
|
||||
const settings = normalizeModelWeaveSettings({
|
||||
defaultBusinessFlowDirection: "TD"
|
||||
});
|
||||
const direction = resolveAppProcessBusinessFlowDirection({
|
||||
settingsDefaultDirection: settings.defaultBusinessFlowDirection
|
||||
});
|
||||
|
||||
assert.equal(direction, "TD");
|
||||
const source = buildAppProcessBusinessFlowMermaidSource(
|
||||
{
|
||||
title: "Direction Process",
|
||||
steps: [{ id: "start", label: "Start" }],
|
||||
flows: [],
|
||||
hasExplicitFlows: false
|
||||
},
|
||||
undefined,
|
||||
direction
|
||||
);
|
||||
assert.match(source, /^flowchart TD/);
|
||||
});
|
||||
|
||||
test("app_process Business Flow toolbar override wins over frontmatter and settings", () => {
|
||||
const direction = resolveAppProcessBusinessFlowDirection({
|
||||
toolbarOverride: "LR",
|
||||
frontmatterDirection: "TD",
|
||||
settingsDefaultDirection: "TD"
|
||||
});
|
||||
|
||||
assert.equal(direction, "LR");
|
||||
});
|
||||
|
||||
test("app_process Transitions.to accepts generic Model Weave asset refs", () => {
|
||||
const index = buildVaultIndex([
|
||||
{ path: "PROC-SOURCE.md", content: `---
|
||||
|
|
|
|||
|
|
@ -42,6 +42,46 @@ const { buildAppProcessBusinessFlowMermaidSource } = await import(
|
|||
`../${outputFile}?t=${Date.now()}`
|
||||
);
|
||||
|
||||
test("app_process Business Flow source defaults to LR direction", () => {
|
||||
const source = buildAppProcessBusinessFlowMermaidSource({
|
||||
title: "Direction test",
|
||||
hasExplicitFlows: false,
|
||||
steps: [{ id: "start", label: "Start" }],
|
||||
flows: []
|
||||
});
|
||||
|
||||
assert.match(source, /^flowchart LR/);
|
||||
});
|
||||
|
||||
test("app_process Business Flow source supports TD direction", () => {
|
||||
const source = buildAppProcessBusinessFlowMermaidSource(
|
||||
{
|
||||
title: "Direction test",
|
||||
hasExplicitFlows: false,
|
||||
steps: [{ id: "start", label: "Start" }],
|
||||
flows: []
|
||||
},
|
||||
undefined,
|
||||
"TD"
|
||||
);
|
||||
|
||||
assert.match(source, /^flowchart TD/);
|
||||
});
|
||||
|
||||
test("app_process Business Flow source falls back to LR for unknown direction", () => {
|
||||
const source = buildAppProcessBusinessFlowMermaidSource(
|
||||
{
|
||||
title: "Direction test",
|
||||
hasExplicitFlows: false,
|
||||
steps: [{ id: "start", label: "Start" }],
|
||||
flows: []
|
||||
},
|
||||
undefined,
|
||||
"RL"
|
||||
);
|
||||
|
||||
assert.match(source, /^flowchart LR/);
|
||||
});
|
||||
test("app_process Mermaid labels preserve visible punctuation", () => {
|
||||
const source = buildAppProcessBusinessFlowMermaidSource({
|
||||
title: "Label escaping verification",
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ await build({
|
|||
'export { buildDomainTree } from "./src/core/domain-tree";',
|
||||
'export { buildDomainRelationshipSummaries } from "./src/core/domain-relationships";',
|
||||
'export { mergeDomainDiagramSources, resolveDomainDiagram } from "./src/core/domain-diagram-resolver";',
|
||||
'export { resolveRenderMode } from "./src/core/render-mode";',
|
||||
'export { getSupportedRenderModes, resolveRenderMode } from "./src/core/render-mode";',
|
||||
'export { buildImpactSummary } from "./src/core/impact-analyzer";',
|
||||
'export { buildWeaveMapModel } from "./src/core/weave-map";',
|
||||
'export { createModelWeaveTranslator } from "./src/i18n/messages";',
|
||||
|
|
@ -117,6 +117,7 @@ const {
|
|||
buildVaultIndex,
|
||||
resolveObjectContext,
|
||||
mergeDomainDiagramSources,
|
||||
getSupportedRenderModes,
|
||||
resolveRenderMode,
|
||||
resolveDomainDiagram,
|
||||
buildCurrentObjectDiagnostics,
|
||||
|
|
@ -924,6 +925,11 @@ test("English settings labels for Domain view modes do not contain Japanese fixe
|
|||
assert.equal(/領域|ツリー|マインドマップ|初期表示モード|表示モードです/.test(labels.join(" ")), false);
|
||||
});
|
||||
|
||||
test("Domains render_mode supports domain-specific view modes", () => {
|
||||
assert.deepEqual(getSupportedRenderModes("domains"), ["mindmap", "area", "tree"]);
|
||||
assert.deepEqual(getSupportedRenderModes("domain-diagram"), ["mindmap", "area", "tree"]);
|
||||
});
|
||||
|
||||
test("resolves Domains render_mode values without warnings", () => {
|
||||
for (const [value, expected] of [
|
||||
["tree", "tree"],
|
||||
|
|
|
|||
Loading…
Reference in a new issue