Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/auto-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:

strategy:
matrix:
node-version: [16.x]
node-version: [22.x]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/

steps:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:

strategy:
matrix:
node-version: [16.x, 18.x]
node-version: [22.x]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/

steps:
Expand All @@ -29,7 +29,7 @@ jobs:
chmod u+x ./scripts/compile.sh
./scripts/compile.sh
- name: Uploading dist
if: ${{ matrix.node-version == '16.x' }}
if: ${{ matrix.node-version == '22.x' }}
uses: actions/upload-artifact@v4
with:
name: pygwalker-app
Expand Down
40 changes: 20 additions & 20 deletions app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,17 @@
"@anywidget/react": "^0.0.8",
"@headlessui/react": "^1.7.14",
"@heroicons/react": "^2.0.8",
"@kanaries/graphic-walker": "0.4.70",
"@kanaries/graphic-walker": "0.5.0-alpha.2",
"@kanaries/gw-dsl-parser": "0.1.49",
"@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-dialog": "^1.0.5",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-select": "^2.0.0",
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-tabs": "^1.0.4",
"@radix-ui/react-toggle": "^1.0.3",
"@radix-ui/react-toggle-group": "^1.0.4",
"@radix-ui/react-checkbox": "^1.3.3",
"@radix-ui/react-dialog": "^1.1.15",
"@radix-ui/react-icons": "^1.3.2",
"@radix-ui/react-label": "^2.1.8",
"@radix-ui/react-select": "^2.2.6",
"@radix-ui/react-slot": "^1.2.4",
"@radix-ui/react-tabs": "^1.1.13",
"@radix-ui/react-toggle": "^1.1.10",
"@radix-ui/react-toggle-group": "^1.1.11",
"@segment/analytics-next": "^1.69.0",
"autoprefixer": "^10.3.5",
"buffer": "^6.0.3",
Expand All @@ -39,24 +39,24 @@
"mobx": "^6.9.0",
"mobx-react-lite": "^3.4.3",
"postcss": "^8.3.7",
"react": "^18.x",
"react-dom": "^18.x",
"react": "19.x",
"react-dom": "19.x",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

React version conflict between dependencies and resolutions

The dependencies section specifies react and react-dom at version 19.x, but the resolutions section forces ^18.x for both. Yarn's resolutions field overrides nested dependency versions across the entire dependency tree, creating a direct conflict with the top-level dependency declaration. This mismatch can cause installation failures, runtime errors, or the notorious "multiple React versions" problem where hooks and context fail silently. The @types/react in devDependencies also remains at ^18.x, further exacerbating type compatibility issues with React 19.

Additional Locations (1)

Fix in Cursor Fix in Web

