diff --git a/.claude/skills/ui-architect/SKILL.md b/.claude/skills/ui-architect/SKILL.md index ed3e38f532..5d804df2a3 100644 --- a/.claude/skills/ui-architect/SKILL.md +++ b/.claude/skills/ui-architect/SKILL.md @@ -4,15 +4,19 @@ description: >- Authoring guardrails for building ANY new frontend UI in the OpenObserve web app (web/) — new views, pages, panels, dialogs, feature components, or edits to existing ones. Enforce six house rules the moment you write Vue/template - markup: (1) use AppPageHeader for every page/module header, (2) build UI from - O2 library components in web/src/lib — never raw Quasar or bare HTML controls - when an O2 equivalent exists, (3) no hardcoded px anywhere — including inside + markup: (1) use OPageHeader for every page/module header, (2) build UI from + O2 library components in web/src/lib — never bare HTML controls when an O2 + equivalent exists, (3) no hardcoded px anywhere — including inside Tailwind class arbitrary values ([320px]) — size with rem/%/vh/vw or Tailwind's - rem-based scale, (4) no scoped-CSS blocks and no inline style="", (5) never - hardcode colors/sizes — use the modern registered --color-* design tokens and - register a new --color-* token if one is missing; the legacy --o2-* token - vocabulary is BANNED (never write var(--o2-*), never define one, never add a - .body--dark block — migrate any --o2-* you touch to its --color-* equivalent), + rem-based scale, and corner radius uses only the two-tier scale rounded-default + (4px controls) / rounded-surface (12px surfaces) / rounded-full — never + rounded-[..] or the retired rounded-sm/md/lg/xl, (4) no scoped-CSS blocks and no + inline style="", (5) never hardcode colors/sizes and never reach a token by raw + var() in a component — use the modern registered --color-* design tokens through + their utility class (bg-x/text-x), register a new --color-* token if one is + missing; the legacy --o2-* token vocabulary is BANNED (never write var(--o2-*), + never define one, never add a .body--dark block — migrate any --o2-* you touch to + its --color-* equivalent); all of this is CI-enforced and fails the build, (6) no hardcoded user-facing text — every label, title, placeholder, and message comes from i18n (useI18n t()) with keys added to web/src/locales/languages/en-US.json. It also settles the recurring @@ -33,7 +37,7 @@ description: >- hover-flyout — how to register its route, and how to gate it for cloud / enterprise / RBAC — even if they don't mention these rules by name. If you are - about to type
-
+
.
-
+
. name="update" size="sm" :class="[ - isAnimating ? '![animation:rotate_2s_linear_infinite] ![transform-origin:center_center] !inline-block' : '', + isAnimating ? 'auto-refresh-icon--spinning' : '', isAnimating ? 'text-primary' : '', 'mr-0.5', ]" /> -
{{ selectedLabel }}
+
{{ selectedLabel }}
-
+
.
- + diff --git a/web/src/components/CodeQueryEditor.vue b/web/src/components/CodeQueryEditor.vue index 11098cd324..ad068b0511 100644 --- a/web/src/components/CodeQueryEditor.vue +++ b/web/src/components/CodeQueryEditor.vue @@ -18,7 +18,7 @@ along with this program. If not, see .
@@ -27,8 +27,8 @@ along with this program. If not, see . v-if="showAiIcon && !disableAi" variant="sidebar-toggle" size="icon-toolbar" - class="absolute! top-2 right-2 z-10 bg-(--o2-bg-primary) border border-(--o2-border-color) transition-all duration-200 hover:bg-(--color-button-outline-hover-bg) hover:border-[var(--o2-color-primary)]" - :class="nlpMode ? 'bg-[var(--o2-color-primary-light)] border-[var(--o2-color-primary)]' : ''" + class="absolute! top-2 right-2 z-10 bg-card-glass-bg border border-card-glass-border transition-all duration-200 hover:bg-button-outline-hover-bg hover:border-accent" + :class="nlpMode ? 'bg-primary-100 border-accent' : ''" @click="toggleNlpMode" data-test="query-editor-ai-icon-btn" > @@ -75,6 +75,7 @@ const loadMonaco = async () => { import { vrlLanguageDefinition } from "@/utils/query/vrlLanguageDefinition"; import { useStore } from "vuex"; +import { useTheme } from "@/composables/useTheme"; import { debounce } from "lodash-es"; import searchState from "@/composables/useLogs/searchState"; import { useNLQuery } from "@/composables/useNLQuery"; @@ -82,6 +83,7 @@ import { useI18n } from "vue-i18n"; import useNotifications from "@/composables/useNotifications"; import { getImageURL } from "@/utils/zincutils"; import { isAuthError } from "@/utils/authErrors"; +import { getFontMono } from "@/utils/fonts"; import OButton from "@/lib/core/Button/OButton.vue"; import OIcon from "@/lib/core/Icon/OIcon.vue"; import OTooltip from "@/lib/overlay/Tooltip/OTooltip.vue"; @@ -177,10 +179,10 @@ export default defineComponent({ ], setup(props, { emit }) { const store = useStore(); + const { isDark } = useTheme(); const { t } = useI18n(); const { showErrorNotification } = useNotifications(); const editorRef: any = ref(); - // editor object is used to interact with the monaco editor instance let editorObj: any = null; // Emits the editor's content immediately instead of waiting out the change // debounce. Assigned when the editor is created; see `commitModelChange`. @@ -362,11 +364,11 @@ export default defineComponent({ ]; watch( - () => store.state.theme, + () => isDark.value, () => { if (!monaco) return; monaco.editor.setTheme( - store.state.theme == "dark" ? "myCustomDarkTheme" : "myCustomTheme", + isDark.value ? "myCustomDarkTheme" : "myCustomTheme", ); }, ); @@ -427,7 +429,6 @@ export default defineComponent({ const currentLanguage = props.language?.toLowerCase() || "sql"; try { - // Get organization ID from store const orgId = store.state.selectedOrganization?.identifier || "default"; // Create language-appropriate prompt @@ -649,7 +650,11 @@ export default defineComponent({ editorObj = monaco.editor.create(editorElement as HTMLElement, { value: props.query?.trim(), language: props.language, - theme: store.state.theme == "dark" ? "myCustomDarkTheme" : "myCustomTheme", + // Monaco paints its own text and ignores the CSS cascade — without this it + // falls back to its built-in Menlo/Monaco/Courier New stack, which differs + // per OS and from the rest of the app. + fontFamily: getFontMono(), + theme: isDark.value ? "myCustomDarkTheme" : "myCustomTheme", showFoldingControls: enableCodeFolding.value ? "always" : "never", folding: enableCodeFolding.value, wordWrap: "on", @@ -877,11 +882,11 @@ export default defineComponent({ ); watch( - () => store.state.theme, + () => isDark.value, () => { if (!monaco) return; monaco.editor.setTheme( - store.state.theme == "dark" ? "myCustomDarkTheme" : "myCustomTheme", + isDark.value ? "myCustomDarkTheme" : "myCustomTheme", ); }, ); @@ -1174,7 +1179,7 @@ export default defineComponent({ // Computed property for AI icon based on theme const aiIcon = computed(() => { - return store.state.theme === "dark" + return isDark.value ? getImageURL("images/common/ai_icon_dark.svg") : getImageURL("images/common/ai_icon_gradient.svg"); }); @@ -1209,216 +1214,42 @@ export default defineComponent({ }); - diff --git a/web/src/components/CommunitySlackInvite.vue b/web/src/components/CommunitySlackInvite.vue index 92c8c60731..0ea78f934c 100644 --- a/web/src/components/CommunitySlackInvite.vue +++ b/web/src/components/CommunitySlackInvite.vue @@ -153,7 +153,7 @@ const joinSlack = () => {