This is an automated email from the ASF dual-hosted git repository.

EnxDev pushed a commit to branch enxdev/chat-prototype
in repository https://gitbox.apache.org/repos/asf/superset.git

commit d1aa512ec99034a3aefcbe4dfa9425885b67e172
Author: Enzo Martellucci <[email protected]>
AuthorDate: Tue May 26 16:11:26 2026 +0200

    feat(extensions): context sharing namespaces (navigation, explore, 
dashboard, dataset)
    
    Adds four stable namespaces to @apache-superset/core that give extensions
    host-managed access to page context without coupling to Redux internals:
    
    - navigation: getPageType(), onDidChangePage (routing signal only)
    - explore: getCurrentChart(), onDidChangeChart (ChartContext from Redux)
    - dashboard: getCurrentDashboard(), onDidChangeDashboard (DashboardContext
      with active native filter values from Redux)
    - dataset: getCurrentDataset(), onDidChangeDataset (push model)
    
    All four are wired into window.superset via ExtensionsStartup.
    
    Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
---
 .../packages/superset-core/package.json            | 16 ++++
 .../packages/superset-core/src/dashboard/index.ts  | 84 ++++++++++++++++++++
 .../packages/superset-core/src/dataset/index.ts    | 73 ++++++++++++++++++
 .../packages/superset-core/src/explore/index.ts    | 75 ++++++++++++++++++
 .../packages/superset-core/src/index.ts            |  4 +
 .../packages/superset-core/src/navigation/index.ts | 71 +++++++++++++++++
 superset-frontend/src/core/dashboard/index.ts      | 90 ++++++++++++++++++++++
 superset-frontend/src/core/dataset/index.ts        | 62 +++++++++++++++
 superset-frontend/src/core/explore/index.ts        | 84 ++++++++++++++++++++
 superset-frontend/src/core/index.ts                |  4 +
 superset-frontend/src/core/navigation/index.ts     | 69 +++++++++++++++++
 11 files changed, 632 insertions(+)

diff --git a/superset-frontend/packages/superset-core/package.json 
b/superset-frontend/packages/superset-core/package.json
index cf9d4a02665..0fd06f79a62 100644
--- a/superset-frontend/packages/superset-core/package.json
+++ b/superset-frontend/packages/superset-core/package.json
@@ -18,6 +18,22 @@
       "types": "./lib/authentication/index.d.ts",
       "default": "./lib/authentication/index.js"
     },