"react-syntax-highlighter": "^15.5.0",
"streamlit-component-lib": "^2.0.0",
"styled-components": "^5.3.6",
"tailwind-merge": "^1.14.0",
"tailwindcss": "^3.2.4",
"tailwindcss-animate": "^1.0.7",
"uuid": "^8.3.2",
"streamlit-component-lib": "^2.0.0"
"uuid": "^8.3.2"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^24.0.x",
"@rollup/plugin-replace": "^5.0.x",
"@rollup/plugin-terser": "^0.4.x",
"@rollup/plugin-typescript": "^11.0.x",
"@types/node": "^20.7.2",
"@types/react": "^18.x",
"@types/react-dom": "^18.x",
"@types/react": "^19.x",
"@types/react-dom": "^19.x",
"@types/react-syntax-highlighter": "^15.5.7",
"@types/styled-components": "^5.1.26",
"@vitejs/plugin-react": "^3.1.x",
Expand All @@ -65,10 +65,10 @@
"vite-plugin-wasm": "^3.2.2"
},
"resolutions": {
"react": "^18.x",
"react-dom": "^18.x",
"@types/react": "^18.x",
"@types/react-dom": "^18.x"
"react": "^19.x",
"react-dom": "^19.x",
"@types/react": "^19.x",
"@types/react-dom": "^19.x"
},
"peerDependencies": {},
"prettier": {
Expand Down
3 changes: 2 additions & 1 deletion app/src/components/initModal/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from "react";
import { observer } from "mobx-react-lite";
import { Dialog, DialogContent } from "@/components/ui/dialog";
import { Dialog, DialogContent, DialogTitle } from "@/components/ui/dialog";

import commonStore from "../../store/common";

Expand All @@ -10,6 +10,7 @@ const InitModal: React.FC<IInitModal> = observer((props) => {
return (
<Dialog open={commonStore.initModalOpen} modal={true}>
<DialogContent hideClose>
<DialogTitle className="sr-only">{commonStore.initModalInfo.title}</DialogTitle>
<div className="flex justify-between mb-1">
<span className="text-base font-medium text-blue-700 dark:text-white">{commonStore.initModalInfo.title}</span>
<span className="text-sm font-medium text-blue-700 dark:text-white">
Expand Down
1 change: 1 addition & 0 deletions app/src/components/preview/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import style from '@/index.css?inline'

interface IPreviewProps {
gid: string;
themeKey: string;
dark: IDarkMode;
charts: {
Expand Down
90 changes: 90 additions & 0 deletions app/src/components/runcellBanner/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import React, { useState } from "react";
import { tracker } from "@/utils/tracker";

const RUNCELL_LOGO_URL = "https://www.runcell.dev/runcell-logo.svg";
const RUNCELL_WEBSITE = "https://www.runcell.dev?utm_source=pygwalker";

const LLM_LOGOS = [
"https://www.runcell.dev/llm-icons/openai.svg",
"https://www.runcell.dev/llm-icons/claude-color.svg",
"https://www.runcell.dev/llm-icons/gemini-color.svg",
"https://www.runcell.dev/llm-icons/deepseek-color.svg",
];

interface RuncellBannerProps {
env?: string;
}

const checkRuncellInstalled = (): boolean => {
try {
// Runcell JupyterLab plugin stores user status in localStorage
const runcellUser = window.parent.localStorage.getItem("plugin_auth_user_v2");
return runcellUser !== null;
} catch {
return false;
}
};

export const RuncellBanner: React.FC<RuncellBannerProps> = ({ env }) => {
const [dismissed, setDismissed] = useState(false);

// Only show in Jupyter environments
if (env !== "jupyter_widgets" && env !== "anywidget") {
return null;
}

// Don't show if runcell is installed or user dismissed
if (checkRuncellInstalled() || dismissed) {
return null;
}

const handleClick = () => {
tracker.track("click", { entity: "runcell_banner" });
window.open(RUNCELL_WEBSITE, "_blank");
};

const handleDismiss = (e: React.MouseEvent) => {
e.stopPropagation();
tracker.track("click", { entity: "runcell_banner_dismiss" });
setDismissed(true);
};

return (
<div
className="flex items-center justify-between gap-3 px-4 py-2 mb-2 rounded-md bg-gradient-to-r from-purple-50 to-blue-50 dark:from-purple-950/30 dark:to-blue-950/30 border border-purple-200 dark:border-purple-800 cursor-pointer hover:shadow-md transition-shadow"
onClick={handleClick}
>
<div className="flex items-center gap-3">
<img
src={RUNCELL_LOGO_URL}
alt="Runcell"
className="w-6 h-6"
/>
<span className="text-sm text-gray-700 dark:text-gray-300">
Enable AI Agent for data analysis with pip install runcell
</span>
</div>
<div className="flex items-center gap-2">
<div className="flex items-center gap-1 px-2 py-1 rounded-full bg-white/80">
{LLM_LOGOS.map((logo, index) => (
<img
key={index}
src={logo}
alt="LLM"
className="w-4 h-4"
/>
))}
</div>
<button
onClick={handleDismiss}
className="text-gray-400 hover:text-gray-600 dark:hover:text-gray-200 text-lg leading-none px-1"
aria-label="Dismiss"
>
×
</button>
</div>
</div>
);
};

export default RuncellBanner;
97 changes: 57 additions & 40 deletions app/src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useCallback, useContext, useEffect, useState } from 'react';
import ReactDOM from 'react-dom';
import { createRoot } from 'react-dom/client';
import { observer } from "mobx-react-lite";
import { reaction } from "mobx"
import { GraphicWalker, PureRenderer, GraphicRenderer, TableWalker } from '@kanaries/graphic-walker'
Expand Down Expand Up @@ -27,6 +27,7 @@ import InitModal from './components/initModal';
import { getSaveTool } from './tools/saveTool';
import { getExportTool } from './tools/exportTool';
import { getExportDataframeTool } from './tools/exportDataframe';
import { getRuncellTool } from './tools/runcellTool';
import { formatExportedChartDatas } from "./utils/save";
import { tracker } from "@/utils/tracker";
import Notification from "./notify"
Expand All @@ -50,6 +51,7 @@ import { currentMediaTheme } from './utils/theme';
import { AppContext, darkModeContext } from './store/context';
import FormatSpec from './utils/formatSpec';
import { getOpenDesktopTool } from './tools/openDesktop';
import RuncellBanner from './components/runcellBanner';


const initChart = async (gwRef: React.MutableRefObject<IGWHandler | null>, total: number, props: IAppProps) => {
Expand Down Expand Up @@ -208,10 +210,11 @@ const ExploreApp: React.FC<IAppProps & {initChartFlag: boolean}> = (props) => {
}, 0);
}, [mode]);

const runcellTool = getRuncellTool();
const exportTool = getExportTool(setExportOpen);
const openInDesktopTool = getOpenDesktopTool(props, storeRef);

const tools = [exportTool, openInDesktopTool];
const tools = [runcellTool, exportTool, openInDesktopTool];
if (props.env && ["jupyter_widgets", "streamlit", "gradio", "marimo", "anywidget", "web_server"].indexOf(props.env) !== -1 && props.useSaveTool) {
const saveTool = getSaveTool(props, gwRef, storeRef, isChanged, setIsChanged);
tools.push(saveTool);
Expand All @@ -228,20 +231,25 @@ const ExploreApp: React.FC<IAppProps & {initChartFlag: boolean}> = (props) => {

const enhanceAPI = React.useMemo(() => {
if (props.showCloudTool) {
return {
features: {
"askviz": async (metas: IViewField[], query: string) => {
const resp = await communicationStore.comm?.sendMsg("get_spec_by_text", { metas, query });
return resp?.data.data;
},
"vlChat": async (metas: IViewField[], chats: IChatMessage[]) => {
const resp = await communicationStore.comm?.sendMsg("get_chart_by_chats", { metas, chats });
return resp?.data.data;
}
}
const features: Record<string, any> = {};
if (props.enableAskViz) {
features["askviz"] = async (metas: IViewField[], query: string) => {
const resp = await communicationStore.comm?.sendMsg("get_spec_by_text", { metas, query });
return resp?.data.data;
};
}
if (props.enableVlChat) {
features["vlChat"] = async (metas: IViewField[], chats: IChatMessage[]) => {
const resp = await communicationStore.comm?.sendMsg("get_chart_by_chats", { metas, chats });
return resp?.data.data;
};
}
if (Object.keys(features).length > 0) {
return { features };
}
}
}, [props.showCloudTool]);
return undefined;
}, [props.showCloudTool, props.enableAskViz, props.enableVlChat]);

const computationCallback = React.useMemo(() => getComputationCallback(props), []);

Expand Down Expand Up @@ -401,6 +409,7 @@ function GWalkerComponent(props: IAppProps) {

return (
<React.StrictMode>
<RuncellBanner env={props.env} />
{ props.gwMode === "explore" && <ExploreApp {...props} dataSource={dataSource} initChartFlag={initChartFlag} /> }
{ props.gwMode === "renderer" && <PureRednererApp {...props} dataSource={dataSource} /> }
{ props.gwMode === "filter_renderer" && <GraphicRendererApp {...props} dataSource={dataSource} /> }
Expand Down Expand Up @@ -430,12 +439,14 @@ function GWalker(props: IAppProps, id: string) {
}

preRender(props).then(() => {
ReactDOM.render(
<MainApp darkMode={props.dark} gid={props.id} sendMessage={props.env?.startsWith("jupyter")}>
<GWalkerComponent {...props} />
</MainApp>,
document.getElementById(id)
);
const container = document.getElementById(id);
if (container) {
createRoot(container).render(
<MainApp darkMode={props.dark} gid={props.id} sendMessage={props.env?.startsWith("jupyter")}>
<GWalkerComponent {...props} />
</MainApp>
);
}
})
}

Expand All @@ -447,22 +458,26 @@ function PreviewApp(props: IPreviewProps, containerId: string) {
window.document.getElementById(containerId)?.remove();
}

ReactDOM.render(
<MainApp darkMode={props.dark} hideToolBar>
<Preview {...props} />
</MainApp>,
document.getElementById(containerId)
);
const container = document.getElementById(containerId);
if (container) {
createRoot(container).render(
<MainApp darkMode={props.dark} hideToolBar>
<Preview {...props} />
</MainApp>
);
}
}

function ChartPreviewApp(props: IChartPreviewProps, id: string) {
props.visSpec = FormatSpec([props.visSpec], [])[0];
ReactDOM.render(
<MainApp darkMode={props.dark} hideToolBar>
<ChartPreview {...props} />
</MainApp>,
document.getElementById(id)
);
const container = document.getElementById(id);
if (container) {
createRoot(container).render(
<MainApp darkMode={props.dark} hideToolBar>
<ChartPreview {...props} />
</MainApp>
);
}
}

function GraphicRendererApp(props: IAppProps) {
Expand Down Expand Up @@ -539,7 +554,7 @@ function TableWalkerApp(props: IAppProps) {
function SteamlitGWalkerApp(streamlitProps: any) {
const props = streamlitProps.args as IAppProps;
const [inited, setInited] = useState(false);
const container = React.useRef(null);
const container = React.useRef<HTMLDivElement>(null);
props.visSpec = FormatSpec(props.visSpec, props.rawFields);

useEffect(() => {
Expand Down Expand Up @@ -572,13 +587,15 @@ function SteamlitGWalkerApp(streamlitProps: any) {
};

const StreamlitGWalker = () => {
const StreamlitGWalker = withStreamlitConnection(SteamlitGWalkerApp);
ReactDOM.render(
<React.StrictMode>
<StreamlitGWalker />
</React.StrictMode>,
document.getElementById("root")
)
const StreamlitGWalkerComponent = withStreamlitConnection(SteamlitGWalkerApp);
const container = document.getElementById("root");
if (container) {
createRoot(container).render(
<React.StrictMode>
<StreamlitGWalkerComponent />
</React.StrictMode>
);
}
}

function AnywidgetGWalkerApp() {
Expand Down
2 changes: 2 additions & 0 deletions app/src/interfaces/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ export interface IAppProps {
needLoadDatas?: boolean;
specType: string;
showCloudTool: boolean;
enableAskViz: boolean;
enableVlChat: boolean;
needInitChart: boolean;
useKernelCalc: boolean;
useSaveTool: boolean;
Expand Down
Loading
Loading