Skip to content

Commit 6c72074

Browse files
authored
Merge pull request #620 from Kanaries/feat-compress
feat: compress all data in html
2 parents 6711936 + 763dd12 commit 6c72074

File tree

6 files changed

+41
-84
lines changed

6 files changed

+41
-84
lines changed

app/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@
3737
"lucide-react": "^0.341.0",
3838
"mobx": "^6.9.0",
3939
"mobx-react-lite": "^3.4.3",
40-
"pako": "^2.1.0",
4140
"postcss": "^8.3.7",
4241
"react": "^18.x",
4342
"react-dom": "^18.x",

app/src/components/preview/index.tsx

Lines changed: 6 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import React from "react";
22
import { observer } from "mobx-react-lite";
3-
import pako from "pako";
4-
import { PureRenderer } from '@kanaries/graphic-walker';
3+
import { PureRenderer, IRow } from '@kanaries/graphic-walker';
54
import type { IDarkMode, IThemeKey } from '@kanaries/graphic-walker/interfaces';
65

76
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
@@ -13,45 +12,12 @@ interface IPreviewProps {
1312
dark: IDarkMode;
1413
charts: {
1514
visSpec: any;
16-
data: string;
15+
data: IRow[];
1716
}[];
1817
}
1918

20-
const getInflateData = (dataStr: string) => {
21-
let binaryString = atob(dataStr);
22-
let compressed = new Uint8Array(binaryString.length);
23-
for (let i = 0; i < binaryString.length; i++) {
24-
compressed[i] = binaryString.charCodeAt(i);
25-
}
26-
const inflated = pako.inflate(compressed, {to: "string"});
27-
28-
const datas = JSON.parse(inflated);
29-
const keys = Object.keys(datas);
30-
if (keys.length === 0) {
31-
return [];
32-
}
33-
const count = datas[keys[0]].length;
34-
35-
const result = [] as any[];
36-
for (let i = 0; i < count; i++) {
37-
let item = {};
38-
keys.forEach(key => {
39-
item[key] = datas[key][i]
40-
})
41-
result.push(item);
42-
}
43-
44-
return result;
45-
}
46-
4719
const Preview: React.FC<IPreviewProps> = observer((props) => {
4820
const { charts, themeKey } = props;
49-
const formatedCharts = charts.map((chart) => {
50-
return {
51-
...chart,
52-
data: getInflateData(chart.data)
53-
}
54-
})
5521

5622
return (
5723
<React.StrictMode>
@@ -60,12 +26,12 @@ const Preview: React.FC<IPreviewProps> = observer((props) => {
6026
<Tabs defaultValue="0" className="w-full">
6127
<div className="overflow-x-auto max-w-full">
6228
<TabsList>
63-
{formatedCharts.map((chart, index) => {
29+
{charts.map((chart, index) => {
6430
return <TabsTrigger key={index} value={index.toString()}>{chart.visSpec.name}</TabsTrigger>
6531
})}
6632
</TabsList>
6733
</div>
68-
{formatedCharts.map((chart, index) => {
34+
{charts.map((chart, index) => {
6935
return <TabsContent key={index} value={index.toString()}>
7036
<PureRenderer
7137
vizThemeConfig={themeKey as IThemeKey}
@@ -89,13 +55,12 @@ interface IChartPreviewProps {
8955
themeKey: string;
9056
dark: IDarkMode;
9157
visSpec: any;
92-
data: string;
58+
data: IRow[];
9359
title: string;
9460
desc: string;
9561
}
9662

9763
const ChartPreview: React.FC<IChartPreviewProps> = observer((props) => {
98-
const formatedData = getInflateData(props.data);
9964

10065
return (
10166
<React.StrictMode>
@@ -109,7 +74,7 @@ const ChartPreview: React.FC<IChartPreviewProps> = observer((props) => {
10974
visualLayout={props.visSpec.layout}
11075
visualState={props.visSpec.encodings}
11176
type='remote'
112-
computation={async(_) => { return formatedData }}
77+
computation={async(_) => { return props.data }}
11378
appearance={props.dark as IDarkMode}
11479
/>
11580
</div>

app/yarn.lock

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5343,11 +5343,6 @@ pad-left@^2.1.0:
53435343
dependencies:
53445344
repeat-string "^1.5.4"
53455345

5346-
pako@^2.1.0:
5347-
version "2.1.0"
5348-
resolved "https://registry.yarnpkg.com/pako/-/pako-2.1.0.tgz#266cc37f98c7d883545d11335c00fbd4062c9a86"
5349-
integrity sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==
5350-
53515346
papaparse@^5.1.1:
53525347
version "5.4.1"
53535348
resolved "https://registry.yarnpkg.com/papaparse/-/papaparse-5.4.1.tgz#f45c0f871853578bd3a30f92d96fdcfb6ebea127"

pygwalker/services/preview_image.py

Lines changed: 5 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from pygwalker.utils.encode import DataFrameEncoder
1111
from pygwalker.utils.display import display_html
1212
from pygwalker.utils.randoms import generate_hash_code
13-
from pygwalker.services.render import jinja_env, GWALKER_SCRIPT_BASE64
13+
from pygwalker.services.render import jinja_env, GWALKER_SCRIPT_BASE64, compress_data
1414

1515

1616
class ImgData(BaseModel):
@@ -31,20 +31,6 @@ class ChartData(BaseModel):
3131
title: str
3232

3333

34-
def _compress_data(data: List[List[Dict[str, Any]]]) -> str:
35-
formated_data = {}
36-
if data:
37-
keys = list(data[0].keys())
38-
formated_data = {key: [] for key in keys}
39-
for item in data:
40-
for key in keys:
41-
formated_data[key].append(item[key])
42-
43-
data_json_str = json.dumps(formated_data, cls=DataFrameEncoder)
44-
data_base64_str = base64.b64encode(zlib.compress(data_json_str.encode())).decode()
45-
return data_base64_str
46-
47-
4834
def render_gw_preview_html(
4935
vis_spec_obj: List[Dict[str, Any]],
5036
datas: List[List[Dict[str, Any]]],
@@ -60,10 +46,9 @@ def render_gw_preview_html(
6046
vis_spec_obj,
6147
datas
6248
):
63-
data_base64_str = _compress_data(data)
6449
charts.append({
6550
"visSpec": vis_spec_item,
66-
"data": data_base64_str
51+
"data": data
6752
})
6853

6954
props = {"charts": charts, "themeKey": theme_key, "dark": appearance, "gid": gid}
@@ -75,7 +60,7 @@ def render_gw_preview_html(
7560
'id': container_id,
7661
'gw_script': GWALKER_SCRIPT_BASE64,
7762
"component_script": "PyGWalkerApp.PreviewApp(props, gw_id);",
78-
"props": json.dumps(props, cls=DataFrameEncoder)
63+
"props": compress_data(json.dumps(props, cls=DataFrameEncoder))
7964
},
8065
component_url=""
8166
)
@@ -97,7 +82,7 @@ def render_gw_chart_preview_html(
9782

9883
props = {
9984
"visSpec": single_vis_spec,
100-
"data": _compress_data(data),
85+
"data": data,
10186
"themeKey": theme_key,
10287
"title": title,
10388
"desc": desc,
@@ -111,7 +96,7 @@ def render_gw_chart_preview_html(
11196
'id': container_id,
11297
'gw_script': GWALKER_SCRIPT_BASE64,
11398
"component_script": "PyGWalkerApp.ChartPreviewApp(props, gw_id);",
114-
"props": json.dumps(props, cls=DataFrameEncoder)
99+
"props": compress_data(json.dumps(props, cls=DataFrameEncoder))
115100
},
116101
component_url=""
117102
)

pygwalker/services/render.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import base64
44
import html as m_html
55
from typing import Dict, List, Any, Optional
6+
import zlib
67

78
from jinja2 import Environment, PackageLoader
89

@@ -17,9 +18,17 @@
1718
autoescape=(()), # select_autoescape()
1819
)
1920

21+
22+
def compress_data(data: str) -> str:
23+
compress = zlib.compressobj(zlib.Z_BEST_COMPRESSION, zlib.DEFLATED, 15, 8, 0)
24+
compressed_data = compress.compress(data.encode())
25+
compressed_data += compress.flush()
26+
return base64.b64encode(compressed_data).decode()
27+
28+
2029
with open(os.path.join(ROOT_DIR, 'templates', 'dist', 'pygwalker-app.iife.js'), 'r', encoding='utf8') as f:
2130
GWALKER_SCRIPT = f.read()
22-
GWALKER_SCRIPT_BASE64 = base64.b64encode(GWALKER_SCRIPT.encode()).decode()
31+
GWALKER_SCRIPT_BASE64 = compress_data(GWALKER_SCRIPT)
2332

2433

2534
def get_max_limited_datas(datas: List[Dict[str, Any]], byte_limit: int) -> List[Dict[str, Any]]:
@@ -65,7 +74,7 @@ def render_gwalker_html(gid: int, props: Dict[str, Any]) -> str:
6574
'id': container_id,
6675
'gw_script': GWALKER_SCRIPT_BASE64,
6776
"component_script": "PyGWalkerApp.GWalker(props, gw_id);",
68-
"props": json.dumps(props, cls=DataFrameEncoder)
77+
"props": compress_data(json.dumps(props, cls=DataFrameEncoder)),
6978
},
7079
component_url=GlobalVarManager.component_url
7180
)

pygwalker/templates/pygwalker_main_page.html

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,30 +10,34 @@
1010
Loading Graphic-Walker UI...
1111
</div>
1212
<script>
13-
function decodeGwScript(gw_script) {
14-
var decodedBase64 = atob(gw_script);
15-
var textDecoder = new TextDecoder('utf-8');
16-
return textDecoder.decode(new Uint8Array(decodedBase64.split('').map(char => char.charCodeAt(0))));
17-
}
18-
eval(decodeGwScript("{{ gwalker.gw_script }}"));
19-
13+
var gw_script = "{{ gwalker.gw_script }}";
2014
var gw_id = "{{ gwalker.id }}";
21-
var props = {{ gwalker.props }};
22-
try{
23-
window.__GW_VERSION=props.version;
24-
{{ gwalker.component_script }};
25-
} catch(e) {
26-
console.error(e);
15+
var props_data = "{{ gwalker.props }}";
16+
async function runGwScript() {
17+
const script_stream = await fetch('data:application/octet-stream;base64,' + gw_script).then((res) => res.body.pipeThrough(new DecompressionStream('deflate')));
18+
const script = await new Response(script_stream).text();
19+
eval(script);
20+
const props_stream = await fetch('data:application/octet-stream;base64,' + props_data).then((res) => res.body.pipeThrough(new DecompressionStream('deflate')));
21+
const props = await new Response(props_stream).json();
22+
try{
23+
window.__GW_VERSION=props.version;
24+
{{ gwalker.component_script }};
25+
} catch(e) {
26+
console.error(e);
27+
}
2728
}
29+
runGwScript();
2830
</script>
2931
</body>
3032
{% endif %}
3133

3234
{% if component_url != "" %}
3335
<body>
3436
<script>
35-
window.onload = function() {
36-
const props = {{ gwalker.props }};
37+
window.onload = async function() {
38+
const props_data = "{{ gwalker.props }}";
39+
const props_stream = await fetch('data:application/octet-stream;base64,' + props_data).then((res) => res.body.pipeThrough(new DecompressionStream('deflate')));
40+
const props = await new Response(props_stream).json();
3741
const iframe = window.parent.document.getElementById("gwalker-" + props.id);
3842
iframe.onload = function() {
3943
iframe.contentWindow.postMessage({data: props, type: "pyg_props"}, "*");

0 commit comments

Comments
 (0)