mirror of
https://github.com/olehomelchenko/astrolabe-nvc.git
synced 2025-12-21 21:22:23 +00:00
refactor: initialize snippets storage with sample data and improve import handling
This commit is contained in:
296
sample-data.json
Normal file
296
sample-data.json
Normal file
@@ -0,0 +1,296 @@
|
|||||||
|
{
|
||||||
|
"version": "1.0",
|
||||||
|
"exportedAt": "2025-10-19T17:08:27.358Z",
|
||||||
|
"exportedBy": "Astrolabe",
|
||||||
|
"snippets": [
|
||||||
|
{
|
||||||
|
"id": 1760877277665.0476,
|
||||||
|
"name": "Inline Data Bar Chart (Sample)",
|
||||||
|
"created": "2025-10-19T12:34:36.985Z",
|
||||||
|
"modified": "2025-10-19T17:07:38.044Z",
|
||||||
|
"spec": {
|
||||||
|
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
|
||||||
|
"description": "A simple bar chart with embedded data.",
|
||||||
|
"data": {
|
||||||
|
"values": [
|
||||||
|
{
|
||||||
|
"category": "A",
|
||||||
|
"value": 28
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "B",
|
||||||
|
"value": 55
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "C",
|
||||||
|
"value": 43
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "D",
|
||||||
|
"value": 91
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "E",
|
||||||
|
"value": 81
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "F",
|
||||||
|
"value": 53
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "G",
|
||||||
|
"value": 19
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "H",
|
||||||
|
"value": 87
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"mark": "bar",
|
||||||
|
"encoding": {
|
||||||
|
"x": {
|
||||||
|
"field": "category",
|
||||||
|
"type": "nominal",
|
||||||
|
"axis": {
|
||||||
|
"labelAngle": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"y": {
|
||||||
|
"field": "value",
|
||||||
|
"type": "quantitative"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"draftSpec": {
|
||||||
|
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
|
||||||
|
"description": "A simple bar chart with embedded data.",
|
||||||
|
"data": {
|
||||||
|
"values": [
|
||||||
|
{
|
||||||
|
"category": "A",
|
||||||
|
"value": 28
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "B",
|
||||||
|
"value": 55
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "C",
|
||||||
|
"value": 43
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "D",
|
||||||
|
"value": 91
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "E",
|
||||||
|
"value": 81
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "F",
|
||||||
|
"value": 53
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "G",
|
||||||
|
"value": 19
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "H",
|
||||||
|
"value": 87
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"mark": "bar",
|
||||||
|
"encoding": {
|
||||||
|
"x": {
|
||||||
|
"field": "category",
|
||||||
|
"type": "nominal",
|
||||||
|
"axis": {
|
||||||
|
"labelAngle": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"y": {
|
||||||
|
"field": "value",
|
||||||
|
"type": "quantitative"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"comment": "",
|
||||||
|
"tags": [],
|
||||||
|
"datasetRefs": [],
|
||||||
|
"meta": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1760891255058.404,
|
||||||
|
"name": "World Population Area Chart (Sample)",
|
||||||
|
"created": "2025-10-19T16:27:34.366Z",
|
||||||
|
"modified": "2025-10-19T16:44:48.633Z",
|
||||||
|
"spec": {
|
||||||
|
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
|
||||||
|
"data": {
|
||||||
|
"name": "World Population (Sample)"
|
||||||
|
},
|
||||||
|
"width": "container",
|
||||||
|
"height": "container",
|
||||||
|
"mark": "area",
|
||||||
|
"encoding": {
|
||||||
|
"x": {
|
||||||
|
"field": "Year",
|
||||||
|
"timeUnit": "year"
|
||||||
|
},
|
||||||
|
"y": {
|
||||||
|
"field": "Growth",
|
||||||
|
"type": "quantitative"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"draftSpec": {
|
||||||
|
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
|
||||||
|
"data": {
|
||||||
|
"name": "World Population (Sample)"
|
||||||
|
},
|
||||||
|
"width": "container",
|
||||||
|
"height": "container",
|
||||||
|
"mark": "area",
|
||||||
|
"encoding": {
|
||||||
|
"x": {
|
||||||
|
"field": "Year",
|
||||||
|
"timeUnit": "year"
|
||||||
|
},
|
||||||
|
"y": {
|
||||||
|
"field": "Growth",
|
||||||
|
"type": "quantitative"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"comment": "Visualization using dataset: World Population (Sample)",
|
||||||
|
"tags": [],
|
||||||
|
"datasetRefs": [
|
||||||
|
"World Population (Sample)"
|
||||||
|
],
|
||||||
|
"meta": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1760891802060.8884,
|
||||||
|
"name": "CO2 Emissions Line Chart (Sample)",
|
||||||
|
"created": "2025-10-19T16:36:41.432Z",
|
||||||
|
"modified": "2025-10-19T16:45:32.295Z",
|
||||||
|
"spec": {
|
||||||
|
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
|
||||||
|
"data": {
|
||||||
|
"name": "CO2 Concentration (Sample)"
|
||||||
|
},
|
||||||
|
"width": 200,
|
||||||
|
"height": 600,
|
||||||
|
"mark": "line",
|
||||||
|
"encoding": {
|
||||||
|
"x": {
|
||||||
|
"field": "Date",
|
||||||
|
"timeUnit": "month"
|
||||||
|
},
|
||||||
|
"color": {
|
||||||
|
"field": "Date",
|
||||||
|
"timeUnit": "year",
|
||||||
|
"type": "ordinal"
|
||||||
|
},
|
||||||
|
"y": {
|
||||||
|
"field": "CO2",
|
||||||
|
"type": "quantitative",
|
||||||
|
"scale": {
|
||||||
|
"zero": false
|
||||||
|
},
|
||||||
|
"aggregate": "average"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"draftSpec": {
|
||||||
|
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
|
||||||
|
"data": {
|
||||||
|
"name": "CO2 Concentration (Sample)"
|
||||||
|
},
|
||||||
|
"width": 200,
|
||||||
|
"height": 600,
|
||||||
|
"mark": "line",
|
||||||
|
"encoding": {
|
||||||
|
"x": {
|
||||||
|
"field": "Date",
|
||||||
|
"timeUnit": "month"
|
||||||
|
},
|
||||||
|
"color": {
|
||||||
|
"field": "Date",
|
||||||
|
"timeUnit": "year",
|
||||||
|
"type": "ordinal"
|
||||||
|
},
|
||||||
|
"y": {
|
||||||
|
"field": "CO2",
|
||||||
|
"type": "quantitative",
|
||||||
|
"scale": {
|
||||||
|
"zero": false
|
||||||
|
},
|
||||||
|
"aggregate": "average"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"comment": "Visualization using dataset: CO2 Concentration (Sample)",
|
||||||
|
"tags": [],
|
||||||
|
"datasetRefs": [
|
||||||
|
"CO2 Concentration (Sample)"
|
||||||
|
],
|
||||||
|
"meta": {}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"datasets": [
|
||||||
|
{
|
||||||
|
"id": 1760891191833.647,
|
||||||
|
"name": "World Population (Sample)",
|
||||||
|
"created": "2025-10-19T16:26:31.174Z",
|
||||||
|
"modified": "2025-10-19T16:26:31.174Z",
|
||||||
|
"data": "Year\tPopulation\tGrowth\n1951\t2543130380\t0.0175\n1952\t2590270899\t0.0185\n1953\t2640278797\t0.0193\n1954\t2691979339\t0.0196\n1955\t2746072141\t0.0201\n1956\t2801002631\t0.0200\n1957\t2857866857\t0.0203\n1958\t2916108097\t0.0204\n1959\t2970292188\t0.0186\n1960\t3019233434\t0.0165\n1961\t3068370609\t0.0163\n1962\t3126686743\t0.0190\n1963\t3195779247\t0.0221\n1964\t3267212338\t0.0224\n1965\t3337111983\t0.0214\n1966\t3406417036\t0.0208\n1967\t3475448166\t0.0203\n1968\t3546810808\t0.0205\n1969\t3620655275\t0.0208\n1970\t3695390336\t0.0206\n1971\t3770163092\t0.0202\n1972\t3844800885\t0.0198\n1973\t3920251504\t0.0196\n1974\t3995517077\t0.0192\n1975\t4069437231\t0.0185\n1976\t4142505882\t0.0180\n1977\t4215772490\t0.0177\n1978\t4289657708\t0.0175\n1979\t4365582871\t0.0177\n1980\t4444007706\t0.0180\n1981\t4524627658\t0.0181\n1982\t4607984871\t0.0184\n1983\t4691884238\t0.0182\n1984\t4775836074\t0.0179\n1985\t4861730613\t0.0180\n1986\t4950063339\t0.0182\n1987\t5040984495\t0.0184\n1988\t5132293974\t0.0181\n1989\t5223704308\t0.0178\n1990\t5316175862\t0.0177\n1991\t5406245867\t0.0169\n1992\t5492686093\t0.0160\n1993\t5577433523\t0.0154\n1994\t5660727993\t0.0149\n1995\t5743219454\t0.0146\n1996\t5825145298\t0.0143\n1997\t5906481261\t0.0140\n1998\t5987312480\t0.0137\n1999\t6067758458\t0.0134\n2000\t6148898975\t0.0134\n2001\t6230746982\t0.0133\n2002\t6312407360\t0.0131\n2003\t6393898365\t0.0129\n2004\t6475751478\t0.0128\n2005\t6558176119\t0.0127\n2006\t6641416218\t0.0127\n2007\t6725948544\t0.0127\n2008\t6811597272\t0.0127\n2009\t6898305908\t0.0127\n2010\t6985603105\t0.0127\n2011\t7073125425\t0.0125\n2012\t7161697921\t0.0125\n2013\t7250593370\t0.0124\n2014\t7339013419\t0.0122\n2015\t7426597537\t0.0119\n2016\t7513474238\t0.0117\n2017\t7599822404\t0.0115\n2018\t7683789828\t0.0110\n2019\t7764951032\t0.0106\n2020\t7840952880\t0.0098\n2021\t7909295151\t0.0087\n2022\t7975105156\t0.0083\n2023\t8045311447\t0.0088",
|
||||||
|
"format": "tsv",
|
||||||
|
"source": "inline",
|
||||||
|
"comment": "Sample dataset from Wiki: https://en.wikipedia.org/wiki/World_population",
|
||||||
|
"rowCount": 73,
|
||||||
|
"columnCount": 3,
|
||||||
|
"columns": [
|
||||||
|
"Year",
|
||||||
|
"Population",
|
||||||
|
"Growth"
|
||||||
|
],
|
||||||
|
"columnTypes": [
|
||||||
|
{
|
||||||
|
"name": "Year",
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Population",
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Growth",
|
||||||
|
"type": "number"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"size": 1701
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1760891542328.1973,
|
||||||
|
"name": "CO2 Concentration (Sample)",
|
||||||
|
"created": "2025-10-19T16:32:21.911Z",
|
||||||
|
"modified": "2025-10-19T16:32:46.230Z",
|
||||||
|
"data": "https://raw.githubusercontent.com/vega/vega-datasets/refs/heads/main/data/co2-concentration.csv",
|
||||||
|
"format": "csv",
|
||||||
|
"source": "url",
|
||||||
|
"comment": "",
|
||||||
|
"rowCount": 741,
|
||||||
|
"columnCount": 3,
|
||||||
|
"columns": [
|
||||||
|
"Date",
|
||||||
|
"CO2",
|
||||||
|
"adjusted CO2"
|
||||||
|
],
|
||||||
|
"columnTypes": [],
|
||||||
|
"size": 18547
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -8,9 +8,8 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
const theme = getSetting('ui.theme') || 'light';
|
const theme = getSetting('ui.theme') || 'light';
|
||||||
document.documentElement.setAttribute('data-theme', theme);
|
document.documentElement.setAttribute('data-theme', theme);
|
||||||
|
|
||||||
// Initialize snippet storage and render list
|
// Initialize snippet storage and render list (async)
|
||||||
initializeSnippetsStorage();
|
initializeSnippetsStorage().then(() => {
|
||||||
|
|
||||||
// Initialize sort controls
|
// Initialize sort controls
|
||||||
initializeSortControls();
|
initializeSortControls();
|
||||||
|
|
||||||
@@ -29,6 +28,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
selectSnippet(firstSnippet.id);
|
selectSnippet(firstSnippet.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Load saved layout
|
// Load saved layout
|
||||||
loadLayoutFromStorage();
|
loadLayoutFromStorage();
|
||||||
|
|||||||
@@ -280,12 +280,27 @@ const SnippetStorage = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Initialize storage with default snippet if empty
|
// Initialize storage with sample data from JSON file if empty
|
||||||
function initializeSnippetsStorage() {
|
async function initializeSnippetsStorage() {
|
||||||
const existingSnippets = SnippetStorage.loadSnippets();
|
const existingSnippets = SnippetStorage.loadSnippets();
|
||||||
|
|
||||||
if (existingSnippets.length === 0) {
|
if (existingSnippets.length === 0) {
|
||||||
// Create default snippet using the sample spec from config
|
// Try loading sample data from JSON file
|
||||||
|
try {
|
||||||
|
const response = await fetch('sample-data.json');
|
||||||
|
if (response.ok) {
|
||||||
|
const sampleData = await response.json();
|
||||||
|
const result = await processImportedData(sampleData, { silent: true });
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
return result.normalizedSnippets;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('Failed to load sample-data.json, using fallback:', error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback: create default snippet using the sample spec from config
|
||||||
const defaultSnippet = createSnippet(sampleSpec, "Sample Bar Chart");
|
const defaultSnippet = createSnippet(sampleSpec, "Sample Bar Chart");
|
||||||
defaultSnippet.comment = "A simple bar chart showing category values";
|
defaultSnippet.comment = "A simple bar chart showing category values";
|
||||||
|
|
||||||
@@ -1296,15 +1311,9 @@ function estimateImportFit(existingSnippets, newSnippets) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Import snippets and datasets from JSON file
|
// Core logic to process imported data (shared between file import and initial sample data)
|
||||||
function importSnippets(fileInput) {
|
async function processImportedData(importedData, options = {}) {
|
||||||
const file = fileInput.files[0];
|
const { silent = false } = options;
|
||||||
if (!file) return;
|
|
||||||
|
|
||||||
const reader = new FileReader();
|
|
||||||
reader.onload = async function(e) {
|
|
||||||
try {
|
|
||||||
const importedData = JSON.parse(e.target.result);
|
|
||||||
|
|
||||||
// Detect format: legacy (array) or unified (object with version)
|
// Detect format: legacy (array) or unified (object with version)
|
||||||
let snippetsToImport = [];
|
let snippetsToImport = [];
|
||||||
@@ -1323,8 +1332,8 @@ function importSnippets(fileInput) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (snippetsToImport.length === 0) {
|
if (snippetsToImport.length === 0) {
|
||||||
Toast.info('No snippets found in file');
|
if (!silent) Toast.info('No snippets found in file');
|
||||||
return;
|
return { success: false };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Import datasets first (if any)
|
// Import datasets first (if any)
|
||||||
@@ -1387,7 +1396,7 @@ function importSnippets(fileInput) {
|
|||||||
// Estimate storage fit
|
// Estimate storage fit
|
||||||
const fit = estimateImportFit(existingSnippets, normalizedSnippets);
|
const fit = estimateImportFit(existingSnippets, normalizedSnippets);
|
||||||
|
|
||||||
if (!fit.willFit) {
|
if (!fit.willFit && !silent) {
|
||||||
Toast.warning(
|
Toast.warning(
|
||||||
`⚠️ Import is ${formatBytes(fit.overageBytes)} over the 5 MB limit. Attempting to load...`,
|
`⚠️ Import is ${formatBytes(fit.overageBytes)} over the 5 MB limit. Attempting to load...`,
|
||||||
5000
|
5000
|
||||||
@@ -1398,6 +1407,7 @@ function importSnippets(fileInput) {
|
|||||||
const allSnippets = existingSnippets.concat(normalizedSnippets);
|
const allSnippets = existingSnippets.concat(normalizedSnippets);
|
||||||
|
|
||||||
if (SnippetStorage.saveSnippets(allSnippets)) {
|
if (SnippetStorage.saveSnippets(allSnippets)) {
|
||||||
|
if (!silent) {
|
||||||
let message = `Imported ${snippetsImported} snippet${snippetsImported !== 1 ? 's' : ''}`;
|
let message = `Imported ${snippetsImported} snippet${snippetsImported !== 1 ? 's' : ''}`;
|
||||||
if (datasetsImported > 0) {
|
if (datasetsImported > 0) {
|
||||||
message += ` and ${datasetsImported} dataset${datasetsImported !== 1 ? 's' : ''}`;
|
message += ` and ${datasetsImported} dataset${datasetsImported !== 1 ? 's' : ''}`;
|
||||||
@@ -1414,19 +1424,36 @@ function importSnippets(fileInput) {
|
|||||||
Toast.success(message);
|
Toast.success(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Track event
|
||||||
|
Analytics.track('project-import', `Import ${snippetsImported} snippets, ${datasetsImported} datasets`);
|
||||||
|
}
|
||||||
|
|
||||||
renderSnippetList();
|
renderSnippetList();
|
||||||
updateStorageMonitor();
|
updateStorageMonitor();
|
||||||
|
|
||||||
// Track event
|
return { success: true, snippetsImported, datasetsImported, normalizedSnippets };
|
||||||
Analytics.track('project-import', `Import ${snippetsImported} snippets, ${datasetsImported} datasets`);
|
|
||||||
} else {
|
} else {
|
||||||
const overageBytes = fit.overageBytes > 0 ? fit.overageBytes : calculateDataSize(allSnippets) - STORAGE_LIMIT_BYTES;
|
const overageBytes = fit.overageBytes > 0 ? fit.overageBytes : calculateDataSize(allSnippets) - STORAGE_LIMIT_BYTES;
|
||||||
|
if (!silent) {
|
||||||
Toast.error(
|
Toast.error(
|
||||||
`Storage quota exceeded by ${formatBytes(overageBytes)}. Please delete some snippets and try again.`,
|
`Storage quota exceeded by ${formatBytes(overageBytes)}. Please delete some snippets and try again.`,
|
||||||
6000
|
6000
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
return { success: false };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Import snippets and datasets from JSON file
|
||||||
|
function importSnippets(fileInput) {
|
||||||
|
const file = fileInput.files[0];
|
||||||
|
if (!file) return;
|
||||||
|
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = async function(e) {
|
||||||
|
try {
|
||||||
|
const importedData = JSON.parse(e.target.result);
|
||||||
|
await processImportedData(importedData);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Import error:', error);
|
console.error('Import error:', error);
|
||||||
Toast.error('Failed to import. Please check that the file is valid JSON.');
|
Toast.error('Failed to import. Please check that the file is valid JSON.');
|
||||||
|
|||||||
Reference in New Issue
Block a user