+    "./dashboard": {
+      "types": "./lib/dashboard/index.d.ts",
+      "default": "./lib/dashboard/index.js"
+    },
+    "./dataset": {
+      "types": "./lib/dataset/index.d.ts",
+      "default": "./lib/dataset/index.js"
+    },
+    "./explore": {
+      "types": "./lib/explore/index.d.ts",
+      "default": "./lib/explore/index.js"
+    },
+    "./navigation": {
+      "types": "./lib/navigation/index.d.ts",
+      "default": "./lib/navigation/index.js"
+    },
     "./commands": {
       "types": "./lib/commands/index.d.ts",
       "default": "./lib/commands/index.js"
diff --git a/superset-frontend/packages/superset-core/src/dashboard/index.ts 
b/superset-frontend/packages/superset-core/src/dashboard/index.ts
new file mode 100644
index 00000000000..4e78c7eef6c
--- /dev/null
+++ b/superset-frontend/packages/superset-core/src/dashboard/index.ts
@@ -0,0 +1,84 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * @fileoverview Dashboard namespace for Superset extensions (P3).
+ *
+ * Exposes dashboard identity and filter state as a stable semantic API.
+ * Extensions must not depend on the Redux dashboard slice structure directly.
+ */
+
+import { Event } from '../common';
+
+/**
+ * A single native filter's current selected value(s).
+ * The value type is intentionally kept as `unknown` because filter values
+ * are heterogeneous (date ranges, string lists, numbers, etc.).
+ */
+export interface FilterValue {
+  /** The filter's stable id. */
+  filterId: string;
+  /** Display label of the filter. */
+  label: string;
+  /** Currently applied value, or `null` when the filter is cleared. */
+  value: unknown;
+}
+
+/**
+ * Normalized dashboard context exposed to extensions on the Dashboard page.
+ */
+export interface DashboardContext {
+  /** Numeric dashboard id. */
+  dashboardId: number;
+  /** Display title of the dashboard. */
+  title: string;
+  /**
+   * Active native filter values keyed by filter id.
+   * Only includes filters that have a value applied.
+   */
+  filters: FilterValue[];
+}
+
+/**
+ * Returns the normalized dashboard context for the page currently being 
viewed,
+ * or `undefined` when the user is not on a Dashboard page.
+ *
+ * @example
+ * ```typescript
+ * const dash = dashboard.getCurrentDashboard();
+ * if (dash) {
+ *   console.log(dash.title, dash.filters);
+ * }
+ * ```
+ */
+export declare function getCurrentDashboard(): DashboardContext | undefined;
+
+/**
+ * Event fired when the dashboard identity or its active filter values change.
+ * Fired on native filter value changes and on navigation to a different 
dashboard.
+ *
+ * @example
+ * ```typescript
+ * const sub = dashboard.onDidChangeDashboard(dash => {
+ *   chatbot.updateContext({ dashboard: dash });
+ * });
+ * sub.dispose();
+ * ```
+ */
+export declare const onDidChangeDashboard: Event<DashboardContext>;
diff --git a/superset-frontend/packages/superset-core/src/dataset/index.ts 
b/superset-frontend/packages/superset-core/src/dataset/index.ts
new file mode 100644
index 00000000000..ea3fafa4fdb
--- /dev/null
+++ b/superset-frontend/packages/superset-core/src/dataset/index.ts
@@ -0,0 +1,73 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * @fileoverview Dataset namespace for Superset extensions (P3).
+ *
+ * Exposes the dataset currently being viewed as a stable semantic API.
+ * Aligned with backend-enforced dataset visibility and column-access 
semantics.
+ */
+
+import { Event } from '../common';
+
+/**
+ * Normalized dataset context exposed to extensions on the Dataset page.
+ */
+export interface DatasetContext {
+  /** Numeric dataset id. */
+  datasetId: number;
+  /** Display name (table name or virtual dataset name). */
+  datasetName: string;
+  /** Schema the dataset belongs to, if applicable. */
+  schema: string | null;
+  /** Catalog the dataset belongs to, if applicable. */
+  catalog: string | null;
+  /** Database name backing this dataset. */
+  databaseName: string | null;
+  /** Whether this is a virtual (SQL-defined) dataset. */
+  isVirtual: boolean;
+}
+
+/**
+ * Returns the normalized dataset context for the page currently being viewed,
+ * or `undefined` when the user is not on a Dataset page.
+ *
+ * @example
+ * ```typescript
+ * const ds = dataset.getCurrentDataset();
+ * if (ds) {
+ *   console.log(ds.datasetName, ds.schema);
+ * }
+ * ```
+ */
+export declare function getCurrentDataset(): DatasetContext | undefined;
+
+/**
+ * Event fired when the focused dataset changes (e.g. the user navigates to a
+ * different dataset detail page).
+ *
+ * @example
+ * ```typescript
+ * const sub = dataset.onDidChangeDataset(ds => {
+ *   chatbot.updateContext({ dataset: ds });
+ * });
+ * sub.dispose();
+ * ```
+ */
+export declare const onDidChangeDataset: Event<DatasetContext>;
diff --git a/superset-frontend/packages/superset-core/src/explore/index.ts 
b/superset-frontend/packages/superset-core/src/explore/index.ts
new file mode 100644
index 00000000000..162d1b2e6f7
--- /dev/null
+++ b/superset-frontend/packages/superset-core/src/explore/index.ts
@@ -0,0 +1,75 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * @fileoverview Explore namespace for Superset extensions (P3).
+ *
+ * Exposes the current chart/explore context as a stable semantic API.
+ * Normalized over Explore Redux state — extensions must not depend on
+ * the Redux slice structure directly.
+ */
+
+import { Event } from '../common';
+
+/**
+ * Normalized chart context exposed to extensions during an Explore session.
+ * Covers saved chart identity and transient editing context; excludes raw
+ * form-data internals and datasource-implementation details.
+ */
+export interface ChartContext {
+  /** The saved chart id, or `null` when the chart has not been persisted. */
+  chartId: number | null;
+  /** Display name of the saved chart, or `null` for a new/unsaved chart. */
+  chartName: string | null;
+  /** The visualization type currently selected in the editor. */
+  vizType: string;
+  /** Id of the datasource backing the chart (physical or virtual dataset). */
+  datasourceId: number | null;
+  /** Human-readable datasource name. */
+  datasourceName: string | null;
+}
+
+/**
+ * Returns the normalized chart context for the active Explore session, or
+ * `undefined` when the user is not on the Explore page.
+ *
+ * @example
+ * ```typescript
+ * const chart = explore.getCurrentChart();
+ * if (chart) {
+ *   console.log(chart.vizType, chart.chartName);
+ * }
+ * ```
+ */
+export declare function getCurrentChart(): ChartContext | undefined;
+
+/**
+ * Event fired when the chart context changes within the active Explore session
+ * (e.g. when the viz type, datasource, or saved name changes).
+ * Not fired during route changes — subscribe to `navigation.onDidChangePage` 
for those.
+ *
+ * @example
+ * ```typescript
+ * const sub = explore.onDidChangeChart(chart => {
+ *   chatbot.updateContext({ chart });
+ * });
+ * sub.dispose();
+ * ```
+ */
+export declare const onDidChangeChart: Event<ChartContext>;
diff --git a/superset-frontend/packages/superset-core/src/index.ts 
b/superset-frontend/packages/superset-core/src/index.ts
index 75863372409..79c699caff4 100644
--- a/superset-frontend/packages/superset-core/src/index.ts
+++ b/superset-frontend/packages/superset-core/src/index.ts
@@ -19,9 +19,13 @@
 export * as common from './common';
 export * as authentication from './authentication';
 export * as commands from './commands';
+export * as dashboard from './dashboard';
+export * as dataset from './dataset';
 export * as editors from './editors';
+export * as explore from './explore';
 export * as extensions from './extensions';
 export * as menus from './menus';
+export * as navigation from './navigation';
 export * as sqlLab from './sqlLab';
 export * as views from './views';
 export * as contributions from './contributions';
diff --git a/superset-frontend/packages/superset-core/src/navigation/index.ts 
b/superset-frontend/packages/superset-core/src/navigation/index.ts
new file mode 100644
index 00000000000..b1aecf6375f
--- /dev/null
+++ b/superset-frontend/packages/superset-core/src/navigation/index.ts
@@ -0,0 +1,71 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * @fileoverview Navigation namespace for Superset extensions (P3).
+ *
+ * Exposes the current application surface so extensions can react to route
+ * changes without polling. Entity-level context (chart, dashboard, dataset)
+ * is intentionally not included here — use the surface-specific namespace
+ * (`explore`, `dashboard`, `dataset`) to retrieve entity payloads.
+ */
+
+import { Event } from '../common';
+
+/**
+ * The set of top-level application surfaces.
+ * `'other'` covers any route not explicitly enumerated.
+ */
+export type PageType =
+  | 'dashboard'
+  | 'explore'
+  | 'sqllab'
+  | 'dataset'
+  | 'home'
+  | 'other';
+
+/**
+ * Returns the current page surface type.
+ *
+ * @example
+ * ```typescript
+ * const pageType = navigation.getPageType();
+ * if (pageType === 'dashboard') {
+ *   const ctx = dashboard.getCurrentDashboard();
+ * }
+ * ```
+ */
+export declare function getPageType(): PageType;
+
+/**
+ * Event fired whenever the user navigates to a different surface.
+ * Use the surface-specific namespace to read entity context after the event.
+ *
+ * @example
+ * ```typescript
+ * const sub = navigation.onDidChangePage(pageType => {
+ *   if (pageType === 'dashboard') {
+ *     const ctx = dashboard.getCurrentDashboard();
+ *   }
+ * });
+ * // later:
+ * sub.dispose();
+ * ```
+ */
+export declare const onDidChangePage: Event<PageType>;
diff --git a/superset-frontend/src/core/dashboard/index.ts 
b/superset-frontend/src/core/dashboard/index.ts
new file mode 100644
index 00000000000..fbf17f3fa62
--- /dev/null
+++ b/superset-frontend/src/core/dashboard/index.ts
@@ -0,0 +1,90 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Host-internal implementation of the `dashboard` namespace.
+ *
+ * Wraps Redux dashboardInfo and dataMask state and normalizes them into the
+ * stable `DashboardContext` contract. Extensions must not depend on the Redux
+ * slice structure directly.
+ */
+
+import type { dashboard as dashboardApi } from '@apache-superset/core';
+import { HYDRATE_DASHBOARD } from 'src/dashboard/actions/hydrate';
+import {
+  UPDATE_DATA_MASK,
+  SET_DATA_MASK_FOR_FILTER_CHANGES_COMPLETE,
+} from 'src/dataMask/actions';
+import { store, RootState } from 'src/views/store';
+import { AnyListenerPredicate } from '@reduxjs/toolkit';
+import { createActionListener } from '../utils';
+
+type DashboardContext = dashboardApi.DashboardContext;
+type FilterValue = dashboardApi.FilterValue;
+
+function buildDashboardContext(): DashboardContext | undefined {
+  const state = store.getState();
+  const info = (state as any).dashboardInfo;
+  if (!info?.id) return undefined;
+
+  const nativeFilters = (state as any).nativeFilters?.filters ?? {};
+  const dataMask = (state as any).dataMask ?? {};
+
+  const filters: FilterValue[] = Object.entries(dataMask)
+    .filter(([id, mask]: [string, any]) => {
+      if (!(id in nativeFilters)) return false;
+      const value = mask?.filterState?.value;
+      return value !== null && value !== undefined;
+    })
+    .map(([id, mask]: [string, any]) => ({
+      filterId: id,
+      label: nativeFilters[id]?.name ?? id,
+      value: mask.filterState.value,
+    }));
+
+  return {
+    dashboardId: info.id as number,
+    title: info.dashboard_title ?? info.slug ?? String(info.id),
+    filters,
+  };
+}
+
+const dashboardChangePredicate: AnyListenerPredicate<RootState> = action =>
+  action.type === HYDRATE_DASHBOARD ||
+  action.type === UPDATE_DATA_MASK ||
+  action.type === SET_DATA_MASK_FOR_FILTER_CHANGES_COMPLETE;
+
+const getCurrentDashboard: typeof dashboardApi.getCurrentDashboard = () =>
+  buildDashboardContext();
+
+const onDidChangeDashboard: typeof dashboardApi.onDidChangeDashboard = (
+  listener: (ctx: DashboardContext) => void,
+  thisArgs?: any,
+) =>
+  createActionListener<DashboardContext>(
+    dashboardChangePredicate,
+    listener,
+    () => buildDashboardContext() ?? null,
+    thisArgs,
+  );
+
+export const dashboard: typeof dashboardApi = {
+  getCurrentDashboard,
+  onDidChangeDashboard,
+};
diff --git a/superset-frontend/src/core/dataset/index.ts 
b/superset-frontend/src/core/dataset/index.ts
new file mode 100644
index 00000000000..3c98d79454b
--- /dev/null
+++ b/superset-frontend/src/core/dataset/index.ts
@@ -0,0 +1,62 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Host-internal implementation of the `dataset` namespace.
+ *
+ * Dataset page components call `setCurrentDataset` to publish context as they
+ * load. Extensions consume the stable `DatasetContext` contract; they are
+ * isolated from the page's internal data-fetching implementation.
+ */
+
+import type { dataset as datasetApi } from '@apache-superset/core';
+import { Disposable } from '../models';
+
+type DatasetContext = datasetApi.DatasetContext;
+
+let currentDataset: DatasetContext | undefined;
+const listeners = new Set<(ctx: DatasetContext) => void>();
+
+/**
+ * Host-internal: called by the Dataset page when its entity loads or changes.
+ * Not part of the public `@apache-superset/core` API.
+ */
+export const setCurrentDataset = (ctx: DatasetContext | undefined): void => {
+  currentDataset = ctx;
+  if (ctx) {
+    listeners.forEach(fn => fn(ctx));
+  }
+};
+
+const getCurrentDataset: typeof datasetApi.getCurrentDataset = () =>
+  currentDataset ? { ...currentDataset } : undefined;
+
+const onDidChangeDataset: typeof datasetApi.onDidChangeDataset = (
+  listener: (ctx: DatasetContext) => void,
+  thisArgs?: any,
+): Disposable => {
+  const bound = thisArgs ? listener.bind(thisArgs) : listener;
+  listeners.add(bound);
+  return new Disposable(() => listeners.delete(bound));
+};
+
+export const dataset: typeof datasetApi = {
+  getCurrentDataset,
+  onDidChangeDataset,
+};
diff --git a/superset-frontend/src/core/explore/index.ts 
b/superset-frontend/src/core/explore/index.ts
new file mode 100644
index 00000000000..46b6b2ef84e
--- /dev/null
+++ b/superset-frontend/src/core/explore/index.ts
@@ -0,0 +1,84 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Host-internal implementation of the `explore` namespace.
+ *
+ * Wraps Redux explore state and normalizes it into the stable `ChartContext`
+ * contract. Extensions must not depend on the Redux slice structure directly.
+ */
+
+import type { explore as exploreApi } from '@apache-superset/core';
+import { HYDRATE_EXPLORE } from 'src/explore/actions/hydrateExplore';
+import {
+  SET_FORM_DATA,
+  UPDATE_CHART_TITLE,
+} from 'src/explore/actions/exploreActions';
+import { SET_DATASOURCE } from 'src/explore/actions/datasourcesActions';
+import { store, RootState } from 'src/views/store';
+import { AnyListenerPredicate } from '@reduxjs/toolkit';
+import { createActionListener } from '../utils';
+
+type ChartContext = exploreApi.ChartContext;
+
+function buildChartContext(): ChartContext | undefined {
+  const state = store.getState();
+  const exploreState = (state as any).explore;
+  if (!exploreState) return undefined;
+
+  const { slice, datasource, controls } = exploreState;
+  const vizType: string =
+    (controls?.viz_type?.value as string) ??
+    exploreState.form_data?.viz_type ??
+    '';
+
+  return {
+    chartId: slice?.slice_id ?? null,
+    chartName: exploreState.sliceName ?? slice?.slice_name ?? null,
+    vizType,
+    datasourceId: datasource?.id ?? null,
+    datasourceName:
+      datasource?.table_name ?? datasource?.datasource_name ?? null,
+  };
+}
+
+const exploreChangePredicate: AnyListenerPredicate<RootState> = action =>
+  action.type === HYDRATE_EXPLORE ||
+  action.type === SET_FORM_DATA ||
+  action.type === UPDATE_CHART_TITLE ||
+  action.type === SET_DATASOURCE;
+
+const getCurrentChart: typeof exploreApi.getCurrentChart = () =>
+  buildChartContext();
+
+const onDidChangeChart: typeof exploreApi.onDidChangeChart = (
+  listener: (ctx: ChartContext) => void,
+  thisArgs?: any,
+) =>
+  createActionListener<ChartContext>(
+    exploreChangePredicate,
+    listener,
+    () => buildChartContext() ?? null,
+    thisArgs,
+  );
+
+export const explore: typeof exploreApi = {
+  getCurrentChart,
+  onDidChangeChart,
+};
diff --git a/superset-frontend/src/core/index.ts 
b/superset-frontend/src/core/index.ts
index 6a106ebe87a..d259597457c 100644
--- a/superset-frontend/src/core/index.ts
+++ b/superset-frontend/src/core/index.ts
@@ -28,10 +28,14 @@ export const core: typeof coreType = {
 
 export * from './authentication';
 export * from './commands';
+export * from './dashboard';
+export * from './dataset';
 export * from './editors';
+export * from './explore';
 export * from './extensions';
 export * from './menus';
 export * from './models';
+export * from './navigation';
 export * from './sqlLab';
 export * from './utils';
 export * from './views';
diff --git a/superset-frontend/src/core/navigation/index.ts 
b/superset-frontend/src/core/navigation/index.ts
new file mode 100644
index 00000000000..96e87754f9e
--- /dev/null
+++ b/superset-frontend/src/core/navigation/index.ts
@@ -0,0 +1,69 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Host-internal implementation of the `navigation` namespace.
+ *
+ * Backed by browser location — no Redux dependency.
+ * The app shell calls `notifyPageChange(pathname)` whenever the route changes.
+ */
+
+import type { navigation as navigationApi } from '@apache-superset/core';
+import { Disposable } from '../models';
+
+type PageType = navigationApi.PageType;
+
+const listeners = new Set<(pageType: PageType) => void>();
+
+function derivePageType(pathname: string): PageType {
+  if (pathname.startsWith('/superset/dashboard/')) return 'dashboard';
+  if (pathname.startsWith('/explore/')) return 'explore';
+  if (pathname.startsWith('/superset/explore/')) return 'explore';
+  if (pathname.startsWith('/chart/add')) return 'explore';
+  if (pathname.startsWith('/sqllab/')) return 'sqllab';
+  if (pathname.startsWith('/dataset/')) return 'dataset';
+  if (pathname.startsWith('/superset/welcome/')) return 'home';
+  return 'other';
+}
+
+let currentPageType: PageType = derivePageType(window.location.pathname);
+
+/** Called by ExtensionsStartup whenever the React Router location changes. */
+export const notifyPageChange = (pathname: string): void => {
+  const next = derivePageType(pathname);
+  if (next === currentPageType) return;
+  currentPageType = next;
+  listeners.forEach(fn => fn(next));
+};
+
+const getPageType: typeof navigationApi.getPageType = () => currentPageType;
+
+const onDidChangePage: typeof navigationApi.onDidChangePage = (
+  listener: (pageType: PageType) => void,
+  thisArgs?: any,
+): Disposable => {
+  const bound = thisArgs ? listener.bind(thisArgs) : listener;
+  listeners.add(bound);
+  return new Disposable(() => listeners.delete(bound));
+};
+
+export const navigation: typeof navigationApi = {
+  getPageType,
+  onDidChangePage,
+};

Reply via email to