You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@skywalking.apache.org by wu...@apache.org on 2026/08/28 12:20:35 UTC
(skywalking-horizon-ui) branch main updated: refactor(ui): move Modal and MonacoDiff into components/primitives (#143)
This is an automated email from the ASF dual-hosted git repository.
wu-sheng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/skywalking-horizon-ui.git
The following commit(s) were added to refs/heads/main by this push:
new 9c6690c6 refactor(ui): move Modal and MonacoDiff into components/primitives (#143)
9c6690c6 is described below
commit 9c6690c6b2befcfcf9c114e20ef5b1adcdedf3fb
Author: 吴晟 Wu Sheng <wu...@foxmail.com>
AuthorDate: Fri Aug 28 20:20:29 2026 +0800
refactor(ui): move Modal and MonacoDiff into components/primitives (#143)
Both are generic — a dialog shell and a Monaco diff viewer — and neither
contains a single reference to operate, cluster, inspect, dsl or live-debug.
They lived under `features/operate/_shared/` because that is where they were
first written, and fifteen files came to import them from there: `shell/`,
`render/widgets/`, `layer/`, and three features besides operate. A shell-level
prompt reaching into a feature folder for a dialog is the shape that says the
address is wrong rather than the rule.
Moving them takes the cross-feature imports the layering rule forbids from
fourteen down to one — the admin editor for the 3D map reading that feature's
own config composable, which is a page editing the thing it is the editor for.
No behaviour change: the two files are unmodified apart from a relative
`monaco/setup` import that the new depth broke, now on the `@/` alias like
everything around it.
---
apps/ui/CLAUDE.md | 4 +++-
.../src/{features/operate/_shared => components/primitives}/Modal.vue | 0
.../operate/_shared => components/primitives}/MonacoDiff.vue | 2 +-
apps/ui/src/features/admin/_shared/TemplateDiffModal.vue | 4 ++--
apps/ui/src/features/admin/infra-3d/Infra3dAdminView.vue | 4 ++--
apps/ui/src/features/admin/layer-templates/LayerDashboardsAdmin.vue | 4 ++--
apps/ui/src/features/admin/layer-templates/PageEntityFilter.vue | 2 +-
.../features/admin/overview-templates/NewOverviewDashboardModal.vue | 2 +-
.../src/features/admin/overview-templates/OverviewTemplatesAdmin.vue | 4 ++--
apps/ui/src/features/admin/translations/TranslationsView.vue | 4 ++--
apps/ui/src/features/events/EventsPopout.vue | 2 +-
apps/ui/src/features/operate/_shared/DestructiveConfirm.vue | 2 +-
apps/ui/src/features/operate/alerting-rules/AlertingRulesView.vue | 2 +-
apps/ui/src/features/operate/dsl/DslEditorView.vue | 2 +-
apps/ui/src/features/operate/live-debug/LalCellPopout.vue | 4 ++--
apps/ui/src/layer/browser-errors/SourceMapManager.vue | 2 +-
apps/ui/src/render/widgets/BrowserErrorPopout.vue | 4 ++--
apps/ui/src/render/widgets/LogDetailPopout.vue | 4 ++--
apps/ui/src/shell/TemplateConflictPrompt.vue | 2 +-
19 files changed, 28 insertions(+), 26 deletions(-)
diff --git a/apps/ui/CLAUDE.md b/apps/ui/CLAUDE.md
index fca0faca..455f7962 100644
--- a/apps/ui/CLAUDE.md
+++ b/apps/ui/CLAUDE.md
@@ -85,6 +85,8 @@ Each control owns its own reset and its own indicator, and resolves as its query
`api/` is the only path to HTTP. `shell/` knows about layers and routes, never about a feature's data. `controls/` owns the time range and the refresh ticker; pages subscribe and never own them. `state/` is global Pinia that survives navigation. `features/<feature>/` and `layer/<tab>/` are self-contained — views, composables and components together. `render/` is template-driven and generic. `components/{primitives,charts,icons}/` is feature-AGNOSTIC: the moment a shared component needs f [...]
-**Features do not import from each other**, and the exceptions today say what the rule is for rather than weakening it. `Modal` and `MonacoDiff` sit under `features/operate/_shared/` and are borrowed by admin and events — they are primitives in everything but their address, and moving them is a tidy-up nobody has done. The admin editor for the 3D map reads that feature's own config composable, which is a page editing the thing it is the editor for. Neither is one feature reaching into an [...]
+**Features do not import from each other.** The one exception today is the admin editor for the 3D map, which reads that feature's own config composable — a page editing the thing it is the editor for, not one feature reaching into another's *data*, which is what the rule exists to stop.
+
+A component that several features borrow is a primitive whichever folder it happens to sit in, and belongs in `components/primitives/`. `Modal` and `MonacoDiff` lived under `features/operate/_shared/` for a while because that is where they were first written; fifteen files across `shell/`, `render/`, `layer/` and three features imported them from there, which is the shape that tells you the address was wrong rather than the rule.
**A chart component owns its ECharts instance; a VIEW never creates one.** Most go through the shared wrappers in `components/charts/`; a couple of chart types specific to one feature have their own component beside that feature, which is the same rule applied to a component that knows a feature's shape. What is banned is a view calling `echarts.init` inline, because then nothing owns disposal or theming. D3 lifecycles belong to a composable that tears down on unmount.
diff --git a/apps/ui/src/features/operate/_shared/Modal.vue b/apps/ui/src/components/primitives/Modal.vue
similarity index 100%
rename from apps/ui/src/features/operate/_shared/Modal.vue
rename to apps/ui/src/components/primitives/Modal.vue
diff --git a/apps/ui/src/features/operate/_shared/MonacoDiff.vue b/apps/ui/src/components/primitives/MonacoDiff.vue
similarity index 97%
rename from apps/ui/src/features/operate/_shared/MonacoDiff.vue
rename to apps/ui/src/components/primitives/MonacoDiff.vue
index 67f09930..a02e1994 100644
--- a/apps/ui/src/features/operate/_shared/MonacoDiff.vue
+++ b/apps/ui/src/components/primitives/MonacoDiff.vue
@@ -17,7 +17,7 @@
<script setup lang="ts">
import { onBeforeUnmount, onMounted, ref, watch } from 'vue';
import * as monaco from 'monaco-editor';
-import { setupMonaco, RR_THEME_NAME } from '../../../monaco/setup.js';
+import { setupMonaco, RR_THEME_NAME } from '@/monaco/setup';
const props = withDefaults(
defineProps<{
diff --git a/apps/ui/src/features/admin/_shared/TemplateDiffModal.vue b/apps/ui/src/features/admin/_shared/TemplateDiffModal.vue
index b5a7decf..b6972bea 100644
--- a/apps/ui/src/features/admin/_shared/TemplateDiffModal.vue
+++ b/apps/ui/src/features/admin/_shared/TemplateDiffModal.vue
@@ -35,8 +35,8 @@ import { computed, onMounted, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import { bff } from '@/api/client';
import type { TemplateSyncRow } from '@/api/scopes/template-sync';
-import Modal from '@/features/operate/_shared/Modal.vue';
-import MonacoDiff from '@/features/operate/_shared/MonacoDiff.vue';
+import Modal from '@/components/primitives/Modal.vue';
+import MonacoDiff from '@/components/primitives/MonacoDiff.vue';
import Btn from '@/components/primitives/Btn.vue';
const { t } = useI18n({ useScope: 'global' });
diff --git a/apps/ui/src/features/admin/infra-3d/Infra3dAdminView.vue b/apps/ui/src/features/admin/infra-3d/Infra3dAdminView.vue
index 2ef4f56a..0987e220 100644
--- a/apps/ui/src/features/admin/infra-3d/Infra3dAdminView.vue
+++ b/apps/ui/src/features/admin/infra-3d/Infra3dAdminView.vue
@@ -57,8 +57,8 @@ import { useInfra3dLevels, type ResolvedLevel } from './useInfra3dLevels';
import SyncStatusBanner from '@/features/admin/_shared/SyncStatusBanner.vue';
import TemplateStatusBadge from '@/features/admin/_shared/TemplateStatusBadge.vue';
import Infra3dLayerRow, { type LayerRow } from './Infra3dLayerRow.vue';
-import Modal from '@/features/operate/_shared/Modal.vue';
-import MonacoDiff from '@/features/operate/_shared/MonacoDiff.vue';
+import Modal from '@/components/primitives/Modal.vue';
+import MonacoDiff from '@/components/primitives/MonacoDiff.vue';
// We hydrate the Layers section from the catalog union'd with config
// keys, so an OAP layer the admin hasn't classified yet shows up here
diff --git a/apps/ui/src/features/admin/layer-templates/LayerDashboardsAdmin.vue b/apps/ui/src/features/admin/layer-templates/LayerDashboardsAdmin.vue
index 731d017a..b6cd2b84 100644
--- a/apps/ui/src/features/admin/layer-templates/LayerDashboardsAdmin.vue
+++ b/apps/ui/src/features/admin/layer-templates/LayerDashboardsAdmin.vue
@@ -35,8 +35,8 @@ import type { AdminLayerTemplate } from '@/api/client';
import type { DashboardWidget, InstanceAttributePredicate } from '@skywalking-horizon-ui/api-client';
import SyncStatusBanner from '@/features/admin/_shared/SyncStatusBanner.vue';
-import Modal from '@/features/operate/_shared/Modal.vue';
-import MonacoDiff from '@/features/operate/_shared/MonacoDiff.vue';
+import Modal from '@/components/primitives/Modal.vue';
+import MonacoDiff from '@/components/primitives/MonacoDiff.vue';
import DependencyConfigSection from './DependencyConfigSection.vue';
import ProcessConfigEditor from './ProcessConfigEditor.vue';
import TopologyConfigEditor from './TopologyConfigEditor.vue';
diff --git a/apps/ui/src/features/admin/layer-templates/PageEntityFilter.vue b/apps/ui/src/features/admin/layer-templates/PageEntityFilter.vue
index cfc4afca..5a2cfe84 100644
--- a/apps/ui/src/features/admin/layer-templates/PageEntityFilter.vue
+++ b/apps/ui/src/features/admin/layer-templates/PageEntityFilter.vue
@@ -36,7 +36,7 @@ import { useLayerInstances } from '@/layer/useLayerInstances';
import { useLayerEndpoints } from '@/layer/useLayerEndpoints';
import { instancePageMatcher } from '@/render/layer-dashboard/instancePageFilter';
import TypeaheadSelect from '@/components/primitives/TypeaheadSelect.vue';
-import Modal from '@/features/operate/_shared/Modal.vue';
+import Modal from '@/components/primitives/Modal.vue';
const { t } = useI18n({ useScope: 'global' });
diff --git a/apps/ui/src/features/admin/overview-templates/NewOverviewDashboardModal.vue b/apps/ui/src/features/admin/overview-templates/NewOverviewDashboardModal.vue
index 7116cda4..408edbeb 100644
--- a/apps/ui/src/features/admin/overview-templates/NewOverviewDashboardModal.vue
+++ b/apps/ui/src/features/admin/overview-templates/NewOverviewDashboardModal.vue
@@ -24,7 +24,7 @@
<script setup lang="ts">
import { ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
-import Modal from '@/features/operate/_shared/Modal.vue';
+import Modal from '@/components/primitives/Modal.vue';
import { vAutosize } from '@/utils/autosize';
const { t } = useI18n({ useScope: 'global' });
diff --git a/apps/ui/src/features/admin/overview-templates/OverviewTemplatesAdmin.vue b/apps/ui/src/features/admin/overview-templates/OverviewTemplatesAdmin.vue
index 64c5436e..ef0039d9 100644
--- a/apps/ui/src/features/admin/overview-templates/OverviewTemplatesAdmin.vue
+++ b/apps/ui/src/features/admin/overview-templates/OverviewTemplatesAdmin.vue
@@ -54,8 +54,8 @@ import { refreshConfigBundle } from '@/controls/configBundle';
import { stableStringify } from '@/utils/stableJson';
import TemplatePicker, { type TemplatePickerEntry } from '@/features/admin/_shared/TemplatePicker.vue';
import { useTemplateSync } from '@/features/admin/_shared/useTemplateSync';
-import Modal from '@/features/operate/_shared/Modal.vue';
-import MonacoDiff from '@/features/operate/_shared/MonacoDiff.vue';
+import Modal from '@/components/primitives/Modal.vue';
+import MonacoDiff from '@/components/primitives/MonacoDiff.vue';
import OverviewEditCanvas from './OverviewEditCanvas.vue';
import OverviewWidgetDrawer from './OverviewWidgetDrawer.vue';
import NewOverviewDashboardModal from './NewOverviewDashboardModal.vue';
diff --git a/apps/ui/src/features/admin/translations/TranslationsView.vue b/apps/ui/src/features/admin/translations/TranslationsView.vue
index 99b22e9a..fc0f19c7 100644
--- a/apps/ui/src/features/admin/translations/TranslationsView.vue
+++ b/apps/ui/src/features/admin/translations/TranslationsView.vue
@@ -44,8 +44,8 @@ import LayerDashboardCanvas from '@/features/admin/_shared/LayerDashboardCanvas.
import OverviewDashboardCanvas from '@/features/admin/_shared/OverviewDashboardCanvas.vue';
import TemplatePicker from '@/features/admin/_shared/TemplatePicker.vue';
import TypeaheadSelect from '@/components/primitives/TypeaheadSelect.vue';
-import Modal from '@/features/operate/_shared/Modal.vue';
-import MonacoDiff from '@/features/operate/_shared/MonacoDiff.vue';
+import Modal from '@/components/primitives/Modal.vue';
+import MonacoDiff from '@/components/primitives/MonacoDiff.vue';
import TranslationFieldPanel from './TranslationFieldPanel.vue';
import { useTranslationPicker } from './useTranslationPicker';
import { useTranslationDraft, type EffectiveSource } from './useTranslationDraft';
diff --git a/apps/ui/src/features/events/EventsPopout.vue b/apps/ui/src/features/events/EventsPopout.vue
index bf068688..bcee3d0f 100644
--- a/apps/ui/src/features/events/EventsPopout.vue
+++ b/apps/ui/src/features/events/EventsPopout.vue
@@ -24,7 +24,7 @@
<script setup lang="ts">
import { computed, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
-import Modal from '@/features/operate/_shared/Modal.vue';
+import Modal from '@/components/primitives/Modal.vue';
import EventsGantt from './EventsGantt.vue';
import EventsDetailPanel from './EventsDetailPanel.vue';
import { useEventsWindow, PRESETS, MAX_CUSTOM_MS } from './useEventsWindow';
diff --git a/apps/ui/src/features/operate/_shared/DestructiveConfirm.vue b/apps/ui/src/features/operate/_shared/DestructiveConfirm.vue
index ffa846a2..33e445d6 100644
--- a/apps/ui/src/features/operate/_shared/DestructiveConfirm.vue
+++ b/apps/ui/src/features/operate/_shared/DestructiveConfirm.vue
@@ -17,7 +17,7 @@
<script setup lang="ts">
import { computed, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
-import Modal from './Modal.vue';
+import Modal from '@/components/primitives/Modal.vue';
import Btn from '@/components/primitives/Btn.vue';
const { t } = useI18n();
diff --git a/apps/ui/src/features/operate/alerting-rules/AlertingRulesView.vue b/apps/ui/src/features/operate/alerting-rules/AlertingRulesView.vue
index 9be6f9b7..17bd128b 100644
--- a/apps/ui/src/features/operate/alerting-rules/AlertingRulesView.vue
+++ b/apps/ui/src/features/operate/alerting-rules/AlertingRulesView.vue
@@ -46,7 +46,7 @@ import {
type AlarmRunningContext,
type AlarmMqeSnapshotSeries,
} from '@/api/client';
-import Modal from '@/features/operate/_shared/Modal.vue';
+import Modal from '@/components/primitives/Modal.vue';
import Sparkline from '@/components/charts/Sparkline.vue';
import { useOapInfo } from '@/shell/useOapInfo';
diff --git a/apps/ui/src/features/operate/dsl/DslEditorView.vue b/apps/ui/src/features/operate/dsl/DslEditorView.vue
index 543ef3ff..44931a8f 100644
--- a/apps/ui/src/features/operate/dsl/DslEditorView.vue
+++ b/apps/ui/src/features/operate/dsl/DslEditorView.vue
@@ -30,7 +30,7 @@ import { sha256Hex } from '@/features/operate/dsl/contentHash';
import Btn from '@/components/primitives/Btn.vue';
import Pill from '@/components/primitives/Pill.vue';
import MonacoYaml from '@/features/operate/_shared/MonacoYaml.vue';
-import MonacoDiff from '@/features/operate/_shared/MonacoDiff.vue';
+import MonacoDiff from '@/components/primitives/MonacoDiff.vue';
import DestructiveConfirm from '@/features/operate/_shared/DestructiveConfirm.vue';
import ApplyProgress from '@/features/operate/dsl/ApplyProgress.vue';
import AdminFeatureWarning from '@/shell/AdminFeatureWarning.vue';
diff --git a/apps/ui/src/features/operate/live-debug/LalCellPopout.vue b/apps/ui/src/features/operate/live-debug/LalCellPopout.vue
index cfc092de..caa26656 100644
--- a/apps/ui/src/features/operate/live-debug/LalCellPopout.vue
+++ b/apps/ui/src/features/operate/live-debug/LalCellPopout.vue
@@ -27,9 +27,9 @@
*/
import { computed, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
-import Modal from '@/features/operate/_shared/Modal.vue';
+import Modal from '@/components/primitives/Modal.vue';
import MonacoView from '@/features/operate/_shared/MonacoView.vue';
-import MonacoDiff from '@/features/operate/_shared/MonacoDiff.vue';
+import MonacoDiff from '@/components/primitives/MonacoDiff.vue';
import FloatingPanel from '@/components/primitives/FloatingPanel.vue';
import Icon from '@/components/icons/Icon.vue';
diff --git a/apps/ui/src/layer/browser-errors/SourceMapManager.vue b/apps/ui/src/layer/browser-errors/SourceMapManager.vue
index 75ba8552..12bbf6af 100644
--- a/apps/ui/src/layer/browser-errors/SourceMapManager.vue
+++ b/apps/ui/src/layer/browser-errors/SourceMapManager.vue
@@ -26,7 +26,7 @@ import { useI18n } from 'vue-i18n';
import type { SourceMapDescriptor, SourceMapUsage } from '@/api/client';
import Btn from '@/components/primitives/Btn.vue';
import Icon from '@/components/icons/Icon.vue';
-import Modal from '@/features/operate/_shared/Modal.vue';
+import Modal from '@/components/primitives/Modal.vue';
import { useAuthStore } from '@/state/auth';
const { t } = useI18n({ useScope: 'global' });
diff --git a/apps/ui/src/render/widgets/BrowserErrorPopout.vue b/apps/ui/src/render/widgets/BrowserErrorPopout.vue
index 261d4067..193165a0 100644
--- a/apps/ui/src/render/widgets/BrowserErrorPopout.vue
+++ b/apps/ui/src/render/widgets/BrowserErrorPopout.vue
@@ -22,7 +22,7 @@
the per-layer Browser Logs tab; the host owns the maps list (so it can also
render the SourceMapManager), this popout owns the per-row resolve call.
- Built on `_shared/Modal.vue` (backdrop + Escape + close).
+ Built on `components/primitives/Modal.vue` (backdrop + Escape + close).
Props:
row — the BrowserErrorRow to show; `null` keeps the popout closed.
@@ -36,7 +36,7 @@ import { computed, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import type { BrowserErrorRow, ResolveResponse, SourceMapDescriptor } from '@/api/client';
import { bffClient, describeApiError } from '@/api/client';
-import Modal from '@/features/operate/_shared/Modal.vue';
+import Modal from '@/components/primitives/Modal.vue';
const { t } = useI18n();
diff --git a/apps/ui/src/render/widgets/LogDetailPopout.vue b/apps/ui/src/render/widgets/LogDetailPopout.vue
index 1f9ccbd7..1efe9a32 100644
--- a/apps/ui/src/render/widgets/LogDetailPopout.vue
+++ b/apps/ui/src/render/widgets/LogDetailPopout.vue
@@ -21,7 +21,7 @@
tags table, the service / instance / endpoint / time meta strip, a
copy button, and the trace-id link.
- Built on `_shared/Modal.vue` (backdrop + Escape + close). Used by BOTH
+ Built on `components/primitives/Modal.vue` (backdrop + Escape + close). Used by BOTH
the per-layer Logs tab and the cross-layer Log inspect view. The host
owns the trace jump — the popout just emits `jump-trace`.
@@ -35,7 +35,7 @@
import { computed, onBeforeUnmount, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import type { LogRow } from '@/api/client';
-import Modal from '@/features/operate/_shared/Modal.vue';
+import Modal from '@/components/primitives/Modal.vue';
const { t } = useI18n();
diff --git a/apps/ui/src/shell/TemplateConflictPrompt.vue b/apps/ui/src/shell/TemplateConflictPrompt.vue
index ff11cb93..1bf76931 100644
--- a/apps/ui/src/shell/TemplateConflictPrompt.vue
+++ b/apps/ui/src/shell/TemplateConflictPrompt.vue
@@ -27,7 +27,7 @@
import { computed, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRoute, useRouter } from 'vue-router';
-import Modal from '@/features/operate/_shared/Modal.vue';
+import Modal from '@/components/primitives/Modal.vue';
import { useLayers } from '@/shell/useLayers';
import { useConfigBundle } from '@/controls/configBundle';
import { useLocalTemplateEdits } from '@/controls/localTemplateEdits';