flexdesk 0.18.0 → 0.18.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +58 -0
- package/css/flexdesk.css +83 -5
- package/css/tokens.css +7 -0
- package/dist/charts.js +78 -12
- package/dist/charts.js.map +2 -2
- package/dist/{chunk-5W4EEKSK.js → chunk-3BLIVOFD.js} +3 -3
- package/dist/{chunk-P74F6I6P.js → chunk-4X4SY3CP.js} +4 -4
- package/dist/{chunk-P74F6I6P.js.map → chunk-4X4SY3CP.js.map} +2 -2
- package/dist/{chunk-ZL3VKINP.js → chunk-HL3IIHU3.js} +494 -3
- package/dist/{chunk-ZL3VKINP.js.map → chunk-HL3IIHU3.js.map} +4 -4
- package/dist/{chunk-RKVYIXDC.js → chunk-NFZJ6P42.js} +19 -500
- package/dist/chunk-NFZJ6P42.js.map +7 -0
- package/dist/{chunk-U5GKJUOC.js → chunk-QRBOIUU3.js} +5 -6
- package/dist/chunk-QRBOIUU3.js.map +7 -0
- package/dist/flexdesk.css +83 -5
- package/dist/flow.js +22 -10
- package/dist/flow.js.map +2 -2
- package/dist/git.js +3 -3
- package/dist/tiles.js +4 -4
- package/dist/tokens.css +7 -0
- package/dist/widgets.js +226 -58
- package/dist/widgets.js.map +2 -2
- package/dist/wm.js +2 -2
- package/docs/components/charts.md +40 -0
- package/docs/components/chat-shell.md +8 -2
- package/docs/components/filter-builder.md +52 -6
- package/docs/components/flow-editors.md +20 -0
- package/docs/components/forms-and-dialogs.md +8 -0
- package/docs/components/input-fields.md +24 -0
- package/docs/components/menus.md +4 -0
- package/docs/components/shell.md +4 -0
- package/docs/components/workspace-picker.md +46 -2
- package/package.json +1 -1
- package/src/charts/plot_config_editor.js +92 -6
- package/src/charts/plot_grid.js +6 -1
- package/src/flow/kit/settings_panel.js +24 -9
- package/src/ui/components/chat_shell.js +7 -1
- package/src/ui/components/context_menu.js +8 -2
- package/src/ui/components/filter_builder.js +53 -28
- package/src/ui/components/filter_model.js +86 -18
- package/src/ui/components/inline_renamer.js +10 -5
- package/src/ui/components/modal.js +25 -8
- package/src/ui/components/workspace_picker.js +125 -27
- package/src/ui/utils/tooltip_service.js +81 -14
- package/dist/chunk-RKVYIXDC.js.map +0 -7
- package/dist/chunk-U5GKJUOC.js.map +0 -7
- /package/dist/{chunk-5W4EEKSK.js.map → chunk-3BLIVOFD.js.map} +0 -0
package/dist/charts.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/charts/data_series_plot_window.js", "../src/charts/plot_config_editor.js"],
|
|
4
|
-
"sourcesContent": ["/**\n * Data Series Plot Window\n *\n * Opens a data series in a ManagedWindow with a split layout:\n * - Left: Plotly chart\n * - Right: Configuration panel (column selection, chart type, styling)\n *\n * Config is persisted in workspace metadata via savePlotConfig callback.\n */\n\nimport { ManagedWindow } from '../ui/components/managed_window.js';\nimport {\n ensurePlotly,\n purge,\n DARK_THEME_LAYOUT,\n DEFAULT_CONFIG,\n getSeriesColor,\n} from './plotly_wrapper.js';\nimport {\n buildTrace,\n buildYAxisLayout,\n buildXAxisLayout,\n buildLegendLayout,\n} from './chart_types.js';\n// The two header helpers are INJECTED, not imported.\n//\n// They used to come from ui/js/ui/utils/header_utils.js, which decides whether a\n// column is a STOCK or a FLOW by looking for `asset`, `liability`, `equity`,\n// `reserves`, `deposit`. That is stock-flow-consistent accounting, not charting,\n// and that one import was the last thing keeping this window out of @flexdesk/charts.\n//\n// The defaults below are what a chart library can honestly know about a column\n// name: whether it looks like a time axis, and nothing else.\n\n/** A column called `t`/`time`/`step`/`period` is the x-axis. Everything else is data. */\nconst defaultClassifyHeader = (header) => {\n const lower = String(header ?? '').trim().toLowerCase();\n return (lower === 't' || lower === 'time' || lower === 'step' || lower === 'period')\n ? 'time' : 'indicator';\n};\n\n/** No convention to strip, absent one being supplied. */\nconst defaultLabelHeader = (header) => header;\nimport { createRafResizeObserver } from '../ui/utils/raf_resize_observer.js';\n\nconst INTERPOLATION_MODES = ['linear', 'step', 'cubic'];\nconst CHART_TYPES = ['line', 'scatter', 'bar', 'area', 'area-stacked'];\nconst LEGEND_POSITIONS = ['top', 'bottom', 'right', 'hidden'];\nconst MAX_AUTO_COLUMNS = 3;\n\nclass DataSeriesPlotWindow {\n /**\n * @param {Object} options\n * @param {string} options.seriesId\n * @param {string} options.seriesName\n * @param {string} options.datasetName\n * @param {string[]} options.headers - Column headers from the series\n * @param {Object} options.dataHub - DataHub service for fetching data\n * @param {Object} options.services - { eventBus, logger }\n * @param {Object} [options.host] - A Host (ui/js/host/host.js). Its `dialogs`\n * capability backs Export PNG/CSV; absent => Blob download fallback.\n * @param {Function} options.getPlotConfig - (seriesId) => config | null\n * @param {Function} options.savePlotConfig - (seriesId, config) => void\n */\n constructor({ seriesId, seriesName, datasetName, headers, dataHub, services, host,\n getPlotConfig, savePlotConfig,\n classifyHeader = defaultClassifyHeader,\n labelHeader = defaultLabelHeader }) {\n this.classifyHeader = classifyHeader;\n this.labelHeader = labelHeader;\n this.seriesId = seriesId;\n this.seriesName = seriesName;\n this.datasetName = datasetName;\n this.headers = headers;\n this.dataHub = dataHub;\n this.services = services;\n this._host = host || null;\n this.getPlotConfig = getPlotConfig;\n this.savePlotConfig = savePlotConfig;\n\n this.window = null;\n this.contentEl = null;\n this.plotDiv = null;\n this._resizeObserver = null;\n this._chartCreated = false;\n this._config = null;\n this._fullData = null; // { headers, rows } with all data\n this._numericColumns = null; // Set of numeric column headers\n this._loading = false;\n }\n\n async show() {\n if (this.window?.isVisible) {\n this.window.bringToFront();\n return;\n }\n\n await ensurePlotly();\n await this.#fetchData();\n\n // Determine which columns are numeric from actual data\n this._numericColumns = new Set(this.headers.filter(h => this.#isNumericColumn(h)));\n\n // Load or create config (filter out any non-numeric columns from saved config)\n const saved = this.getPlotConfig?.(this.seriesId);\n if (saved) {\n this._config = { ...saved };\n this._config.yColumns = (saved.yColumns || []).filter(c => this._numericColumns.has(c.header));\n } else {\n this._config = DataSeriesPlotWindow.createDefaultConfig(\n this.headers, this._numericColumns, this.classifyHeader);\n }\n\n this.#createWindow();\n this.#setupResizeObserver();\n await this.#renderPlot();\n }\n\n close() {\n if (this.window?.isVisible) {\n this.window.close();\n }\n }\n\n // \u2500\u2500 Window creation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n #createWindow() {\n this.contentEl = this.#buildContent();\n\n const title = this.datasetName\n ? `${this.datasetName} \u00B7 ${this.seriesName}`\n : this.seriesName || 'Series Plot';\n\n this.window = new ManagedWindow({\n id: `data-series-plot-${this.seriesId}`,\n title,\n icon: 'monitoring',\n content: this.contentEl,\n minWidth: 600,\n minHeight: 400,\n defaultWidth: 900,\n defaultHeight: 550,\n canMinimize: true,\n canMaximize: true,\n canResize: true,\n canDrag: true,\n onClose: () => this.#dispose(),\n });\n\n this.window.show();\n }\n\n #buildContent() {\n const container = document.createElement('div');\n container.className = 'twm-data-series-plot-container';\n\n // Actions bar (download buttons)\n const actionsBar = document.createElement('div');\n actionsBar.className = 'twm-data-series-plot-actions';\n\n const pngBtn = document.createElement('button');\n pngBtn.className = 'twm-btn-icon twm-has-tooltip';\n pngBtn.type = 'button';\n pngBtn.setAttribute('data-tooltip', 'Download plot as PNG');\n pngBtn.innerHTML = '<span class=\"material-symbols-outlined\">image</span>';\n pngBtn.addEventListener('click', () => this.#downloadPNG());\n\n const csvBtn = document.createElement('button');\n csvBtn.className = 'twm-btn-icon twm-has-tooltip';\n csvBtn.type = 'button';\n csvBtn.setAttribute('data-tooltip', 'Download data as CSV');\n csvBtn.innerHTML = '<span class=\"material-symbols-outlined\">download</span>';\n csvBtn.addEventListener('click', () => this.#downloadCSV());\n\n actionsBar.appendChild(pngBtn);\n actionsBar.appendChild(csvBtn);\n\n // Chart area\n const chartWrap = document.createElement('div');\n chartWrap.className = 'twm-data-series-plot-body';\n\n this.plotDiv = document.createElement('div');\n this.plotDiv.className = 'twm-data-series-plot-chart';\n chartWrap.appendChild(this.plotDiv);\n\n // Config panel\n const configPanel = this.#buildConfigPanel();\n chartWrap.appendChild(configPanel);\n\n container.appendChild(actionsBar);\n container.appendChild(chartWrap);\n\n return container;\n }\n\n // \u2500\u2500 Config panel \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n #buildConfigPanel() {\n const panel = document.createElement('div');\n panel.className = 'twm-data-series-plot-config';\n\n // X Column\n panel.appendChild(this.#buildSection('X Axis', this.#buildXColumnSelect()));\n\n // Y Columns\n this._yColumnsList = document.createElement('div');\n this._yColumnsList.className = 'twm-data-series-plot-config__columns';\n this.#populateYColumns();\n panel.appendChild(this.#buildSection('Y Axis', this._yColumnsList));\n\n // Chart type\n panel.appendChild(this.#buildSection('Chart Type', this.#buildSelect(\n CHART_TYPES,\n this._config.chartType,\n (val) => { this._config.chartType = val; this.#onConfigChanged(); }\n )));\n\n // Interpolation\n panel.appendChild(this.#buildSection('Interpolation', this.#buildSelect(\n INTERPOLATION_MODES,\n this._config.interpolation,\n (val) => { this._config.interpolation = val; this.#onConfigChanged(); }\n )));\n\n // Legend position\n panel.appendChild(this.#buildSection('Legend', this.#buildSelect(\n LEGEND_POSITIONS,\n this._config.legendPosition,\n (val) => { this._config.legendPosition = val; this.#onConfigChanged(); }\n )));\n\n // Show data points\n panel.appendChild(this.#buildSection('', this.#buildCheckbox(\n 'Show data points',\n this._config.showDataPoints,\n (checked) => { this._config.showDataPoints = checked; this.#onConfigChanged(); }\n )));\n\n return panel;\n }\n\n #buildSection(label, content) {\n const section = document.createElement('div');\n section.className = 'twm-data-series-plot-config__section';\n\n if (label) {\n const labelEl = document.createElement('div');\n labelEl.className = 'twm-data-series-plot-config__label';\n labelEl.textContent = label;\n section.appendChild(labelEl);\n }\n\n if (content instanceof HTMLElement) {\n section.appendChild(content);\n }\n\n return section;\n }\n\n #buildXColumnSelect() {\n const select = document.createElement('select');\n for (const header of this.headers) {\n if (!this._numericColumns.has(header)) continue;\n const opt = document.createElement('option');\n opt.value = header;\n opt.textContent = this.labelHeader(header);\n if (header === this._config.xColumn) opt.selected = true;\n select.appendChild(opt);\n }\n select.addEventListener('change', () => {\n this._config.xColumn = select.value;\n this.#onConfigChanged();\n });\n return select;\n }\n\n #populateYColumns() {\n const list = this._yColumnsList;\n list.innerHTML = '';\n\n const selectedSet = new Set(this._config.yColumns.map(c => c.header));\n\n for (const header of this.headers) {\n if (header === this._config.xColumn) continue;\n if (!this._numericColumns.has(header)) continue;\n\n const item = document.createElement('label');\n item.className = 'twm-data-series-plot-config__column-item';\n\n const cb = document.createElement('input');\n cb.type = 'checkbox';\n cb.checked = selectedSet.has(header);\n cb.addEventListener('change', () => this.#toggleYColumn(header, cb.checked));\n\n const colorSwatch = document.createElement('input');\n colorSwatch.type = 'color';\n colorSwatch.className = 'twm-data-series-plot-config__color-swatch';\n const existing = this._config.yColumns.find(c => c.header === header);\n colorSwatch.value = existing?.color || this.#nextColor(this.headers.indexOf(header));\n colorSwatch.addEventListener('input', () => this.#updateColumnColor(header, colorSwatch.value));\n\n const label = document.createElement('span');\n label.textContent = this.labelHeader(header);\n label.title = header;\n\n item.appendChild(cb);\n item.appendChild(colorSwatch);\n item.appendChild(label);\n list.appendChild(item);\n }\n }\n\n #toggleYColumn(header, checked) {\n if (checked) {\n if (!this._config.yColumns.find(c => c.header === header)) {\n const colorSwatch = this._yColumnsList.querySelector(\n `label:has(span[title=\"${header}\"]) input[type=\"color\"]`\n );\n this._config.yColumns.push({\n header,\n color: colorSwatch?.value || this.#nextColor(this.headers.indexOf(header)),\n });\n }\n } else {\n this._config.yColumns = this._config.yColumns.filter(c => c.header !== header);\n }\n this.#onConfigChanged();\n }\n\n #updateColumnColor(header, color) {\n const col = this._config.yColumns.find(c => c.header === header);\n if (col) {\n col.color = color;\n this.#onConfigChanged();\n }\n }\n\n #nextColor(index) {\n return getSeriesColor(index);\n }\n\n #buildSelect(options, value, onChange) {\n const select = document.createElement('select');\n for (const opt of options) {\n const el = document.createElement('option');\n el.value = opt;\n el.textContent = opt.charAt(0).toUpperCase() + opt.slice(1);\n if (opt === value) el.selected = true;\n select.appendChild(el);\n }\n select.addEventListener('change', () => onChange(select.value));\n return select;\n }\n\n #buildCheckbox(label, checked, onChange) {\n const wrapper = document.createElement('label');\n wrapper.className = 'twm-data-series-plot-config__column-item';\n\n const cb = document.createElement('input');\n cb.type = 'checkbox';\n cb.checked = checked;\n cb.addEventListener('change', () => onChange(cb.checked));\n\n const span = document.createElement('span');\n span.textContent = label;\n\n wrapper.appendChild(cb);\n wrapper.appendChild(span);\n return wrapper;\n }\n\n // \u2500\u2500 Data fetching \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n async #fetchData() {\n if (this._loading) return;\n this._loading = true;\n\n try {\n const response = await this.dataHub.fetchSeriesById(this.seriesId, { limit: null, offset: 0 });\n if (response?.ok) {\n this._fullData = {\n headers: response.headers || [],\n rows: response.rows || [],\n };\n } else {\n this.#notify('Plot', response?.error || 'Failed to load series data', 'error');\n }\n } catch (err) {\n this.services.logger?.warn?.('[DataSeriesPlotWindow] Fetch failed', err);\n this.#notify('Plot', 'Failed to load data', 'error');\n } finally {\n this._loading = false;\n }\n }\n\n #isNumericColumn(header) {\n if (!this._fullData) return false;\n const data = this.#extractColumnData(header);\n // Sample up to 20 non-empty values\n let numericCount = 0;\n let sampleCount = 0;\n for (const v of data) {\n if (v == null || v === '') continue;\n sampleCount++;\n if (Number.isFinite(Number(v))) numericCount++;\n if (sampleCount >= 20) break;\n }\n return sampleCount > 0 && numericCount / sampleCount >= 0.8;\n }\n\n // \u2500\u2500 Chart rendering \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n #extractColumnData(columnHeader) {\n if (!this._fullData) return [];\n\n const { headers, rows } = this._fullData;\n const idx = headers.indexOf(columnHeader);\n if (idx < 0) return [];\n\n const TIME_NAMES = new Set(['time', 'tDisp', 'year', 'date', 'timestamp']);\n const firstIsTime = TIME_NAMES.has(headers[0]);\n const colOffset = firstIsTime ? 1 : 0;\n\n return rows.map(row => {\n if (Array.isArray(row)) return row[idx];\n if (row && typeof row === 'object') {\n // Handle {tDisp, cols} format\n if (TIME_NAMES.has(columnHeader) && row.tDisp !== undefined) return row.tDisp;\n if (Array.isArray(row.cols)) return row.cols[idx - colOffset] ?? '';\n return row[columnHeader] ?? '';\n }\n return '';\n }).map(v => {\n const n = Number(v);\n return Number.isFinite(n) ? n : v;\n });\n }\n\n async #renderPlot() {\n if (!this.plotDiv || !window.Plotly || !this._fullData) return;\n\n const cfg = this._config;\n const xData = this.#extractColumnData(cfg.xColumn);\n\n if (xData.length === 0 || cfg.yColumns.length === 0) {\n // Show empty state\n if (this._chartCreated) {\n try { purge(this.plotDiv); } catch (e) { /* ignore */ }\n this._chartCreated = false;\n }\n this.plotDiv.innerHTML = '<div style=\"display:flex;align-items:center;justify-content:center;height:100%;color:#888;font-size:13px;\">Select columns to plot</div>';\n return;\n }\n\n // Clear empty state if present\n if (!this._chartCreated) {\n this.plotDiv.innerHTML = '';\n }\n\n const traces = [];\n\n for (let i = 0; i < cfg.yColumns.length; i++) {\n const col = cfg.yColumns[i];\n const yData = this.#extractColumnData(col.header);\n\n traces.push(buildTrace({\n chartType: cfg.chartType,\n x: xData,\n y: yData,\n name: this.labelHeader(col.header),\n color: col.color,\n lineWidth: 2,\n lineStyle: 'solid',\n interpolation: cfg.interpolation,\n yAxisId: 'y',\n showMarkers: cfg.showDataPoints,\n seriesIndex: i,\n }));\n }\n\n const legendAtTop = cfg.legendPosition === 'top';\n const legendAtBottom = cfg.legendPosition === 'bottom';\n\n const layout = {\n ...DARK_THEME_LAYOUT,\n autosize: true,\n margin: {\n l: 60, r: 40,\n b: legendAtBottom ? 80 : 50,\n t: legendAtTop ? 60 : 30,\n },\n };\n\n // Remove 3D scene from layout\n delete layout.scene;\n\n // X-axis\n layout.xaxis = {\n ...buildXAxisLayout({ title: this.labelHeader(cfg.xColumn) }),\n };\n\n // Unified hover\n layout.hovermode = 'x unified';\n layout.xaxis.showspikes = true;\n layout.xaxis.spikemode = 'across';\n layout.xaxis.spikethickness = 1;\n layout.xaxis.spikecolor = '#666666';\n layout.xaxis.spikedash = 'dot';\n\n // Y-axis\n layout.yaxis = buildYAxisLayout({\n axisId: 'yaxis',\n title: '',\n position: 'left',\n scaleType: 'linear',\n showGrid: true,\n });\n\n // Legend\n const legendConfig = buildLegendLayout(cfg.legendPosition);\n if (legendConfig.legend) {\n layout.legend = legendConfig.legend;\n }\n layout.showlegend = legendConfig.showlegend !== false;\n\n const plotConfig = {\n ...DEFAULT_CONFIG,\n responsive: true,\n displayModeBar: true,\n modeBarButtonsToRemove: ['lasso2d', 'select2d', 'toImage'],\n };\n\n try {\n await window.Plotly.newPlot(this.plotDiv, traces, layout, plotConfig);\n this._chartCreated = true;\n\n requestAnimationFrame(() => {\n try {\n window.Plotly.Plots.resize(this.plotDiv);\n } catch (e) { /* ignore */ }\n });\n } catch (e) {\n console.error('[DataSeriesPlotWindow] Chart render error:', e);\n }\n }\n\n // \u2500\u2500 Config change handler \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n #onConfigChanged() {\n this.#renderPlot();\n this.savePlotConfig?.(this.seriesId, { ...this._config, yColumns: [...this._config.yColumns] });\n }\n\n // \u2500\u2500 Resize observer \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n #setupResizeObserver() {\n if (!this.plotDiv) return;\n\n this._resizeObserver = createRafResizeObserver(() => {\n if (this.plotDiv && window.Plotly && this._chartCreated) {\n try {\n window.Plotly.Plots.resize(this.plotDiv);\n } catch (e) { /* ignore */ }\n }\n });\n\n this._resizeObserver.observe(this.plotDiv);\n }\n\n // \u2500\u2500 Downloads \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n async #downloadPNG() {\n if (!this.plotDiv || !window.Plotly || !this._chartCreated) {\n this.#notify('Download', 'Plot not ready', 'warn');\n return;\n }\n\n const filename = `${(this.seriesName || 'series').replace(/[^a-z0-9]/gi, '_')}.png`;\n\n try {\n const pngDataUrl = await window.Plotly.toImage(this.plotDiv, {\n format: 'png', width: 1920, height: 1080, scale: 2,\n });\n\n // null <=> no save dialog behind the capability (host.js) -> fall back.\n const dialogs = this._host?.dialogs;\n const result = dialogs\n ? await dialogs.saveFile({ data: pngDataUrl.split(',')[1], filename, kind: 'png' })\n : null;\n if (result) {\n if (result.ok) {\n this.#notify('Download', `Plot saved to ${result.path}`, 'success');\n } else if (!result.cancelled) {\n this.#notify('Download', result.error || 'Failed to save image', 'error');\n }\n } else {\n const link = document.createElement('a');\n link.href = pngDataUrl;\n link.download = filename;\n link.click();\n }\n } catch (err) {\n this.services.logger?.warn?.('[DataSeriesPlotWindow] PNG download failed', err);\n this.#notify('Download', 'Failed to generate PNG', 'error');\n }\n }\n\n async #downloadCSV() {\n if (!this._fullData || !this._fullData.rows.length) {\n this.#notify('Download', 'No data available', 'warn');\n return;\n }\n\n const cfg = this._config;\n const columns = [cfg.xColumn, ...cfg.yColumns.map(c => c.header)];\n const headers = columns.map(h => this.labelHeader(h));\n\n // Pre-extract all columns once\n const columnData = columns.map(col => this.#extractColumnData(col));\n const rowCount = this._fullData.rows.length;\n\n const lines = [headers.join(',')];\n for (let r = 0; r < rowCount; r++) {\n const vals = columnData.map(data => this.#csvEscape(data[r]));\n lines.push(vals.join(','));\n }\n const csv = lines.join('\\n');\n\n const filename = `${(this.seriesName || 'series').replace(/[^a-z0-9]/gi, '_')}_data.csv`;\n\n // null <=> no save dialog behind the capability (host.js) -> fall back.\n const dialogs = this._host?.dialogs;\n let result = null;\n if (dialogs) {\n try {\n result = await dialogs.saveFile({ data: csv, filename, kind: 'csv' });\n } catch (err) {\n this.#notify('Download', 'Failed to save file', 'error');\n return;\n }\n }\n if (result) {\n if (result.ok) {\n this.#notify('Download', `Saved to ${result.path}`, 'success');\n } else if (!result.cancelled) {\n this.#notify('Download', result.error || 'Failed to save file', 'error');\n }\n } else {\n const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });\n const url = URL.createObjectURL(blob);\n const link = document.createElement('a');\n link.href = url;\n link.download = filename;\n link.click();\n URL.revokeObjectURL(url);\n }\n }\n\n #csvEscape(value) {\n if (value == null) return '';\n const str = String(value);\n if (/[\",\\n]/.test(str)) return `\"${str.replace(/\"/g, '\"\"')}\"`;\n return str;\n }\n\n // \u2500\u2500 Notifications \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n #notify(title, message, severity = 'info') {\n this.services.eventBus?.emit?.('toast:show', { title, message, type: severity });\n }\n\n // \u2500\u2500 Disposal \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n #dispose() {\n if (this._resizeObserver) {\n this._resizeObserver.disconnect();\n this._resizeObserver = null;\n }\n\n if (this.plotDiv && window.Plotly) {\n try { purge(this.plotDiv); } catch (e) { /* ignore */ }\n }\n\n _openPlotWindows.delete(this.seriesId);\n\n this.window = null;\n this.contentEl = null;\n this.plotDiv = null;\n this._yColumnsList = null;\n this._chartCreated = false;\n this._fullData = null;\n this._numericColumns = null;\n this._config = null;\n }\n\n // \u2500\u2500 Default config factory \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * The config a series with no saved one opens with: the first numeric\n * column `classifyHeader` calls `'time'` is X (else the first numeric\n * column), and up to MAX_AUTO_COLUMNS other numeric columns are Y.\n *\n * Static, so it has no window: the header classifier is a parameter. A\n * window passes its own (`options.classifyHeader`); a caller that passes\n * none gets the default, which knows `t`/`time`/`step`/`period`.\n *\n * @param {string[]} headers\n * @param {Set<string>} [numericColumns] - absent => every column is numeric\n * @param {(header: string) => string} [classifyHeader=defaultClassifyHeader]\n */\n static createDefaultConfig(headers, numericColumns, classifyHeader = defaultClassifyHeader) {\n const isNumeric = numericColumns instanceof Set ? (h) => numericColumns.has(h) : () => true;\n\n // Find best X column (time-like, must be numeric)\n let xColumn = '';\n for (const h of headers) {\n if (isNumeric(h) && classifyHeader(h) === 'time') {\n xColumn = h;\n break;\n }\n }\n // Fallback: first numeric column\n if (!xColumn) {\n for (const h of headers) {\n if (isNumeric(h)) { xColumn = h; break; }\n }\n }\n\n // Pick first N non-X numeric columns for Y\n const yColumns = [];\n for (const h of headers) {\n if (h === xColumn) continue;\n if (!isNumeric(h)) continue;\n if (yColumns.length >= MAX_AUTO_COLUMNS) break;\n yColumns.push({ header: h, color: getSeriesColor(yColumns.length) });\n }\n\n return {\n xColumn,\n yColumns,\n chartType: 'line',\n showDataPoints: false,\n interpolation: 'linear',\n legendPosition: 'top',\n };\n }\n}\n\n// \u2500\u2500 Singleton window management \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst _openPlotWindows = new Map();\n\nexport async function openDataSeriesPlotWindow(options) {\n const { seriesId } = options;\n let win = _openPlotWindows.get(seriesId);\n if (win?.window?.isVisible) {\n win.window.bringToFront();\n return win;\n }\n\n win = new DataSeriesPlotWindow(options);\n _openPlotWindows.set(seriesId, win);\n await win.show();\n return win;\n}\n\nexport function closeDataSeriesPlotWindow(seriesId) {\n const win = _openPlotWindows.get(seriesId);\n if (win) {\n win.close();\n _openPlotWindows.delete(seriesId);\n }\n}\n\nexport default DataSeriesPlotWindow;\n", "/**\n * plot_config_editor.js \u2014 edit a plot config (plot_config.js: `{ layout, subplots }`)\n * in place.\n *\n * const editor = renderPlotConfigEditor(el, plotConfig, {\n * onChange: () => grid.update({ plotConfig }),\n * seriesProvider: ({ value, role, subplot }) => myNames(value), // or a promise\n * });\n * editor.dispose();\n *\n * It draws a layout picker, a tab per subplot slot (when the layout has more\n * than one) and, for the subplot in front: its title; its chart type, legend,\n * interpolation, NaN handling and data points; its x axis (time, or a series);\n * its y axes (label, scale, position, min, max, step) and their series (each a\n * block: a header line of picker, label and remove over one label column of\n * colour, width, dash, stack group, the embedder's own fields\n * and, with `overlaySources`, an overlay source); its z axis while the chart\n * type is 3D; its reference lines and shaded bands; and the toggles of any\n * injected `seriesTransforms` (series_transforms.js).\n *\n * Every edit writes the config object it was given and then calls\n * `onChange(config)`. A layout with more slots than the config has subplots\n * gets default subplots (makeDefaultSubplot) for the new slots; a layout with\n * fewer keeps the extra subplots, unseen, so switching back restores them.\n *\n * A 3D chart type gives the subplot a z axis and uses its first y axis only:\n * while one is chosen the other y axes are shown disabled and no y axis can be\n * added. A 2D chart type removes the z axis (`zAxis: null`, as in\n * makeDefaultSubplot).\n *\n * Series names come from `seriesProvider`, through an AutocompleteField per\n * picker: it is asked `{ value, role, subplot }` (`role` is 'series', 'x' or\n * 'z') and answers AutocompleteField items (`{ label, insertText?, type? }`)\n * or a promise of them. One that throws or rejects shows no suggestions and\n * leaves what was typed as it is. Nothing here knows what a series is.\n *\n * It fits PlotPopoutWindow's `configRenderer` slot, which disposes what the\n * renderer returns when the window closes (openPlotPopoutWindow is async):\n *\n * const win = await openPlotPopoutWindow({ id, title, plotConfig, getResults, services,\n * configRenderer: (el) => renderPlotConfigEditor(el, plotConfig,\n * { onChange: () => win.updateConfig(plotConfig) }) });\n */\n\nimport { AutocompleteField } from '../ui/components/autocomplete_field.js';\nimport { ColorField } from '../ui/components/color_field.js';\nimport { ToggleField } from '../ui/components/toggle_field.js';\nimport { selectBox } from '../ui/components/choice_controls.js';\nimport { createTabs } from '../ui/components/tabs.js';\nimport { CHART_TYPES_2D, CHART_TYPES_3D, is3DChart } from './chart_types.js';\nimport { PLOT_LAYOUTS, makeDefaultSubplot, makeDefaultYAxis, makeDefaultSeries } from './plot_config.js';\nimport { renderSeriesTransformControls } from './series_transforms.js';\n\nconst CHART_TYPE_OPTIONS = Object.freeze([\n { group: '2D', options: CHART_TYPES_2D },\n { group: '3D', options: CHART_TYPES_3D },\n]);\nconst SCALES = Object.freeze([{ value: 'linear', label: 'Linear' }, { value: 'log', label: 'Log' }]);\nconst POSITIONS = Object.freeze([{ value: 'left', label: 'Left' }, { value: 'right', label: 'Right' }]);\nconst DASHES = Object.freeze([\n { value: 'solid', label: 'Solid' }, { value: 'dashed', label: 'Dashed' }, { value: 'dotted', label: 'Dotted' },\n]);\nconst LEGEND_POSITIONS = Object.freeze([\n { value: 'hidden', label: 'Hidden' }, { value: 'top', label: 'Top' }, { value: 'bottom', label: 'Bottom' },\n { value: 'left', label: 'Left' }, { value: 'right', label: 'Right' },\n]);\n// 'auto' is drawn as 'linear' (buildTrace); it is listed so a config saved with it\n// shows it and keeps it.\nconst INTERPOLATIONS = Object.freeze([\n { value: 'auto', label: 'Auto' },\n { value: 'linear', label: 'Linear' }, { value: 'step', label: 'Step' }, { value: 'cubic', label: 'Cubic spline' },\n]);\nconst NAN_STRATEGIES = Object.freeze([\n { value: 'gap', label: 'Gap' }, { value: 'zero', label: 'Zero' },\n { value: 'hold', label: 'Hold last value' }, { value: 'interpolate', label: 'Interpolate' },\n]);\nconst X_SOURCES = Object.freeze([{ value: 'time', label: 'Time' }, { value: 'series', label: 'Series' }]);\nconst OVERLAY_MODES = Object.freeze([\n { value: 'markers+lines', label: 'Markers and lines' }, { value: 'markers', label: 'Markers' },\n { value: 'lines', label: 'Lines' },\n]);\n\nfunction el(tag, className, text) {\n const node = document.createElement(tag);\n if (className) node.className = className;\n if (text != null) node.textContent = String(text);\n return node;\n}\n\nfunction button(text, onClick, { title = null, className = 'twm-btn twm-plot-config__add' } = {}) {\n const b = el('button', className, text);\n b.type = 'button';\n if (title) b.title = title;\n b.addEventListener('click', () => { if (!b.disabled) onClick(); });\n return b;\n}\n\nfunction removeButton(title, onClick) {\n const b = button('', onClick, { title, className: 'twm-btn-icon twm-plot-config__remove' });\n b.setAttribute('aria-label', title);\n b.appendChild(el('span', 'material-symbols-outlined', 'close'));\n return b;\n}\n\nlet hintCount = 0;\n\n/**\n * A labelled control on the editor's label column. A single input or select\n * is wrapped in a `<label>`; a compound one (a picker, a colour field) is not,\n * so a click on its text does not land on whichever of its inputs comes\n * first. A `hint` is drawn under the control, in the control's column, and\n * outside any `<label>`: the row is then a `<div>` whose label text is the\n * `<label for>` of a single control, and the hint describes the control\n * (`aria-describedby`) without being part of its name.\n */\nfunction field(label, control, { hint = null } = {}) {\n const single = control.matches('input, select, textarea');\n const hinted = hint != null && hint !== '';\n if (!hinted) {\n const row = el(single ? 'label' : 'div', 'twm-plot-config__field');\n row.append(el('span', 'twm-plot-config__label', label), control);\n return row;\n }\n const row = el('div', 'twm-plot-config__field twm-plot-config__field--hinted');\n const text = el(single ? 'label' : 'span', 'twm-plot-config__label', label);\n const note = el('span', 'twm-plot-config__hint', hint);\n note.id = `twm-plot-config-hint-${++hintCount}`;\n if (single) {\n if (!control.id) control.id = `${note.id}-control`;\n text.htmlFor = control.id;\n }\n const target = single ? control : (control.querySelector('input, select, textarea') ?? control);\n const describedBy = target.getAttribute('aria-describedby');\n target.setAttribute('aria-describedby', describedBy ? `${describedBy} ${note.id}` : note.id);\n row.append(text, control, note);\n return row;\n}\n\nfunction textInput(value, onSet, { placeholder = '', label = null } = {}) {\n const input = el('input', 'twm-plot-config__input');\n input.type = 'text';\n input.value = value ?? '';\n input.placeholder = placeholder;\n if (label) input.setAttribute('aria-label', label);\n input.addEventListener('input', () => onSet(input.value));\n return input;\n}\n\n/** A number box: empty (or not a number) sets null. */\nfunction numberInput(value, onSet, { placeholder = 'Auto', label = null, min = null, max = null, step = 'any' } = {}) {\n const input = el('input', 'twm-plot-config__input twm-plot-config__input--number');\n input.type = 'number';\n input.step = String(step);\n if (min != null) input.min = String(min);\n if (max != null) input.max = String(max);\n input.value = value == null ? '' : String(value);\n input.placeholder = placeholder;\n if (label) input.setAttribute('aria-label', label);\n input.addEventListener('change', () => {\n const n = parseFloat(input.value);\n onSet(Number.isFinite(n) ? n : null);\n });\n return input;\n}\n\n/** A select over choice options; `onPick(value)` gets the option's own value. */\nfunction select(options, value, onPick, { label = null, empty = null } = {}) {\n const s = selectBox(options, value, {\n label: label ?? '', empty,\n className: 'twm-plot-config__input twm-plot-config__input--select',\n });\n s.box.addEventListener('change', () => onPick(s.valueOf()));\n return s.box;\n}\n\nfunction makeZAxis() {\n return { label: '', scale: 'linear', min: null, max: null, step: null, variable: '' };\n}\n\nlet editorCount = 0;\n\n/**\n * Edit `config` in place.\n *\n * @param {HTMLElement} container - Receives one `.twm-plot-config` child.\n * @param {{ layout: string, subplots: Object[] }} config - Already migrated (migrateData).\n * @param {Object} [options]\n * @param {Function} [options.onChange] - `(config)` after every edit.\n * @param {Function} [options.seriesProvider] - `({ value, role, subplot }) => items |\n * Promise<items>`: the pickers' suggestions (AutocompleteField items).\n * @param {Function} [options.overlaySources] - `() => options | Promise<options>`: choice\n * options (`{ value, label }`, or `{ group, options }`) for a series' overlay\n * source, written to `series.overlay.source`. Absent: no overlay controls.\n * @param {Object} [options.seriesTransforms] - `{ [id]: transform }` (series_transforms.js);\n * their toggles are drawn per subplot.\n * @param {Function} [options.extraSeriesFields] - `(el, series, { subplot, axis, onChange, field })\n * => { dispose? } | specs[] | void`: fields of the embedder's own in each series\n * block, on the series' label column. It may draw into `el` (with\n * `field(label, control, { hint })` for rows that line up) and return a handle,\n * or return an ARRAY of field specs `{ label: string, control: HTMLElement,\n * hint? }` / `{ label: string, render(slot) => { dispose? } | void, hint? }`,\n * laid out after anything drawn into `el`. A bad spec is skipped with a warning.\n * @param {Object} [options.logger] - Given to every AutocompleteField (provider errors);\n * its `warn` also hears a part (an `extraSeriesFields` handle, say) that throws on dispose.\n * @returns {{ dispose: Function, refresh: Function, selectSubplot: Function,\n * readonly activeSubplot: number }}\n */\nfunction renderPlotConfigEditor(container, config, options = {}) {\n const opts = {\n onChange: null,\n seriesProvider: null,\n overlaySources: null,\n seriesTransforms: null,\n extraSeriesFields: null,\n logger: null,\n ...options,\n };\n const editorId = `twm-plot-config-${++editorCount}`;\n let fieldCount = 0;\n let active = 0;\n let disposed = false;\n /** Teardown for everything the subplot body created; run before each redraw. */\n let bodyDisposers = [];\n /** Bumped on each body redraw, so an overlay-source answer for an old body is dropped. */\n let generation = 0;\n\n const notify = () => { if (!disposed) opts.onChange?.(config); };\n const track = (fn) => { bodyDisposers.push(fn); };\n const template = () => PLOT_LAYOUTS[config.layout] || PLOT_LAYOUTS['1x1'];\n const slots = () => template().slots;\n\n function ensureSubplots() {\n if (!Array.isArray(config.subplots)) config.subplots = [];\n while (config.subplots.length < slots()) {\n config.subplots.push(makeDefaultSubplot(config.subplots.length));\n }\n }\n\n const root = el('div', 'twm-plot-config');\n\n // \u2500\u2500 layout picker \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const layoutButtons = new Map();\n const picker = el('div', 'twm-plot-config__layouts');\n picker.setAttribute('role', 'group');\n picker.setAttribute('aria-label', 'Layout');\n for (const [key, tmpl] of Object.entries(PLOT_LAYOUTS)) {\n const b = button(tmpl.label, () => setLayout(key), { className: 'twm-btn twm-plot-config__layout' });\n layoutButtons.set(key, b);\n picker.appendChild(b);\n }\n const syncLayoutButtons = () => {\n for (const [key, b] of layoutButtons) {\n const on = key === config.layout;\n b.setAttribute('aria-pressed', on ? 'true' : 'false');\n b.classList.toggle('twm-plot-config__layout--on', on);\n }\n };\n root.appendChild(section('Layout', picker));\n\n // \u2500\u2500 subplot tabs and the body \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const tabsHost = el('div', 'twm-plot-config__tabs');\n const body = el('div', 'twm-plot-config__body');\n root.append(tabsHost, body);\n container.appendChild(root);\n\n ensureSubplots();\n const tabItems = () => Array.from({ length: slots() }, (_, i) => ({ id: i, label: `Subplot ${i + 1}` }));\n const tabs = createTabs(tabsHost, {\n items: tabItems(),\n active: 0,\n compact: true,\n label: 'Subplots',\n onSelect: (id) => {\n if (id == null || id === active) return;\n active = id;\n renderBody();\n },\n });\n const syncChrome = () => {\n syncLayoutButtons();\n tabs.update(tabItems(), active);\n tabsHost.hidden = slots() < 2;\n };\n\n function setLayout(key) {\n if (disposed || key === config.layout) return;\n config.layout = key;\n ensureSubplots();\n active = Math.min(active, slots() - 1);\n syncChrome();\n renderBody();\n notify();\n }\n\n function section(title, ...children) {\n const s = el('section', 'twm-plot-config__section');\n s.appendChild(el('h4', 'twm-plot-config__heading', title));\n for (const child of children) if (child) s.appendChild(child);\n return s;\n }\n\n function disposeBody() {\n generation += 1;\n const disposers = bodyDisposers;\n bodyDisposers = [];\n for (const fn of disposers) {\n try { fn(); } catch (error) {\n opts.logger?.warn?.('[plot config editor] a field failed to dispose', error);\n }\n }\n }\n\n // \u2500\u2500 shared controls \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /** A series-name picker writing `target[key]`. */\n function seriesPicker(target, key, role, sp, placeholder) {\n let alive = true;\n const ac = new AutocompleteField({\n fieldId: `${editorId}-field-${++fieldCount}`,\n field: { placeholder, value: target[key] ?? '' },\n skipNamespacePrefix: true,\n logger: opts.logger,\n provider: ({ value }) => (opts.seriesProvider\n ? opts.seriesProvider({ value, role, subplot: sp })\n : []),\n onChange: (value) => {\n // The field reports its value again on blur, after a redraw\n // may have disposed it: a disposed field writes nothing.\n if (!alive || (target[key] ?? '') === value) return;\n target[key] = value;\n notify();\n },\n });\n track(() => { alive = false; ac.dispose(); });\n const node = ac.render();\n node.classList.add('twm-plot-config__picker');\n return node;\n }\n\n /** `keep` takes its teardown: the body's (track), or a narrower redraw's. */\n function colorField(value, onSet, label, keep = track) {\n const f = new ColorField({\n value: value ?? '',\n label,\n onChange: (v) => { if (v !== null) { onSet(v || null); notify(); } },\n });\n keep(() => f.dispose());\n return f.render();\n }\n\n function toggle(label, checked, onSet) {\n const t = new ToggleField({ label, checked, compact: true, onChange: (on) => { onSet(on); notify(); } });\n track(() => t.dispose());\n return t.render();\n }\n\n /** Rows of objects under `sp[key]`, each with a remove button, and an add button. */\n function listEditor(sp, key, { addLabel, makeItem, fields }) {\n if (!Array.isArray(sp[key])) sp[key] = [];\n const items = sp[key];\n const wrap = el('div', 'twm-plot-config__list');\n items.forEach((item, i) => {\n const row = el('div', 'twm-plot-config__row');\n row.append(...fields(item), removeButton('Remove', () => {\n items.splice(i, 1);\n renderBody();\n notify();\n }));\n wrap.appendChild(row);\n });\n wrap.appendChild(button(addLabel, () => {\n items.push(makeItem());\n renderBody();\n notify();\n }));\n return wrap;\n }\n\n // \u2500\u2500 the subplot in front \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n function normalise(sp) {\n if (!sp.xAxis) sp.xAxis = { useTime: true, variable: '' };\n if (!Array.isArray(sp.yAxes) || !sp.yAxes.length) sp.yAxes = [makeDefaultYAxis(0)];\n for (const axis of sp.yAxes) if (!Array.isArray(axis.series)) axis.series = [];\n if (is3DChart(sp.chartType) && !sp.zAxis) sp.zAxis = makeZAxis();\n }\n\n function renderBody() {\n if (disposed) return;\n disposeBody();\n body.replaceChildren();\n const sp = config.subplots[active];\n if (!sp) return;\n normalise(sp);\n const is3D = is3DChart(sp.chartType);\n let sources = null;\n const overlaySources = () => (sources ??= Promise.resolve().then(() => opts.overlaySources()));\n\n // Element.append() writes a null child as the text \"null\": a section the\n // subplot does not have (the z axis of a 2D chart, the transforms when\n // there are none) is left out, not appended.\n body.append(...[\n section('Subplot', field('Title', textInput(sp.displayName, (v) => { sp.displayName = v; notify(); },\n { placeholder: 'Untitled' }))),\n section('Display', ...displayFields(sp)),\n section('X axis', ...xAxisFields(sp)),\n section('Y axes', yAxesEditor(sp, is3D, overlaySources)),\n is3D ? section('Z axis', ...zAxisFields(sp)) : null,\n section('Reference lines', referenceLinesEditor(sp)),\n section('Shaded bands', shadedBandsEditor(sp)),\n transformsSection(sp),\n ].filter(Boolean));\n }\n\n function displayFields(sp) {\n return [\n field('Chart type', select(CHART_TYPE_OPTIONS, sp.chartType ?? 'line', (v) => {\n const was3D = is3DChart(sp.chartType);\n sp.chartType = v;\n const now3D = is3DChart(v);\n if (now3D) sp.zAxis ??= makeZAxis();\n else sp.zAxis = null;\n if (was3D !== now3D) renderBody();\n notify();\n })),\n field('Legend', select(LEGEND_POSITIONS, sp.legendPosition ?? 'hidden',\n (v) => { sp.legendPosition = v; notify(); })),\n field('Interpolation', select(INTERPOLATIONS, sp.interpolation ?? 'linear',\n (v) => { sp.interpolation = v; notify(); })),\n field('Missing values', select(NAN_STRATEGIES, sp.nanHandling ?? 'gap',\n (v) => { sp.nanHandling = v; notify(); })),\n toggle('Data points', sp.showDataPoints === true, (on) => { sp.showDataPoints = on; }),\n ];\n }\n\n function xAxisFields(sp) {\n const useTime = sp.xAxis.useTime !== false;\n const out = [field('Against', select(X_SOURCES, useTime ? 'time' : 'series', (v) => {\n sp.xAxis.useTime = v === 'time';\n renderBody();\n notify();\n }))];\n if (!useTime) {\n out.push(\n field('Series', seriesPicker(sp.xAxis, 'variable', 'x', sp, 'X series')),\n field('Label', textInput(sp.xAxis.label, (v) => { sp.xAxis.label = v; notify(); },\n { placeholder: sp.xAxis.variable || 'Axis title' })),\n );\n }\n return out;\n }\n\n function zAxisFields(sp) {\n const z = sp.zAxis;\n return [\n field('Series', seriesPicker(z, 'variable', 'z', sp, 'Z series')),\n field('Label', textInput(z.label, (v) => { z.label = v; notify(); }, { placeholder: 'Axis title' })),\n field('Scale', select(SCALES, z.scale ?? 'linear', (v) => { z.scale = v; notify(); })),\n field('Min', numberInput(z.min, (v) => { z.min = v; notify(); })),\n field('Max', numberInput(z.max, (v) => { z.max = v; notify(); })),\n field('Step', numberInput(z.step, (v) => { z.step = v; notify(); })),\n ];\n }\n\n function yAxesEditor(sp, is3D, overlaySources) {\n const wrap = el('div', 'twm-plot-config__axes');\n sp.yAxes.forEach((axis, i) => wrap.appendChild(yAxisEditor(sp, axis, i, is3D, overlaySources)));\n const add = button('Add Y axis', () => {\n sp.yAxes.push(makeDefaultYAxis(sp.yAxes.length));\n renderBody();\n notify();\n }, { title: is3D ? 'A 3D chart has one Y axis' : null });\n add.disabled = is3D;\n wrap.appendChild(add);\n return wrap;\n }\n\n function yAxisEditor(sp, axis, index, is3D, overlaySources) {\n const group = el('fieldset', 'twm-plot-config__axis');\n const legend = el('legend', 'twm-plot-config__axis-title', `Y axis ${index + 1}`);\n group.appendChild(legend);\n if (sp.yAxes.length > 1) {\n // Inside the legend, so it stays usable while the axis is disabled.\n legend.appendChild(removeButton('Remove Y axis', () => {\n sp.yAxes.splice(index, 1);\n renderBody();\n notify();\n }));\n }\n if (is3D && index > 0) {\n group.disabled = true;\n group.classList.add('twm-plot-config__axis--disabled');\n group.title = 'A 3D chart uses the first Y axis only';\n }\n\n const fields = el('div', 'twm-plot-config__axis-fields');\n fields.append(\n field('Label', textInput(axis.label, (v) => { axis.label = v; notify(); }, { placeholder: 'Axis title' })),\n field('Scale', select(SCALES, axis.scale ?? 'linear', (v) => { axis.scale = v; notify(); })),\n field('Position', select(POSITIONS, axis.position ?? 'left', (v) => { axis.position = v; notify(); })),\n field('Min', numberInput(axis.min, (v) => { axis.min = v; notify(); })),\n field('Max', numberInput(axis.max, (v) => { axis.max = v; notify(); })),\n field('Step', numberInput(axis.step, (v) => { axis.step = v; notify(); })),\n );\n group.appendChild(fields);\n\n const list = el('div', 'twm-plot-config__series-list');\n if (!axis.series.length) list.appendChild(el('div', 'twm-plot-config__empty', 'No series.'));\n for (const s of axis.series) list.appendChild(seriesRow(sp, axis, s, overlaySources));\n group.appendChild(list);\n\n group.appendChild(button('Add series', () => {\n const count = sp.yAxes.reduce((n, a) => n + (a.series?.length ?? 0), 0);\n axis.series.push(makeDefaultSeries(count));\n renderBody();\n notify();\n }));\n return group;\n }\n\n /**\n * One series as a block: a header line (series picker, legend label,\n * remove) over its fields on the editor's label column (colour, width,\n * dash, stack group, the embedder's fields, overlay).\n */\n function seriesRow(sp, axis, s, overlaySources) {\n const row = el('div', 'twm-plot-config__series');\n const head = el('div', 'twm-plot-config__series-head');\n head.append(\n seriesPicker(s, 'variable', 'series', sp, 'Series'),\n textInput(s.label, (v) => { s.label = v; notify(); }, { placeholder: 'Legend label', label: 'Legend label' }),\n removeButton('Remove series', () => {\n const i = axis.series.indexOf(s);\n if (i >= 0) axis.series.splice(i, 1);\n renderBody();\n notify();\n }),\n );\n\n const fields = el('div', 'twm-plot-config__series-fields');\n fields.append(\n field('Colour', colorField(s.color, (v) => { if (v) s.color = v; else delete s.color; }, 'Colour')),\n field('Width', numberInput(s.lineWidth ?? 2, (v) => { s.lineWidth = v ?? 2; notify(); },\n { placeholder: '2', label: 'Line width', min: 1, max: 10, step: 1 })),\n field('Dash', select(DASHES, s.lineStyle ?? 'solid', (v) => { s.lineStyle = v; notify(); }, { label: 'Dash' })),\n field('Stack group', textInput(s.stackGroup, (v) => { s.stackGroup = v || null; notify(); },\n { placeholder: 'None', label: 'Stack group' })),\n );\n if (opts.extraSeriesFields) fields.appendChild(extraFields(sp, axis, s));\n if (opts.overlaySources) fields.appendChild(overlayEditor(s, overlaySources));\n row.append(head, fields);\n return row;\n }\n\n const warn = (...args) => (opts.logger?.warn ? opts.logger.warn(...args) : console.warn(...args));\n\n /**\n * The embedder's fields of a series: whatever `extraSeriesFields` draws\n * into the box (which sits in the series' field column), then, when it\n * returns an ARRAY, those field specs laid out by `field()`. Anything\n * else it returns is a handle, disposed when the row goes (`{ dispose }`).\n * A spec that is not `{ label, control: Element }` or\n * `{ label, render(slot) }` is skipped with a warning, as is a `render`\n * that throws: one bad field never leaves half a body.\n */\n function extraFields(sp, axis, s) {\n const extra = el('div', 'twm-plot-config__series-extra');\n const result = opts.extraSeriesFields(extra, s, { subplot: sp, axis, onChange: notify, field });\n if (!Array.isArray(result)) {\n if (typeof result?.dispose === 'function') track(() => result.dispose());\n return extra;\n }\n for (const spec of result) {\n const label = spec?.label;\n let control = null;\n if (typeof label !== 'string') {\n warn('[plot config editor] an extraSeriesFields spec has no label; skipped', spec);\n continue;\n }\n if (spec.control != null) {\n if (!(spec.control instanceof Element)) {\n warn('[plot config editor] an extraSeriesFields spec control is not an Element; skipped', spec);\n continue;\n }\n control = spec.control;\n } else if (typeof spec.render === 'function') {\n control = el('div', 'twm-plot-config__slot');\n try {\n const handle = spec.render(control);\n if (typeof handle?.dispose === 'function') track(() => handle.dispose());\n } catch (error) {\n warn('[plot config editor] an extraSeriesFields spec failed to render; skipped', error);\n continue;\n }\n } else {\n warn('[plot config editor] an extraSeriesFields spec has neither control nor render; skipped', spec);\n continue;\n }\n extra.appendChild(field(label, control, { hint: spec.hint }));\n }\n return extra;\n }\n\n function overlayEditor(s, overlaySources) {\n const wrap = el('div', 'twm-plot-config__overlay');\n const head = el('div', 'twm-plot-config__overlay-source', 'Loading overlay sources\u2026');\n const details = el('div', 'twm-plot-config__overlay-fields');\n wrap.append(head, details);\n\n // The details' colour field is torn down on each redraw of the\n // details, not left for the body's next redraw.\n let detailDisposers = [];\n const disposeDetails = () => {\n const fns = detailDisposers;\n detailDisposers = [];\n for (const fn of fns) {\n try { fn(); } catch (error) {\n opts.logger?.warn?.('[plot config editor] a field failed to dispose', error);\n }\n }\n };\n track(disposeDetails);\n const keep = (fn) => { detailDisposers.push(fn); };\n const drawDetails = () => {\n disposeDetails();\n details.replaceChildren();\n const ov = s.overlay;\n if (!ov) return;\n details.append(\n field('Label', textInput(ov.label, (v) => { ov.label = v; notify(); },\n { placeholder: `${s.label || s.variable || 'Series'} (actual)` })),\n field('Colour', colorField(ov.color, (v) => { if (v) ov.color = v; else delete ov.color; }, 'Overlay colour', keep)),\n field('Dash', select(DASHES, ov.lineStyle ?? 'dashed', (v) => { ov.lineStyle = v; notify(); })),\n field('Width', numberInput(ov.lineWidth ?? 2, (v) => { ov.lineWidth = v ?? 2; notify(); },\n { placeholder: '2', min: 1, max: 10, step: 1 })),\n field('Mode', select(OVERLAY_MODES, ov.mode ?? 'markers+lines', (v) => { ov.mode = v; notify(); })),\n );\n };\n\n const gen = generation;\n overlaySources().then((list) => {\n if (disposed || gen !== generation) return;\n head.replaceChildren(field('Overlay', select(Array.isArray(list) ? list : [], s.overlay?.source, (v) => {\n if (v === undefined) delete s.overlay;\n else s.overlay = { ...(s.overlay ?? {}), source: v };\n drawDetails();\n notify();\n }, { empty: 'None' })));\n drawDetails();\n }, () => {\n if (disposed || gen !== generation) return;\n head.textContent = 'Could not load overlay sources.';\n head.classList.add('twm-plot-config__overlay-source--error');\n });\n return wrap;\n }\n\n function referenceLinesEditor(sp) {\n const axisOptions = sp.yAxes.map((a, i) => ({ value: a.id, label: a.label || `Y axis ${i + 1}` }));\n return listEditor(sp, 'referenceLines', {\n addLabel: 'Add reference line',\n makeItem: () => ({ yAxisId: sp.yAxes[0].id, value: 0, label: '', lineStyle: 'dashed' }),\n fields: (line) => [\n select(axisOptions, line.yAxisId ?? sp.yAxes[0].id, (v) => { line.yAxisId = v; notify(); },\n { label: 'Axis' }),\n numberInput(line.value, (v) => { line.value = v; notify(); }, { placeholder: 'Value', label: 'Value' }),\n textInput(line.label, (v) => { line.label = v; notify(); }, { placeholder: 'Label', label: 'Label' }),\n colorField(line.color, (v) => { if (v) line.color = v; else delete line.color; }, 'Colour'),\n select(DASHES, line.lineStyle ?? 'dashed', (v) => { line.lineStyle = v; notify(); }, { label: 'Dash' }),\n ],\n });\n }\n\n function shadedBandsEditor(sp) {\n return listEditor(sp, 'shadedBands', {\n addLabel: 'Add shaded band',\n makeItem: () => ({ x0: null, x1: null, label: '' }),\n fields: (band) => [\n numberInput(band.x0, (v) => { band.x0 = v; notify(); }, { placeholder: 'From', label: 'From' }),\n numberInput(band.x1, (v) => { band.x1 = v; notify(); }, { placeholder: 'To', label: 'To' }),\n textInput(band.label, (v) => { band.label = v; notify(); }, { placeholder: 'Label', label: 'Label' }),\n colorField(band.color, (v) => { if (v) band.color = v; else delete band.color; }, 'Colour'),\n numberInput(band.opacity, (v) => {\n if (v == null) delete band.opacity; else band.opacity = v;\n notify();\n }, { placeholder: 'Opacity', label: 'Opacity', min: 0, max: 1, step: 0.05 }),\n ],\n });\n }\n\n function transformsSection(sp) {\n if (!Object.keys(opts.seriesTransforms ?? {}).length) return null;\n const s = section('Derived series');\n const controls = renderSeriesTransformControls(s, sp, opts.seriesTransforms, { onChange: notify });\n track(() => controls.dispose());\n return s;\n }\n\n syncChrome();\n renderBody();\n\n return {\n /** Dispose every field it created and remove the editor. Later calls do nothing. */\n dispose() {\n if (disposed) return;\n disposed = true;\n disposeBody();\n tabs.destroy();\n root.remove();\n },\n /** Redraw from the config, after it was changed from outside. */\n refresh() {\n if (disposed) return;\n ensureSubplots();\n active = Math.min(active, slots() - 1);\n syncChrome();\n renderBody();\n },\n /** Bring subplot `index` to the front. */\n selectSubplot(index) {\n if (disposed || !Number.isInteger(index) || index < 0 || index >= slots()) return;\n tabs.select(index);\n },\n /** The index of the subplot in front. */\n get activeSubplot() { return active; },\n };\n}\n\nexport { renderPlotConfigEditor };\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["/**\n * Data Series Plot Window\n *\n * Opens a data series in a ManagedWindow with a split layout:\n * - Left: Plotly chart\n * - Right: Configuration panel (column selection, chart type, styling)\n *\n * Config is persisted in workspace metadata via savePlotConfig callback.\n */\n\nimport { ManagedWindow } from '../ui/components/managed_window.js';\nimport {\n ensurePlotly,\n purge,\n DARK_THEME_LAYOUT,\n DEFAULT_CONFIG,\n getSeriesColor,\n} from './plotly_wrapper.js';\nimport {\n buildTrace,\n buildYAxisLayout,\n buildXAxisLayout,\n buildLegendLayout,\n} from './chart_types.js';\n// The two header helpers are INJECTED, not imported.\n//\n// They used to come from ui/js/ui/utils/header_utils.js, which decides whether a\n// column is a STOCK or a FLOW by looking for `asset`, `liability`, `equity`,\n// `reserves`, `deposit`. That is stock-flow-consistent accounting, not charting,\n// and that one import was the last thing keeping this window out of @flexdesk/charts.\n//\n// The defaults below are what a chart library can honestly know about a column\n// name: whether it looks like a time axis, and nothing else.\n\n/** A column called `t`/`time`/`step`/`period` is the x-axis. Everything else is data. */\nconst defaultClassifyHeader = (header) => {\n const lower = String(header ?? '').trim().toLowerCase();\n return (lower === 't' || lower === 'time' || lower === 'step' || lower === 'period')\n ? 'time' : 'indicator';\n};\n\n/** No convention to strip, absent one being supplied. */\nconst defaultLabelHeader = (header) => header;\nimport { createRafResizeObserver } from '../ui/utils/raf_resize_observer.js';\n\nconst INTERPOLATION_MODES = ['linear', 'step', 'cubic'];\nconst CHART_TYPES = ['line', 'scatter', 'bar', 'area', 'area-stacked'];\nconst LEGEND_POSITIONS = ['top', 'bottom', 'right', 'hidden'];\nconst MAX_AUTO_COLUMNS = 3;\n\nclass DataSeriesPlotWindow {\n /**\n * @param {Object} options\n * @param {string} options.seriesId\n * @param {string} options.seriesName\n * @param {string} options.datasetName\n * @param {string[]} options.headers - Column headers from the series\n * @param {Object} options.dataHub - DataHub service for fetching data\n * @param {Object} options.services - { eventBus, logger }\n * @param {Object} [options.host] - A Host (ui/js/host/host.js). Its `dialogs`\n * capability backs Export PNG/CSV; absent => Blob download fallback.\n * @param {Function} options.getPlotConfig - (seriesId) => config | null\n * @param {Function} options.savePlotConfig - (seriesId, config) => void\n */\n constructor({ seriesId, seriesName, datasetName, headers, dataHub, services, host,\n getPlotConfig, savePlotConfig,\n classifyHeader = defaultClassifyHeader,\n labelHeader = defaultLabelHeader }) {\n this.classifyHeader = classifyHeader;\n this.labelHeader = labelHeader;\n this.seriesId = seriesId;\n this.seriesName = seriesName;\n this.datasetName = datasetName;\n this.headers = headers;\n this.dataHub = dataHub;\n this.services = services;\n this._host = host || null;\n this.getPlotConfig = getPlotConfig;\n this.savePlotConfig = savePlotConfig;\n\n this.window = null;\n this.contentEl = null;\n this.plotDiv = null;\n this._resizeObserver = null;\n this._chartCreated = false;\n this._config = null;\n this._fullData = null; // { headers, rows } with all data\n this._numericColumns = null; // Set of numeric column headers\n this._loading = false;\n }\n\n async show() {\n if (this.window?.isVisible) {\n this.window.bringToFront();\n return;\n }\n\n await ensurePlotly();\n await this.#fetchData();\n\n // Determine which columns are numeric from actual data\n this._numericColumns = new Set(this.headers.filter(h => this.#isNumericColumn(h)));\n\n // Load or create config (filter out any non-numeric columns from saved config)\n const saved = this.getPlotConfig?.(this.seriesId);\n if (saved) {\n this._config = { ...saved };\n this._config.yColumns = (saved.yColumns || []).filter(c => this._numericColumns.has(c.header));\n } else {\n this._config = DataSeriesPlotWindow.createDefaultConfig(\n this.headers, this._numericColumns, this.classifyHeader);\n }\n\n this.#createWindow();\n this.#setupResizeObserver();\n await this.#renderPlot();\n }\n\n close() {\n if (this.window?.isVisible) {\n this.window.close();\n }\n }\n\n // \u2500\u2500 Window creation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n #createWindow() {\n this.contentEl = this.#buildContent();\n\n const title = this.datasetName\n ? `${this.datasetName} \u00B7 ${this.seriesName}`\n : this.seriesName || 'Series Plot';\n\n this.window = new ManagedWindow({\n id: `data-series-plot-${this.seriesId}`,\n title,\n icon: 'monitoring',\n content: this.contentEl,\n minWidth: 600,\n minHeight: 400,\n defaultWidth: 900,\n defaultHeight: 550,\n canMinimize: true,\n canMaximize: true,\n canResize: true,\n canDrag: true,\n onClose: () => this.#dispose(),\n });\n\n this.window.show();\n }\n\n #buildContent() {\n const container = document.createElement('div');\n container.className = 'twm-data-series-plot-container';\n\n // Actions bar (download buttons)\n const actionsBar = document.createElement('div');\n actionsBar.className = 'twm-data-series-plot-actions';\n\n const pngBtn = document.createElement('button');\n pngBtn.className = 'twm-btn-icon twm-has-tooltip';\n pngBtn.type = 'button';\n pngBtn.setAttribute('data-tooltip', 'Download plot as PNG');\n pngBtn.innerHTML = '<span class=\"material-symbols-outlined\">image</span>';\n pngBtn.addEventListener('click', () => this.#downloadPNG());\n\n const csvBtn = document.createElement('button');\n csvBtn.className = 'twm-btn-icon twm-has-tooltip';\n csvBtn.type = 'button';\n csvBtn.setAttribute('data-tooltip', 'Download data as CSV');\n csvBtn.innerHTML = '<span class=\"material-symbols-outlined\">download</span>';\n csvBtn.addEventListener('click', () => this.#downloadCSV());\n\n actionsBar.appendChild(pngBtn);\n actionsBar.appendChild(csvBtn);\n\n // Chart area\n const chartWrap = document.createElement('div');\n chartWrap.className = 'twm-data-series-plot-body';\n\n this.plotDiv = document.createElement('div');\n this.plotDiv.className = 'twm-data-series-plot-chart';\n chartWrap.appendChild(this.plotDiv);\n\n // Config panel\n const configPanel = this.#buildConfigPanel();\n chartWrap.appendChild(configPanel);\n\n container.appendChild(actionsBar);\n container.appendChild(chartWrap);\n\n return container;\n }\n\n // \u2500\u2500 Config panel \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n #buildConfigPanel() {\n const panel = document.createElement('div');\n panel.className = 'twm-data-series-plot-config';\n\n // X Column\n panel.appendChild(this.#buildSection('X Axis', this.#buildXColumnSelect()));\n\n // Y Columns\n this._yColumnsList = document.createElement('div');\n this._yColumnsList.className = 'twm-data-series-plot-config__columns';\n this.#populateYColumns();\n panel.appendChild(this.#buildSection('Y Axis', this._yColumnsList));\n\n // Chart type\n panel.appendChild(this.#buildSection('Chart Type', this.#buildSelect(\n CHART_TYPES,\n this._config.chartType,\n (val) => { this._config.chartType = val; this.#onConfigChanged(); }\n )));\n\n // Interpolation\n panel.appendChild(this.#buildSection('Interpolation', this.#buildSelect(\n INTERPOLATION_MODES,\n this._config.interpolation,\n (val) => { this._config.interpolation = val; this.#onConfigChanged(); }\n )));\n\n // Legend position\n panel.appendChild(this.#buildSection('Legend', this.#buildSelect(\n LEGEND_POSITIONS,\n this._config.legendPosition,\n (val) => { this._config.legendPosition = val; this.#onConfigChanged(); }\n )));\n\n // Show data points\n panel.appendChild(this.#buildSection('', this.#buildCheckbox(\n 'Show data points',\n this._config.showDataPoints,\n (checked) => { this._config.showDataPoints = checked; this.#onConfigChanged(); }\n )));\n\n return panel;\n }\n\n #buildSection(label, content) {\n const section = document.createElement('div');\n section.className = 'twm-data-series-plot-config__section';\n\n if (label) {\n const labelEl = document.createElement('div');\n labelEl.className = 'twm-data-series-plot-config__label';\n labelEl.textContent = label;\n section.appendChild(labelEl);\n }\n\n if (content instanceof HTMLElement) {\n section.appendChild(content);\n }\n\n return section;\n }\n\n #buildXColumnSelect() {\n const select = document.createElement('select');\n for (const header of this.headers) {\n if (!this._numericColumns.has(header)) continue;\n const opt = document.createElement('option');\n opt.value = header;\n opt.textContent = this.labelHeader(header);\n if (header === this._config.xColumn) opt.selected = true;\n select.appendChild(opt);\n }\n select.addEventListener('change', () => {\n this._config.xColumn = select.value;\n this.#onConfigChanged();\n });\n return select;\n }\n\n #populateYColumns() {\n const list = this._yColumnsList;\n list.innerHTML = '';\n\n const selectedSet = new Set(this._config.yColumns.map(c => c.header));\n\n for (const header of this.headers) {\n if (header === this._config.xColumn) continue;\n if (!this._numericColumns.has(header)) continue;\n\n const item = document.createElement('label');\n item.className = 'twm-data-series-plot-config__column-item';\n\n const cb = document.createElement('input');\n cb.type = 'checkbox';\n cb.checked = selectedSet.has(header);\n cb.addEventListener('change', () => this.#toggleYColumn(header, cb.checked));\n\n const colorSwatch = document.createElement('input');\n colorSwatch.type = 'color';\n colorSwatch.className = 'twm-data-series-plot-config__color-swatch';\n const existing = this._config.yColumns.find(c => c.header === header);\n colorSwatch.value = existing?.color || this.#nextColor(this.headers.indexOf(header));\n colorSwatch.addEventListener('input', () => this.#updateColumnColor(header, colorSwatch.value));\n\n const label = document.createElement('span');\n label.textContent = this.labelHeader(header);\n label.title = header;\n\n item.appendChild(cb);\n item.appendChild(colorSwatch);\n item.appendChild(label);\n list.appendChild(item);\n }\n }\n\n #toggleYColumn(header, checked) {\n if (checked) {\n if (!this._config.yColumns.find(c => c.header === header)) {\n const colorSwatch = this._yColumnsList.querySelector(\n `label:has(span[title=\"${header}\"]) input[type=\"color\"]`\n );\n this._config.yColumns.push({\n header,\n color: colorSwatch?.value || this.#nextColor(this.headers.indexOf(header)),\n });\n }\n } else {\n this._config.yColumns = this._config.yColumns.filter(c => c.header !== header);\n }\n this.#onConfigChanged();\n }\n\n #updateColumnColor(header, color) {\n const col = this._config.yColumns.find(c => c.header === header);\n if (col) {\n col.color = color;\n this.#onConfigChanged();\n }\n }\n\n #nextColor(index) {\n return getSeriesColor(index);\n }\n\n #buildSelect(options, value, onChange) {\n const select = document.createElement('select');\n for (const opt of options) {\n const el = document.createElement('option');\n el.value = opt;\n el.textContent = opt.charAt(0).toUpperCase() + opt.slice(1);\n if (opt === value) el.selected = true;\n select.appendChild(el);\n }\n select.addEventListener('change', () => onChange(select.value));\n return select;\n }\n\n #buildCheckbox(label, checked, onChange) {\n const wrapper = document.createElement('label');\n wrapper.className = 'twm-data-series-plot-config__column-item';\n\n const cb = document.createElement('input');\n cb.type = 'checkbox';\n cb.checked = checked;\n cb.addEventListener('change', () => onChange(cb.checked));\n\n const span = document.createElement('span');\n span.textContent = label;\n\n wrapper.appendChild(cb);\n wrapper.appendChild(span);\n return wrapper;\n }\n\n // \u2500\u2500 Data fetching \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n async #fetchData() {\n if (this._loading) return;\n this._loading = true;\n\n try {\n const response = await this.dataHub.fetchSeriesById(this.seriesId, { limit: null, offset: 0 });\n if (response?.ok) {\n this._fullData = {\n headers: response.headers || [],\n rows: response.rows || [],\n };\n } else {\n this.#notify('Plot', response?.error || 'Failed to load series data', 'error');\n }\n } catch (err) {\n this.services.logger?.warn?.('[DataSeriesPlotWindow] Fetch failed', err);\n this.#notify('Plot', 'Failed to load data', 'error');\n } finally {\n this._loading = false;\n }\n }\n\n #isNumericColumn(header) {\n if (!this._fullData) return false;\n const data = this.#extractColumnData(header);\n // Sample up to 20 non-empty values\n let numericCount = 0;\n let sampleCount = 0;\n for (const v of data) {\n if (v == null || v === '') continue;\n sampleCount++;\n if (Number.isFinite(Number(v))) numericCount++;\n if (sampleCount >= 20) break;\n }\n return sampleCount > 0 && numericCount / sampleCount >= 0.8;\n }\n\n // \u2500\u2500 Chart rendering \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n #extractColumnData(columnHeader) {\n if (!this._fullData) return [];\n\n const { headers, rows } = this._fullData;\n const idx = headers.indexOf(columnHeader);\n if (idx < 0) return [];\n\n const TIME_NAMES = new Set(['time', 'tDisp', 'year', 'date', 'timestamp']);\n const firstIsTime = TIME_NAMES.has(headers[0]);\n const colOffset = firstIsTime ? 1 : 0;\n\n return rows.map(row => {\n if (Array.isArray(row)) return row[idx];\n if (row && typeof row === 'object') {\n // Handle {tDisp, cols} format\n if (TIME_NAMES.has(columnHeader) && row.tDisp !== undefined) return row.tDisp;\n if (Array.isArray(row.cols)) return row.cols[idx - colOffset] ?? '';\n return row[columnHeader] ?? '';\n }\n return '';\n }).map(v => {\n const n = Number(v);\n return Number.isFinite(n) ? n : v;\n });\n }\n\n async #renderPlot() {\n if (!this.plotDiv || !window.Plotly || !this._fullData) return;\n\n const cfg = this._config;\n const xData = this.#extractColumnData(cfg.xColumn);\n\n if (xData.length === 0 || cfg.yColumns.length === 0) {\n // Show empty state\n if (this._chartCreated) {\n try { purge(this.plotDiv); } catch (e) { /* ignore */ }\n this._chartCreated = false;\n }\n this.plotDiv.innerHTML = '<div style=\"display:flex;align-items:center;justify-content:center;height:100%;color:#888;font-size:13px;\">Select columns to plot</div>';\n return;\n }\n\n // Clear empty state if present\n if (!this._chartCreated) {\n this.plotDiv.innerHTML = '';\n }\n\n const traces = [];\n\n for (let i = 0; i < cfg.yColumns.length; i++) {\n const col = cfg.yColumns[i];\n const yData = this.#extractColumnData(col.header);\n\n traces.push(buildTrace({\n chartType: cfg.chartType,\n x: xData,\n y: yData,\n name: this.labelHeader(col.header),\n color: col.color,\n lineWidth: 2,\n lineStyle: 'solid',\n interpolation: cfg.interpolation,\n yAxisId: 'y',\n showMarkers: cfg.showDataPoints,\n seriesIndex: i,\n }));\n }\n\n const legendAtTop = cfg.legendPosition === 'top';\n const legendAtBottom = cfg.legendPosition === 'bottom';\n\n const layout = {\n ...DARK_THEME_LAYOUT,\n autosize: true,\n margin: {\n l: 60, r: 40,\n b: legendAtBottom ? 80 : 50,\n t: legendAtTop ? 60 : 30,\n },\n };\n\n // Remove 3D scene from layout\n delete layout.scene;\n\n // X-axis\n layout.xaxis = {\n ...buildXAxisLayout({ title: this.labelHeader(cfg.xColumn) }),\n };\n\n // Unified hover\n layout.hovermode = 'x unified';\n layout.xaxis.showspikes = true;\n layout.xaxis.spikemode = 'across';\n layout.xaxis.spikethickness = 1;\n layout.xaxis.spikecolor = '#666666';\n layout.xaxis.spikedash = 'dot';\n\n // Y-axis\n layout.yaxis = buildYAxisLayout({\n axisId: 'yaxis',\n title: '',\n position: 'left',\n scaleType: 'linear',\n showGrid: true,\n });\n\n // Legend\n const legendConfig = buildLegendLayout(cfg.legendPosition);\n if (legendConfig.legend) {\n layout.legend = legendConfig.legend;\n }\n layout.showlegend = legendConfig.showlegend !== false;\n\n const plotConfig = {\n ...DEFAULT_CONFIG,\n responsive: true,\n displayModeBar: true,\n modeBarButtonsToRemove: ['lasso2d', 'select2d', 'toImage'],\n };\n\n try {\n await window.Plotly.newPlot(this.plotDiv, traces, layout, plotConfig);\n this._chartCreated = true;\n\n requestAnimationFrame(() => {\n try {\n window.Plotly.Plots.resize(this.plotDiv);\n } catch (e) { /* ignore */ }\n });\n } catch (e) {\n console.error('[DataSeriesPlotWindow] Chart render error:', e);\n }\n }\n\n // \u2500\u2500 Config change handler \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n #onConfigChanged() {\n this.#renderPlot();\n this.savePlotConfig?.(this.seriesId, { ...this._config, yColumns: [...this._config.yColumns] });\n }\n\n // \u2500\u2500 Resize observer \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n #setupResizeObserver() {\n if (!this.plotDiv) return;\n\n this._resizeObserver = createRafResizeObserver(() => {\n if (this.plotDiv && window.Plotly && this._chartCreated) {\n try {\n window.Plotly.Plots.resize(this.plotDiv);\n } catch (e) { /* ignore */ }\n }\n });\n\n this._resizeObserver.observe(this.plotDiv);\n }\n\n // \u2500\u2500 Downloads \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n async #downloadPNG() {\n if (!this.plotDiv || !window.Plotly || !this._chartCreated) {\n this.#notify('Download', 'Plot not ready', 'warn');\n return;\n }\n\n const filename = `${(this.seriesName || 'series').replace(/[^a-z0-9]/gi, '_')}.png`;\n\n try {\n const pngDataUrl = await window.Plotly.toImage(this.plotDiv, {\n format: 'png', width: 1920, height: 1080, scale: 2,\n });\n\n // null <=> no save dialog behind the capability (host.js) -> fall back.\n const dialogs = this._host?.dialogs;\n const result = dialogs\n ? await dialogs.saveFile({ data: pngDataUrl.split(',')[1], filename, kind: 'png' })\n : null;\n if (result) {\n if (result.ok) {\n this.#notify('Download', `Plot saved to ${result.path}`, 'success');\n } else if (!result.cancelled) {\n this.#notify('Download', result.error || 'Failed to save image', 'error');\n }\n } else {\n const link = document.createElement('a');\n link.href = pngDataUrl;\n link.download = filename;\n link.click();\n }\n } catch (err) {\n this.services.logger?.warn?.('[DataSeriesPlotWindow] PNG download failed', err);\n this.#notify('Download', 'Failed to generate PNG', 'error');\n }\n }\n\n async #downloadCSV() {\n if (!this._fullData || !this._fullData.rows.length) {\n this.#notify('Download', 'No data available', 'warn');\n return;\n }\n\n const cfg = this._config;\n const columns = [cfg.xColumn, ...cfg.yColumns.map(c => c.header)];\n const headers = columns.map(h => this.labelHeader(h));\n\n // Pre-extract all columns once\n const columnData = columns.map(col => this.#extractColumnData(col));\n const rowCount = this._fullData.rows.length;\n\n const lines = [headers.join(',')];\n for (let r = 0; r < rowCount; r++) {\n const vals = columnData.map(data => this.#csvEscape(data[r]));\n lines.push(vals.join(','));\n }\n const csv = lines.join('\\n');\n\n const filename = `${(this.seriesName || 'series').replace(/[^a-z0-9]/gi, '_')}_data.csv`;\n\n // null <=> no save dialog behind the capability (host.js) -> fall back.\n const dialogs = this._host?.dialogs;\n let result = null;\n if (dialogs) {\n try {\n result = await dialogs.saveFile({ data: csv, filename, kind: 'csv' });\n } catch (err) {\n this.#notify('Download', 'Failed to save file', 'error');\n return;\n }\n }\n if (result) {\n if (result.ok) {\n this.#notify('Download', `Saved to ${result.path}`, 'success');\n } else if (!result.cancelled) {\n this.#notify('Download', result.error || 'Failed to save file', 'error');\n }\n } else {\n const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });\n const url = URL.createObjectURL(blob);\n const link = document.createElement('a');\n link.href = url;\n link.download = filename;\n link.click();\n URL.revokeObjectURL(url);\n }\n }\n\n #csvEscape(value) {\n if (value == null) return '';\n const str = String(value);\n if (/[\",\\n]/.test(str)) return `\"${str.replace(/\"/g, '\"\"')}\"`;\n return str;\n }\n\n // \u2500\u2500 Notifications \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n #notify(title, message, severity = 'info') {\n this.services.eventBus?.emit?.('toast:show', { title, message, type: severity });\n }\n\n // \u2500\u2500 Disposal \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n #dispose() {\n if (this._resizeObserver) {\n this._resizeObserver.disconnect();\n this._resizeObserver = null;\n }\n\n if (this.plotDiv && window.Plotly) {\n try { purge(this.plotDiv); } catch (e) { /* ignore */ }\n }\n\n _openPlotWindows.delete(this.seriesId);\n\n this.window = null;\n this.contentEl = null;\n this.plotDiv = null;\n this._yColumnsList = null;\n this._chartCreated = false;\n this._fullData = null;\n this._numericColumns = null;\n this._config = null;\n }\n\n // \u2500\u2500 Default config factory \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * The config a series with no saved one opens with: the first numeric\n * column `classifyHeader` calls `'time'` is X (else the first numeric\n * column), and up to MAX_AUTO_COLUMNS other numeric columns are Y.\n *\n * Static, so it has no window: the header classifier is a parameter. A\n * window passes its own (`options.classifyHeader`); a caller that passes\n * none gets the default, which knows `t`/`time`/`step`/`period`.\n *\n * @param {string[]} headers\n * @param {Set<string>} [numericColumns] - absent => every column is numeric\n * @param {(header: string) => string} [classifyHeader=defaultClassifyHeader]\n */\n static createDefaultConfig(headers, numericColumns, classifyHeader = defaultClassifyHeader) {\n const isNumeric = numericColumns instanceof Set ? (h) => numericColumns.has(h) : () => true;\n\n // Find best X column (time-like, must be numeric)\n let xColumn = '';\n for (const h of headers) {\n if (isNumeric(h) && classifyHeader(h) === 'time') {\n xColumn = h;\n break;\n }\n }\n // Fallback: first numeric column\n if (!xColumn) {\n for (const h of headers) {\n if (isNumeric(h)) { xColumn = h; break; }\n }\n }\n\n // Pick first N non-X numeric columns for Y\n const yColumns = [];\n for (const h of headers) {\n if (h === xColumn) continue;\n if (!isNumeric(h)) continue;\n if (yColumns.length >= MAX_AUTO_COLUMNS) break;\n yColumns.push({ header: h, color: getSeriesColor(yColumns.length) });\n }\n\n return {\n xColumn,\n yColumns,\n chartType: 'line',\n showDataPoints: false,\n interpolation: 'linear',\n legendPosition: 'top',\n };\n }\n}\n\n// \u2500\u2500 Singleton window management \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst _openPlotWindows = new Map();\n\nexport async function openDataSeriesPlotWindow(options) {\n const { seriesId } = options;\n let win = _openPlotWindows.get(seriesId);\n if (win?.window?.isVisible) {\n win.window.bringToFront();\n return win;\n }\n\n win = new DataSeriesPlotWindow(options);\n _openPlotWindows.set(seriesId, win);\n await win.show();\n return win;\n}\n\nexport function closeDataSeriesPlotWindow(seriesId) {\n const win = _openPlotWindows.get(seriesId);\n if (win) {\n win.close();\n _openPlotWindows.delete(seriesId);\n }\n}\n\nexport default DataSeriesPlotWindow;\n", "/**\n * plot_config_editor.js \u2014 edit a plot config (plot_config.js: `{ layout, subplots }`)\n * in place.\n *\n * const editor = renderPlotConfigEditor(el, plotConfig, {\n * onChange: () => grid.update({ plotConfig }),\n * seriesProvider: ({ value, role, subplot }) => myNames(value), // or a promise\n * });\n * editor.dispose();\n *\n * It draws a layout picker, a tab per subplot slot (when the layout has more\n * than one) and, for the subplot in front: its title; its chart type, legend,\n * interpolation, NaN handling and data points; its x axis (time, or a series);\n * its y axes (label, scale, position, min, max, step) and their series (each a\n * block: a header line of picker, label and remove over one label column of\n * colour, width, dash, stack group, the embedder's own fields\n * and, with `overlaySources`, an overlay source); its z axis while the chart\n * type is 3D; its reference lines and shaded bands; and the toggles of any\n * injected `seriesTransforms` (series_transforms.js).\n *\n * Every edit writes the config object it was given and then calls\n * `onChange(config)`. A layout with more slots than the config has subplots\n * gets default subplots (makeDefaultSubplot) for the new slots; a layout with\n * fewer keeps the extra subplots, unseen, so switching back restores them.\n *\n * A 3D chart type gives the subplot a z axis and uses its first y axis only:\n * while one is chosen the other y axes are shown disabled and no y axis can be\n * added. A 2D chart type removes the z axis (`zAxis: null`, as in\n * makeDefaultSubplot).\n *\n * `hide` takes parts of the editor away, for an embedder whose plots never use\n * them: '3d' (the 3D chart types and the z axis), 'stackGroup' (a series' stack\n * group), 'xAxis' (the x axis section; the subplot then plots against time).\n *\n * A reference line's value is a number. With `constantsProvider` it may also name\n * a constant: the box offers the names, and stores a name as a string, which\n * renderPlotGrid's `resolveConstant` turns into a value at each draw.\n *\n * Series names come from `seriesProvider`, through an AutocompleteField per\n * picker: it is asked `{ value, role, subplot }` (`role` is 'series', 'x' or\n * 'z') and answers AutocompleteField items (`{ label, insertText?, type? }`)\n * or a promise of them. One that throws or rejects shows no suggestions and\n * leaves what was typed as it is. Nothing here knows what a series is.\n *\n * It fits PlotPopoutWindow's `configRenderer` slot, which disposes what the\n * renderer returns when the window closes (openPlotPopoutWindow is async):\n *\n * const win = await openPlotPopoutWindow({ id, title, plotConfig, getResults, services,\n * configRenderer: (el) => renderPlotConfigEditor(el, plotConfig,\n * { onChange: () => win.updateConfig(plotConfig) }) });\n */\n\nimport { AutocompleteField } from '../ui/components/autocomplete_field.js';\nimport { attachInputOptions } from '../ui/components/combobox.js';\nimport { ColorField } from '../ui/components/color_field.js';\nimport { ToggleField } from '../ui/components/toggle_field.js';\nimport { selectBox } from '../ui/components/choice_controls.js';\nimport { createTabs } from '../ui/components/tabs.js';\nimport { CHART_TYPES_2D, CHART_TYPES_3D, is3DChart } from './chart_types.js';\nimport { PLOT_LAYOUTS, makeDefaultSubplot, makeDefaultYAxis, makeDefaultSeries } from './plot_config.js';\nimport { renderSeriesTransformControls } from './series_transforms.js';\n\nconst CHART_TYPE_OPTIONS = Object.freeze([\n { group: '2D', options: CHART_TYPES_2D },\n { group: '3D', options: CHART_TYPES_3D },\n]);\nconst HIDEABLE = Object.freeze(['3d', 'stackGroup', 'xAxis']);\nconst SCALES = Object.freeze([{ value: 'linear', label: 'Linear' }, { value: 'log', label: 'Log' }]);\nconst POSITIONS = Object.freeze([{ value: 'left', label: 'Left' }, { value: 'right', label: 'Right' }]);\nconst DASHES = Object.freeze([\n { value: 'solid', label: 'Solid' }, { value: 'dashed', label: 'Dashed' }, { value: 'dotted', label: 'Dotted' },\n]);\nconst LEGEND_POSITIONS = Object.freeze([\n { value: 'hidden', label: 'Hidden' }, { value: 'top', label: 'Top' }, { value: 'bottom', label: 'Bottom' },\n { value: 'left', label: 'Left' }, { value: 'right', label: 'Right' },\n]);\n// 'auto' is drawn as 'linear' (buildTrace); it is listed so a config saved with it\n// shows it and keeps it.\nconst INTERPOLATIONS = Object.freeze([\n { value: 'auto', label: 'Auto' },\n { value: 'linear', label: 'Linear' }, { value: 'step', label: 'Step' }, { value: 'cubic', label: 'Cubic spline' },\n]);\nconst NAN_STRATEGIES = Object.freeze([\n { value: 'gap', label: 'Gap' }, { value: 'zero', label: 'Zero' },\n { value: 'hold', label: 'Hold last value' }, { value: 'interpolate', label: 'Interpolate' },\n]);\nconst X_SOURCES = Object.freeze([{ value: 'time', label: 'Time' }, { value: 'series', label: 'Series' }]);\nconst OVERLAY_MODES = Object.freeze([\n { value: 'markers+lines', label: 'Markers and lines' }, { value: 'markers', label: 'Markers' },\n { value: 'lines', label: 'Lines' },\n]);\n\nfunction el(tag, className, text) {\n const node = document.createElement(tag);\n if (className) node.className = className;\n if (text != null) node.textContent = String(text);\n return node;\n}\n\nfunction button(text, onClick, { title = null, className = 'twm-btn twm-plot-config__add' } = {}) {\n const b = el('button', className, text);\n b.type = 'button';\n if (title) b.title = title;\n b.addEventListener('click', () => { if (!b.disabled) onClick(); });\n return b;\n}\n\nfunction removeButton(title, onClick) {\n const b = button('', onClick, { title, className: 'twm-btn-icon twm-plot-config__remove' });\n b.setAttribute('aria-label', title);\n b.appendChild(el('span', 'material-symbols-outlined', 'close'));\n return b;\n}\n\nlet hintCount = 0;\n\n/**\n * A labelled control on the editor's label column. A single input or select\n * is wrapped in a `<label>`; a compound one (a picker, a colour field) is not,\n * so a click on its text does not land on whichever of its inputs comes\n * first. A `hint` is drawn under the control, in the control's column, and\n * outside any `<label>`: the row is then a `<div>` whose label text is the\n * `<label for>` of a single control, and the hint describes the control\n * (`aria-describedby`) without being part of its name.\n */\nfunction field(label, control, { hint = null } = {}) {\n const single = control.matches('input, select, textarea');\n const hinted = hint != null && hint !== '';\n if (!hinted) {\n const row = el(single ? 'label' : 'div', 'twm-plot-config__field');\n row.append(el('span', 'twm-plot-config__label', label), control);\n return row;\n }\n const row = el('div', 'twm-plot-config__field twm-plot-config__field--hinted');\n const text = el(single ? 'label' : 'span', 'twm-plot-config__label', label);\n const note = el('span', 'twm-plot-config__hint', hint);\n note.id = `twm-plot-config-hint-${++hintCount}`;\n if (single) {\n if (!control.id) control.id = `${note.id}-control`;\n text.htmlFor = control.id;\n }\n const target = single ? control : (control.querySelector('input, select, textarea') ?? control);\n const describedBy = target.getAttribute('aria-describedby');\n target.setAttribute('aria-describedby', describedBy ? `${describedBy} ${note.id}` : note.id);\n row.append(text, control, note);\n return row;\n}\n\nfunction textInput(value, onSet, { placeholder = '', label = null } = {}) {\n const input = el('input', 'twm-plot-config__input');\n input.type = 'text';\n input.value = value ?? '';\n input.placeholder = placeholder;\n if (label) input.setAttribute('aria-label', label);\n input.addEventListener('input', () => onSet(input.value));\n return input;\n}\n\n/** A number box: empty (or not a number) sets null. */\nfunction numberInput(value, onSet, { placeholder = 'Auto', label = null, min = null, max = null, step = 'any' } = {}) {\n const input = el('input', 'twm-plot-config__input twm-plot-config__input--number');\n input.type = 'number';\n input.step = String(step);\n if (min != null) input.min = String(min);\n if (max != null) input.max = String(max);\n input.value = value == null ? '' : String(value);\n input.placeholder = placeholder;\n if (label) input.setAttribute('aria-label', label);\n input.addEventListener('change', () => {\n const n = parseFloat(input.value);\n onSet(Number.isFinite(n) ? n : null);\n });\n return input;\n}\n\n/** A select over choice options; `onPick(value)` gets the option's own value. */\nfunction select(options, value, onPick, { label = null, empty = null } = {}) {\n const s = selectBox(options, value, {\n label: label ?? '', empty,\n className: 'twm-plot-config__input twm-plot-config__input--select',\n });\n s.box.addEventListener('change', () => onPick(s.valueOf()));\n return s.box;\n}\n\nfunction makeZAxis() {\n return { label: '', scale: 'linear', min: null, max: null, step: null, variable: '' };\n}\n\nlet editorCount = 0;\n\n/**\n * Edit `config` in place.\n *\n * @param {HTMLElement} container - Receives one `.twm-plot-config` child.\n * @param {{ layout: string, subplots: Object[] }} config - Already migrated (migrateData).\n * @param {Object} [options]\n * @param {Function} [options.onChange] - `(config)` after every edit.\n * @param {Function} [options.seriesProvider] - `({ value, role, subplot }) => items |\n * Promise<items>`: the pickers' suggestions (AutocompleteField items).\n * @param {Function} [options.overlaySources] - `() => options | Promise<options>`: choice\n * options (`{ value, label }`, or `{ group, options }`) for a series' overlay\n * source, written to `series.overlay.source`. Absent: no overlay controls.\n * @param {Object} [options.seriesTransforms] - `{ [id]: transform }` (series_transforms.js);\n * their toggles are drawn per subplot.\n * @param {Function} [options.extraSeriesFields] - `(el, series, { subplot, axis, onChange, field })\n * => { dispose? } | specs[] | void`: fields of the embedder's own in each series\n * block, on the series' label column. It may draw into `el` (with\n * `field(label, control, { hint })` for rows that line up) and return a handle,\n * or return an ARRAY of field specs `{ label: string, control: HTMLElement,\n * hint? }` / `{ label: string, render(slot) => { dispose? } | void, hint? }`,\n * laid out after anything drawn into `el`. A bad spec is skipped with a warning.\n * @param {Array<'3d'|'stackGroup'|'xAxis'>} [options.hide] - Parts of the editor not to\n * draw. See the file's header. Any other name throws.\n * @param {Function} [options.constantsProvider] - `() => [{ name, value, label?, group? }]`:\n * the names a reference line's value may take, asked each time the list is drawn.\n * @param {Object} [options.logger] - Given to every AutocompleteField (provider errors);\n * its `warn` also hears a part (an `extraSeriesFields` handle, say) that throws on dispose.\n * @returns {{ dispose: Function, refresh: Function, selectSubplot: Function,\n * readonly activeSubplot: number }}\n */\nfunction renderPlotConfigEditor(container, config, options = {}) {\n const opts = {\n onChange: null,\n seriesProvider: null,\n overlaySources: null,\n seriesTransforms: null,\n extraSeriesFields: null,\n hide: [],\n constantsProvider: null,\n logger: null,\n ...options,\n };\n for (const part of opts.hide) {\n if (!HIDEABLE.includes(part)) throw new TypeError(`[plot config editor] cannot hide \"${part}\"`);\n }\n const hidden = new Set(opts.hide);\n const editorId = `twm-plot-config-${++editorCount}`;\n let fieldCount = 0;\n let active = 0;\n let disposed = false;\n /** Teardown for everything the subplot body created; run before each redraw. */\n let bodyDisposers = [];\n /** Bumped on each body redraw, so an overlay-source answer for an old body is dropped. */\n let generation = 0;\n\n const notify = () => { if (!disposed) opts.onChange?.(config); };\n const track = (fn) => { bodyDisposers.push(fn); };\n const template = () => PLOT_LAYOUTS[config.layout] || PLOT_LAYOUTS['1x1'];\n const slots = () => template().slots;\n\n function ensureSubplots() {\n if (!Array.isArray(config.subplots)) config.subplots = [];\n while (config.subplots.length < slots()) {\n config.subplots.push(makeDefaultSubplot(config.subplots.length));\n }\n }\n\n const root = el('div', 'twm-plot-config');\n\n // \u2500\u2500 layout picker \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const layoutButtons = new Map();\n const picker = el('div', 'twm-plot-config__layouts');\n picker.setAttribute('role', 'group');\n picker.setAttribute('aria-label', 'Layout');\n for (const [key, tmpl] of Object.entries(PLOT_LAYOUTS)) {\n const b = button(tmpl.label, () => setLayout(key), { className: 'twm-btn twm-plot-config__layout' });\n layoutButtons.set(key, b);\n picker.appendChild(b);\n }\n const syncLayoutButtons = () => {\n for (const [key, b] of layoutButtons) {\n const on = key === config.layout;\n b.setAttribute('aria-pressed', on ? 'true' : 'false');\n b.classList.toggle('twm-plot-config__layout--on', on);\n }\n };\n root.appendChild(section('Layout', picker));\n\n // \u2500\u2500 subplot tabs and the body \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const tabsHost = el('div', 'twm-plot-config__tabs');\n const body = el('div', 'twm-plot-config__body');\n root.append(tabsHost, body);\n container.appendChild(root);\n\n ensureSubplots();\n const tabItems = () => Array.from({ length: slots() }, (_, i) => ({ id: i, label: `Subplot ${i + 1}` }));\n const tabs = createTabs(tabsHost, {\n items: tabItems(),\n active: 0,\n compact: true,\n label: 'Subplots',\n onSelect: (id) => {\n if (id == null || id === active) return;\n active = id;\n renderBody();\n },\n });\n const syncChrome = () => {\n syncLayoutButtons();\n tabs.update(tabItems(), active);\n tabsHost.hidden = slots() < 2;\n };\n\n function setLayout(key) {\n if (disposed || key === config.layout) return;\n config.layout = key;\n ensureSubplots();\n active = Math.min(active, slots() - 1);\n syncChrome();\n renderBody();\n notify();\n }\n\n function section(title, ...children) {\n const s = el('section', 'twm-plot-config__section');\n s.appendChild(el('h4', 'twm-plot-config__heading', title));\n for (const child of children) if (child) s.appendChild(child);\n return s;\n }\n\n function disposeBody() {\n generation += 1;\n const disposers = bodyDisposers;\n bodyDisposers = [];\n for (const fn of disposers) {\n try { fn(); } catch (error) {\n opts.logger?.warn?.('[plot config editor] a field failed to dispose', error);\n }\n }\n }\n\n // \u2500\u2500 shared controls \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /** A series-name picker writing `target[key]`. */\n function seriesPicker(target, key, role, sp, placeholder) {\n let alive = true;\n const ac = new AutocompleteField({\n fieldId: `${editorId}-field-${++fieldCount}`,\n field: { placeholder, value: target[key] ?? '' },\n skipNamespacePrefix: true,\n logger: opts.logger,\n provider: ({ value }) => (opts.seriesProvider\n ? opts.seriesProvider({ value, role, subplot: sp })\n : []),\n onChange: (value) => {\n // The field reports its value again on blur, after a redraw\n // may have disposed it: a disposed field writes nothing.\n if (!alive || (target[key] ?? '') === value) return;\n target[key] = value;\n notify();\n },\n });\n track(() => { alive = false; ac.dispose(); });\n const node = ac.render();\n node.classList.add('twm-plot-config__picker');\n return node;\n }\n\n /** `keep` takes its teardown: the body's (track), or a narrower redraw's. */\n function colorField(value, onSet, label, keep = track) {\n const f = new ColorField({\n value: value ?? '',\n label,\n onChange: (v) => { if (v !== null) { onSet(v || null); notify(); } },\n });\n keep(() => f.dispose());\n return f.render();\n }\n\n function toggle(label, checked, onSet) {\n const t = new ToggleField({ label, checked, compact: true, onChange: (on) => { onSet(on); notify(); } });\n track(() => t.dispose());\n return t.render();\n }\n\n /** Rows of objects under `sp[key]`, each with a remove button, and an add button. */\n function listEditor(sp, key, { addLabel, makeItem, fields }) {\n if (!Array.isArray(sp[key])) sp[key] = [];\n const items = sp[key];\n const wrap = el('div', 'twm-plot-config__list');\n items.forEach((item, i) => {\n const row = el('div', 'twm-plot-config__row');\n row.append(...fields(item), removeButton('Remove', () => {\n items.splice(i, 1);\n renderBody();\n notify();\n }));\n wrap.appendChild(row);\n });\n wrap.appendChild(button(addLabel, () => {\n items.push(makeItem());\n renderBody();\n notify();\n }));\n return wrap;\n }\n\n // \u2500\u2500 the subplot in front \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n function normalise(sp) {\n if (!sp.xAxis) sp.xAxis = { useTime: true, variable: '' };\n if (hidden.has('xAxis')) sp.xAxis.useTime = true;\n if (!Array.isArray(sp.yAxes) || !sp.yAxes.length) sp.yAxes = [makeDefaultYAxis(0)];\n for (const axis of sp.yAxes) if (!Array.isArray(axis.series)) axis.series = [];\n if (is3DChart(sp.chartType) && !sp.zAxis) sp.zAxis = makeZAxis();\n }\n\n function renderBody() {\n if (disposed) return;\n disposeBody();\n body.replaceChildren();\n const sp = config.subplots[active];\n if (!sp) return;\n normalise(sp);\n const is3D = is3DChart(sp.chartType);\n let sources = null;\n const overlaySources = () => (sources ??= Promise.resolve().then(() => opts.overlaySources()));\n\n // Element.append() writes a null child as the text \"null\": a section the\n // subplot does not have (the z axis of a 2D chart, the transforms when\n // there are none) is left out, not appended.\n body.append(...[\n section('Subplot', field('Title', textInput(sp.displayName, (v) => { sp.displayName = v; notify(); },\n { placeholder: 'Untitled' }))),\n section('Display', ...displayFields(sp)),\n hidden.has('xAxis') ? null : section('X axis', ...xAxisFields(sp)),\n section('Y axes', yAxesEditor(sp, is3D, overlaySources)),\n is3D && !hidden.has('3d') ? section('Z axis', ...zAxisFields(sp)) : null,\n section('Reference lines', referenceLinesEditor(sp)),\n section('Shaded bands', shadedBandsEditor(sp)),\n transformsSection(sp),\n ].filter(Boolean));\n }\n\n function displayFields(sp) {\n return [\n field('Chart type', select(hidden.has('3d') ? CHART_TYPES_2D : CHART_TYPE_OPTIONS, sp.chartType ?? 'line', (v) => {\n const was3D = is3DChart(sp.chartType);\n sp.chartType = v;\n const now3D = is3DChart(v);\n if (now3D) sp.zAxis ??= makeZAxis();\n else sp.zAxis = null;\n if (was3D !== now3D) renderBody();\n notify();\n })),\n field('Legend', select(LEGEND_POSITIONS, sp.legendPosition ?? 'hidden',\n (v) => { sp.legendPosition = v; notify(); })),\n field('Interpolation', select(INTERPOLATIONS, sp.interpolation ?? 'linear',\n (v) => { sp.interpolation = v; notify(); })),\n field('Missing values', select(NAN_STRATEGIES, sp.nanHandling ?? 'gap',\n (v) => { sp.nanHandling = v; notify(); })),\n toggle('Data points', sp.showDataPoints === true, (on) => { sp.showDataPoints = on; }),\n ];\n }\n\n function xAxisFields(sp) {\n const useTime = sp.xAxis.useTime !== false;\n const out = [field('Against', select(X_SOURCES, useTime ? 'time' : 'series', (v) => {\n sp.xAxis.useTime = v === 'time';\n renderBody();\n notify();\n }))];\n if (!useTime) {\n out.push(\n field('Series', seriesPicker(sp.xAxis, 'variable', 'x', sp, 'X series')),\n field('Label', textInput(sp.xAxis.label, (v) => { sp.xAxis.label = v; notify(); },\n { placeholder: sp.xAxis.variable || 'Axis title' })),\n );\n }\n return out;\n }\n\n function zAxisFields(sp) {\n const z = sp.zAxis;\n return [\n field('Series', seriesPicker(z, 'variable', 'z', sp, 'Z series')),\n field('Label', textInput(z.label, (v) => { z.label = v; notify(); }, { placeholder: 'Axis title' })),\n field('Scale', select(SCALES, z.scale ?? 'linear', (v) => { z.scale = v; notify(); })),\n field('Min', numberInput(z.min, (v) => { z.min = v; notify(); })),\n field('Max', numberInput(z.max, (v) => { z.max = v; notify(); })),\n field('Step', numberInput(z.step, (v) => { z.step = v; notify(); })),\n ];\n }\n\n function yAxesEditor(sp, is3D, overlaySources) {\n const wrap = el('div', 'twm-plot-config__axes');\n sp.yAxes.forEach((axis, i) => wrap.appendChild(yAxisEditor(sp, axis, i, is3D, overlaySources)));\n const add = button('Add Y axis', () => {\n sp.yAxes.push(makeDefaultYAxis(sp.yAxes.length));\n renderBody();\n notify();\n }, { title: is3D ? 'A 3D chart has one Y axis' : null });\n add.disabled = is3D;\n wrap.appendChild(add);\n return wrap;\n }\n\n function yAxisEditor(sp, axis, index, is3D, overlaySources) {\n const group = el('fieldset', 'twm-plot-config__axis');\n const legend = el('legend', 'twm-plot-config__axis-title', `Y axis ${index + 1}`);\n group.appendChild(legend);\n if (sp.yAxes.length > 1) {\n // Inside the legend, so it stays usable while the axis is disabled.\n legend.appendChild(removeButton('Remove Y axis', () => {\n sp.yAxes.splice(index, 1);\n renderBody();\n notify();\n }));\n }\n if (is3D && index > 0) {\n group.disabled = true;\n group.classList.add('twm-plot-config__axis--disabled');\n group.title = 'A 3D chart uses the first Y axis only';\n }\n\n const fields = el('div', 'twm-plot-config__axis-fields');\n fields.append(\n field('Label', textInput(axis.label, (v) => { axis.label = v; notify(); }, { placeholder: 'Axis title' })),\n field('Scale', select(SCALES, axis.scale ?? 'linear', (v) => { axis.scale = v; notify(); })),\n field('Position', select(POSITIONS, axis.position ?? 'left', (v) => { axis.position = v; notify(); })),\n field('Min', numberInput(axis.min, (v) => { axis.min = v; notify(); })),\n field('Max', numberInput(axis.max, (v) => { axis.max = v; notify(); })),\n field('Step', numberInput(axis.step, (v) => { axis.step = v; notify(); })),\n );\n group.appendChild(fields);\n\n const list = el('div', 'twm-plot-config__series-list');\n if (!axis.series.length) list.appendChild(el('div', 'twm-plot-config__empty', 'No series.'));\n for (const s of axis.series) list.appendChild(seriesRow(sp, axis, s, overlaySources));\n group.appendChild(list);\n\n group.appendChild(button('Add series', () => {\n const count = sp.yAxes.reduce((n, a) => n + (a.series?.length ?? 0), 0);\n axis.series.push(makeDefaultSeries(count));\n renderBody();\n notify();\n }));\n return group;\n }\n\n /**\n * One series as a block: a header line (series picker, legend label,\n * remove) over its fields on the editor's label column (colour, width,\n * dash, stack group, the embedder's fields, overlay).\n */\n function seriesRow(sp, axis, s, overlaySources) {\n const row = el('div', 'twm-plot-config__series');\n const head = el('div', 'twm-plot-config__series-head');\n head.append(\n seriesPicker(s, 'variable', 'series', sp, 'Series'),\n textInput(s.label, (v) => { s.label = v; notify(); }, { placeholder: 'Legend label', label: 'Legend label' }),\n removeButton('Remove series', () => {\n const i = axis.series.indexOf(s);\n if (i >= 0) axis.series.splice(i, 1);\n renderBody();\n notify();\n }),\n );\n\n const fields = el('div', 'twm-plot-config__series-fields');\n fields.append(\n field('Colour', colorField(s.color, (v) => { if (v) s.color = v; else delete s.color; }, 'Colour')),\n field('Width', numberInput(s.lineWidth ?? 2, (v) => { s.lineWidth = v ?? 2; notify(); },\n { placeholder: '2', label: 'Line width', min: 1, max: 10, step: 1 })),\n field('Dash', select(DASHES, s.lineStyle ?? 'solid', (v) => { s.lineStyle = v; notify(); }, { label: 'Dash' })),\n );\n if (!hidden.has('stackGroup')) {\n fields.appendChild(field('Stack group', textInput(s.stackGroup, (v) => { s.stackGroup = v || null; notify(); },\n { placeholder: 'None', label: 'Stack group' })));\n }\n if (opts.extraSeriesFields) fields.appendChild(extraFields(sp, axis, s));\n if (opts.overlaySources) fields.appendChild(overlayEditor(s, overlaySources));\n row.append(head, fields);\n return row;\n }\n\n const warn = (...args) => (opts.logger?.warn ? opts.logger.warn(...args) : console.warn(...args));\n\n /**\n * The embedder's fields of a series: whatever `extraSeriesFields` draws\n * into the box (which sits in the series' field column), then, when it\n * returns an ARRAY, those field specs laid out by `field()`. Anything\n * else it returns is a handle, disposed when the row goes (`{ dispose }`).\n * A spec that is not `{ label, control: Element }` or\n * `{ label, render(slot) }` is skipped with a warning, as is a `render`\n * that throws: one bad field never leaves half a body.\n */\n function extraFields(sp, axis, s) {\n const extra = el('div', 'twm-plot-config__series-extra');\n const result = opts.extraSeriesFields(extra, s, { subplot: sp, axis, onChange: notify, field });\n if (!Array.isArray(result)) {\n if (typeof result?.dispose === 'function') track(() => result.dispose());\n return extra;\n }\n for (const spec of result) {\n const label = spec?.label;\n let control = null;\n if (typeof label !== 'string') {\n warn('[plot config editor] an extraSeriesFields spec has no label; skipped', spec);\n continue;\n }\n if (spec.control != null) {\n if (!(spec.control instanceof Element)) {\n warn('[plot config editor] an extraSeriesFields spec control is not an Element; skipped', spec);\n continue;\n }\n control = spec.control;\n } else if (typeof spec.render === 'function') {\n control = el('div', 'twm-plot-config__slot');\n try {\n const handle = spec.render(control);\n if (typeof handle?.dispose === 'function') track(() => handle.dispose());\n } catch (error) {\n warn('[plot config editor] an extraSeriesFields spec failed to render; skipped', error);\n continue;\n }\n } else {\n warn('[plot config editor] an extraSeriesFields spec has neither control nor render; skipped', spec);\n continue;\n }\n extra.appendChild(field(label, control, { hint: spec.hint }));\n }\n return extra;\n }\n\n function overlayEditor(s, overlaySources) {\n const wrap = el('div', 'twm-plot-config__overlay');\n const head = el('div', 'twm-plot-config__overlay-source', 'Loading overlay sources\u2026');\n const details = el('div', 'twm-plot-config__overlay-fields');\n wrap.append(head, details);\n\n // The details' colour field is torn down on each redraw of the\n // details, not left for the body's next redraw.\n let detailDisposers = [];\n const disposeDetails = () => {\n const fns = detailDisposers;\n detailDisposers = [];\n for (const fn of fns) {\n try { fn(); } catch (error) {\n opts.logger?.warn?.('[plot config editor] a field failed to dispose', error);\n }\n }\n };\n track(disposeDetails);\n const keep = (fn) => { detailDisposers.push(fn); };\n const drawDetails = () => {\n disposeDetails();\n details.replaceChildren();\n const ov = s.overlay;\n if (!ov) return;\n details.append(\n field('Label', textInput(ov.label, (v) => { ov.label = v; notify(); },\n { placeholder: `${s.label || s.variable || 'Series'} (actual)` })),\n field('Colour', colorField(ov.color, (v) => { if (v) ov.color = v; else delete ov.color; }, 'Overlay colour', keep)),\n field('Dash', select(DASHES, ov.lineStyle ?? 'dashed', (v) => { ov.lineStyle = v; notify(); })),\n field('Width', numberInput(ov.lineWidth ?? 2, (v) => { ov.lineWidth = v ?? 2; notify(); },\n { placeholder: '2', min: 1, max: 10, step: 1 })),\n field('Mode', select(OVERLAY_MODES, ov.mode ?? 'markers+lines', (v) => { ov.mode = v; notify(); })),\n );\n };\n\n const gen = generation;\n overlaySources().then((list) => {\n if (disposed || gen !== generation) return;\n head.replaceChildren(field('Overlay', select(Array.isArray(list) ? list : [], s.overlay?.source, (v) => {\n if (v === undefined) delete s.overlay;\n else s.overlay = { ...(s.overlay ?? {}), source: v };\n drawDetails();\n notify();\n }, { empty: 'None' })));\n drawDetails();\n }, () => {\n if (disposed || gen !== generation) return;\n head.textContent = 'Could not load overlay sources.';\n head.classList.add('twm-plot-config__overlay-source--error');\n });\n return wrap;\n }\n\n /**\n * A reference line's value as a number or a constant's name. What was typed\n * settles on change, on Enter or Tab, or on a pick; a typed number stays a number\n * even when it is part of a constant's name, so Enter does not take that row.\n */\n function constantValueField(line) {\n const wrap = el('div', 'twm-plot-config__constant');\n const input = el('input', 'twm-plot-config__input');\n input.type = 'text';\n input.spellcheck = false;\n input.placeholder = 'Number or name';\n input.setAttribute('aria-label', 'Value');\n input.value = line.value ?? '';\n const now = el('span', 'twm-plot-config__constant-value');\n wrap.append(input, now);\n\n const isNumber = (text) => text !== '' && Number.isFinite(Number(text));\n const sayValue = () => {\n const named = opts.constantsProvider().find((c) => c.name === input.value.trim());\n now.textContent = named ? `= ${named.value}` : '';\n };\n const settle = () => {\n const text = input.value.trim();\n const next = isNumber(text) ? Number(text) : text || null;\n sayValue();\n if (next === (line.value ?? null)) return;\n line.value = next;\n notify();\n };\n const offered = (query) => {\n const words = (query ?? '').trim().toLowerCase();\n const groups = new Map();\n for (const c of opts.constantsProvider()) {\n const text = c.label ?? c.name;\n if (words && !`${c.name} ${text}`.toLowerCase().includes(words)) continue;\n if (!groups.has(c.group)) groups.set(c.group, []);\n groups.get(c.group).push({ value: c.name, label: text, hint: `= ${c.value}` });\n }\n return [...groups].flatMap(([group, rows]) => (group ? [{ group, options: rows }] : rows));\n };\n const list = attachInputOptions(input, {\n options: offered,\n debounce: 0,\n autoHighlight: true,\n isCandidate: (typed) => isNumber(typed.trim()),\n openOnFocus: true,\n isCurrent: (option) => option.value === input.value.trim(),\n onPick: (option) => {\n input.value = option.value;\n settle();\n },\n onSettle: settle,\n });\n input.addEventListener('change', settle);\n // After the list's own keys, which take Enter while a row is active.\n input.addEventListener('keydown', (event) => { if (event.key === 'Enter') settle(); });\n track(() => list.destroy());\n sayValue();\n return wrap;\n }\n\n function referenceLinesEditor(sp) {\n const axisOptions = sp.yAxes.map((a, i) => ({ value: a.id, label: a.label || `Y axis ${i + 1}` }));\n return listEditor(sp, 'referenceLines', {\n addLabel: 'Add reference line',\n makeItem: () => ({ yAxisId: sp.yAxes[0].id, value: 0, label: '', lineStyle: 'dashed' }),\n fields: (line) => [\n select(axisOptions, line.yAxisId ?? sp.yAxes[0].id, (v) => { line.yAxisId = v; notify(); },\n { label: 'Axis' }),\n opts.constantsProvider\n ? constantValueField(line)\n : numberInput(line.value, (v) => { line.value = v; notify(); }, { placeholder: 'Value', label: 'Value' }),\n textInput(line.label, (v) => { line.label = v; notify(); }, { placeholder: 'Label', label: 'Label' }),\n colorField(line.color, (v) => { if (v) line.color = v; else delete line.color; }, 'Colour'),\n select(DASHES, line.lineStyle ?? 'dashed', (v) => { line.lineStyle = v; notify(); }, { label: 'Dash' }),\n ],\n });\n }\n\n function shadedBandsEditor(sp) {\n return listEditor(sp, 'shadedBands', {\n addLabel: 'Add shaded band',\n makeItem: () => ({ x0: null, x1: null, label: '' }),\n fields: (band) => [\n numberInput(band.x0, (v) => { band.x0 = v; notify(); }, { placeholder: 'From', label: 'From' }),\n numberInput(band.x1, (v) => { band.x1 = v; notify(); }, { placeholder: 'To', label: 'To' }),\n textInput(band.label, (v) => { band.label = v; notify(); }, { placeholder: 'Label', label: 'Label' }),\n colorField(band.color, (v) => { if (v) band.color = v; else delete band.color; }, 'Colour'),\n numberInput(band.opacity, (v) => {\n if (v == null) delete band.opacity; else band.opacity = v;\n notify();\n }, { placeholder: 'Opacity', label: 'Opacity', min: 0, max: 1, step: 0.05 }),\n ],\n });\n }\n\n function transformsSection(sp) {\n if (!Object.keys(opts.seriesTransforms ?? {}).length) return null;\n const s = section('Derived series');\n const controls = renderSeriesTransformControls(s, sp, opts.seriesTransforms, { onChange: notify });\n track(() => controls.dispose());\n return s;\n }\n\n syncChrome();\n renderBody();\n\n return {\n /** Dispose every field it created and remove the editor. Later calls do nothing. */\n dispose() {\n if (disposed) return;\n disposed = true;\n disposeBody();\n tabs.destroy();\n root.remove();\n },\n /** Redraw from the config, after it was changed from outside. */\n refresh() {\n if (disposed) return;\n ensureSubplots();\n active = Math.min(active, slots() - 1);\n syncChrome();\n renderBody();\n },\n /** Bring subplot `index` to the front. */\n selectSubplot(index) {\n if (disposed || !Number.isInteger(index) || index < 0 || index >= slots()) return;\n tabs.select(index);\n },\n /** The index of the subplot in front. */\n get activeSubplot() { return active; },\n };\n}\n\nexport { renderPlotConfigEditor };\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["select", "el", "LEGEND_POSITIONS", "row"]
|
|
7
7
|
}
|
|
@@ -2,10 +2,10 @@ import {
|
|
|
2
2
|
openConfirm,
|
|
3
3
|
openForm,
|
|
4
4
|
openModal
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-NFZJ6P42.js";
|
|
6
6
|
import {
|
|
7
7
|
ManagedWindow
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-HL3IIHU3.js";
|
|
9
9
|
|
|
10
10
|
// src/ui/components/progress.js
|
|
11
11
|
var PROGRESS_STATES = Object.freeze(["running", "done", "error", "cancelled"]);
|
|
@@ -775,4 +775,4 @@ export {
|
|
|
775
775
|
toastSuccess,
|
|
776
776
|
reportBridgeError
|
|
777
777
|
};
|
|
778
|
-
//# sourceMappingURL=chunk-
|
|
778
|
+
//# sourceMappingURL=chunk-3BLIVOFD.js.map
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DataTable,
|
|
3
3
|
createRafResizeObserver
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-QRBOIUU3.js";
|
|
5
5
|
import {
|
|
6
6
|
ManagedWindow
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-HL3IIHU3.js";
|
|
8
8
|
import {
|
|
9
9
|
flatChoices,
|
|
10
10
|
selectBox
|
|
@@ -1565,7 +1565,7 @@ function renderPlotGrid(container, plotConfig, results, options = {}) {
|
|
|
1565
1565
|
const legendPos = opts.alwaysShowLegend && (sp.legendPosition ?? "hidden") === "hidden" ? "top" : sp.legendPosition ?? "hidden";
|
|
1566
1566
|
const xRange = !useX && time.length >= 2 ? [time[0], time[time.length - 1]] : void 0;
|
|
1567
1567
|
const { shapes, annotations } = buildOverlayShapes({
|
|
1568
|
-
referenceLines: sp.referenceLines,
|
|
1568
|
+
referenceLines: (sp.referenceLines ?? []).map((line) => typeof line.value === "string" ? { ...line, value: opts.resolveConstant?.(line.value) } : line),
|
|
1569
1569
|
shadedBands: sp.shadedBands,
|
|
1570
1570
|
yRefByAxisId
|
|
1571
1571
|
});
|
|
@@ -2853,4 +2853,4 @@ export {
|
|
|
2853
2853
|
openPlotPopoutWindow,
|
|
2854
2854
|
openRawTracesWindow
|
|
2855
2855
|
};
|
|
2856
|
-
//# sourceMappingURL=chunk-
|
|
2856
|
+
//# sourceMappingURL=chunk-4X4SY3CP.js.map
|