Repository: incentius-foss/WhatTheDuck
Branch: main
Commit: 61bd634bef1b
Files: 32
Total size: 93.3 KB
Directory structure:
gitextract_46xnnl1r/
├── .editorconfig
├── .github/
│ └── ISSUE_TEMPLATE/
│ ├── bug_report.md
│ └── feature_request.md
├── .gitignore
├── .npmrc
├── .vscode/
│ ├── extensions.json
│ └── settings.json
├── Dockerfile
├── LICENSE.md
├── README.md
├── docker-compose.yaml
├── index.html
├── jsconfig.json
├── package.json
├── postcss.config.js
├── quasar.config.js
├── src/
│ ├── App.vue
│ ├── assets/
│ │ ├── Sample-Spreadsheet-10-rows.csv
│ │ └── customers-100.csv
│ ├── boot/
│ │ ├── .gitkeep
│ │ ├── bus.js
│ │ └── duckdb.js
│ ├── components/
│ │ └── EssentialLink.vue
│ ├── css/
│ │ └── app.css
│ ├── layouts/
│ │ └── MainLayout.vue
│ ├── pages/
│ │ ├── ErrorNotFound.vue
│ │ ├── IndexPage.vue
│ │ ├── NewIndexPage.vue
│ │ └── ace-config.ts
│ └── router/
│ ├── index.js
│ └── routes.js
└── tailwind.config.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .editorconfig
================================================
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
================================================
FILE: .gitignore
================================================
.DS_Store
.thumbs.db
node_modules
# Quasar core related directories
.quasar
/dist
# Cordova related directories and files
/src-cordova/node_modules
/src-cordova/platforms
/src-cordova/plugins
/src-cordova/www
# Capacitor related directories and files
/src-capacitor/www
/src-capacitor/node_modules
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Editor directories and files
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
================================================
FILE: .npmrc
================================================
# pnpm-related options
shamefully-hoist=true
strict-peer-dependencies=false
================================================
FILE: .vscode/extensions.json
================================================
{
"recommendations": [
"editorconfig.editorconfig",
"vue.volar",
"wayou.vscode-todo-highlight"
],
"unwantedRecommendations": [
"octref.vetur",
"hookyqr.beautify",
"dbaeumer.jshint",
"ms-vscode.vscode-typescript-tslint-plugin"
]
}
================================================
FILE: .vscode/settings.json
================================================
{
"editor.bracketPairColorization.enabled": true,
"editor.guides.bracketPairs": true
}
================================================
FILE: Dockerfile
================================================
# Use Node.js Alpine image as base
FROM node:alpine
# Set working directory in the container
WORKDIR /usr/src/app
# Copy package.json and package-lock.json to the working directory
COPY package*.json ./
# Install project dependencies
RUN npm install
RUN npm install -g @quasar/cli
# Copy the rest of the application files to the working directory
COPY . .
# Expose port 8080 to the outside world
EXPOSE 9000
# Default command to start Quasar development server
CMD ["quasar", "dev"]
================================================
FILE: LICENSE.md
================================================
MIT License
Copyright (c) 2024 incentius-foss
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
<p align="center">
<img src="/public/logo3.svg" alt="Image Description" height="120px" width="320px" />
</p>
WhatTheDuck is an open-source web application built on DuckDB, designed to allow users to upload CSV files, store them
in tables, and perform SQL queries on the uploaded data. The application also provides the functionality to download
filtered results in CSV format, supports uploading multiple files, and enables users to perform join queries. It's
important to note that the application stores data temporarily in memory, and refreshing the page will clear all
uploaded data.
> [!NOTE]
> Your data will be processed locally in your browser and won't be sent anywhere.
<a href="https://www.producthunt.com/posts/whattheduck?utm_source=badge-featured&utm_medium=badge&utm_souce=badge-whattheduck" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=404278&theme=light" alt="WhatTheDuck - Your browser based SQL engine for CSV files | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
<a href="https://incentius.com/" target="_blank"><img src="https://incentius.com/img_astro/incentius-logo.png" alt="Incentius" style="width: 56px; height: 56px;" width="56" height="56" /></a>
## Table of Contents
- [Installation](#installation)
- [Usage](#usage)
- [Configuration](#configuration)
- [Contributing](#contributing)
- [License](#license)
## Using docker
To run the application using docker, follow these steps:
```cmd
docker-compose build
docker-compose up
```
## Installation
To install the necessary dependencies, please follow these steps:
1. Clone the WhatTheDuck repository to your local machine.
2. Navigate to the project directory.
3. Run the following command to install the dependencies using Yarn:
```bash
yarn
```
or using npm:
```bash
npm install
```
## Usage
To start the application in development mode, with hot-code reloading and error reporting, follow these steps:
1. Make sure you have completed the installation steps mentioned above.
2. Run the following command:
```bash
quasar dev
```
This will start the application in development mode, and you can access it in your browser at http://localhost:9000 (or
a different port if specified).
To build the application for production, use the following command:
```bash
quasar build
```
The production build of the application will be available in the **dist/** directory.
## Configuration
The application's configuration can be customized by modifying
the [quasar.config.js](https://v2.quasar.dev/quasar-cli-vite/quasar-config-js) file. Please refer to the Configuring
quasar.config.js documentation for detailed instructions on how to customize the configuration according to your needs.
## Contributing
Contributions to WhatTheDuck are welcome! If you would like to contribute to the project, please follow these steps:
1. Fork the repository on GitHub.
2. Clone your forked repository to your local machine.
3. Create a new branch for your feature or bug fix.
4. Make the necessary changes and commit them.
5. Push the changes to your forked repository.
6. Submit a pull request to the main repository.
# Created by Incentius
If you are seeking to leverage innovative technologies to create a custom application or transform your business
processes, we invite you to reach out to Incentius. Discover how we can help you enable digital transformation and
accelerate your business progress. For more information, visit us at [Incentius](https://incentius.com)
## License
WhatTheDuck is open-source software released under the [MIT License](./LICENSE.md). You are free to use, modify, and
distribute the
application in accordance with the terms of the license.
## Star History
[](https://star-history.com/#incentius-foss/WhatTheDuck&Date)
================================================
FILE: docker-compose.yaml
================================================
version: '3.7'
services:
quasar:
build:
context: .
dockerfile: Dockerfile
ports:
- "9000:9000" # Expose Quasar app's port
================================================
FILE: index.html
================================================
<!DOCTYPE html>
<html>
<head>
<title>WhatTheDuck - Run SQL queries on your CSV|Parquet files in browser</title>
<meta charset="utf-8">
<meta name="title" content="WhatTheDuck"/>
<meta name="description"
content="It allows users to upload CSV|Parquet files, store them in tables, and perform SQL queries on the data. The application supports downloading filtered results in CSV format, uploading multiple files, and running join queries. Please note that the data is stored temporarily in memory"/>
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website"/>
<meta property="og:url" content="https://whattheduck.incentius.com/"/>
<meta property="og:title" content="WhatTheDuck"/>
<meta property="og:description"
content="It allows users to upload CSV|Parquet files, store them in tables, and perform SQL queries on the data. The application supports downloading filtered results in CSV format, uploading multiple files, and running join queries. Please note that the data is stored temporarily in memory"/>
<meta property="og:image" content="https://whattheduck.incentius.com/images/WhatTheDuck.png"/>
<!-- Twitter -->
<meta property="twitter:card" content="summary_large_image"/>
<meta property="twitter:url" content="https://whattheduck.incentius.com/"/>
<meta property="twitter:title" content="WhatTheDuck"/>
<meta property="twitter:description"
content="It allows users to upload CSV files, store them in tables, and perform SQL queries on the data. The application supports downloading filtered results in CSV format, uploading multiple files, and running join queries. Please note that the data is stored temporarily in memory"/>
<meta property="twitter:image" content="https://whattheduck.incentius.com/images/WhatTheDuck.png"/>
<meta name="format-detection" content="telephone=no">
<meta name="msapplication-tap-highlight" content="no">
<meta name="viewport"
content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width<% if (ctx.mode.cordova || ctx.mode.capacitor) { %>, viewport-fit=cover<% } %>">
<link rel="icon" type="image/png" sizes="128x128" href="favicon.png">
<link rel="icon" type="image/png" sizes="96x96" href="favicon.png">
<link rel="icon" type="image/png" sizes="32x32" href="favicon.png">
<link rel="icon" type="image/png" sizes="16x16" href="favicon.png">
<link rel="icon" type="image/ico" href="favicon.png">
<script src="https://cdn.jsdelivr.net/npm/ace-builds@1.23.1/src-min-noconflict/ace.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/ace-builds@1.23.1/css/ace.min.css" rel="stylesheet">
<!-- Google Tag Manager -->
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-WTWSQLP');</script>
<!-- End Google Tag Manager -->
</head>
<body>
<!-- Google Tag Manager (noscript) -->
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-WTWSQLP"
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->
<!-- quasar:entry-point -->
</body>
</html>
================================================
FILE: jsconfig.json
================================================
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"src/*": [
"src/*"
],
"app/*": [
"*"
],
"components/*": [
"src/components/*"
],
"layouts/*": [
"src/layouts/*"
],
"pages/*": [
"src/pages/*"
],
"assets/*": [
"src/assets/*"
],
"boot/*": [
"src/boot/*"
],
"stores/*": [
"src/stores/*"
],
"vue$": [
"node_modules/vue/dist/vue.runtime.esm-bundler.js"
]
}
},
"exclude": [
"dist",
".quasar",
"node_modules"
]
}
================================================
FILE: package.json
================================================
{
"name": "duckdb",
"version": "0.0.1",
"description": "A Quasar Project",
"productName": "Quasar App",
"author": "pratikpatel <pratik.patel@aurochssoftware.com>",
"private": true,
"scripts": {
"test": "echo \"No test specified\" && exit 0"
},
"dependencies": {
"@duckdb/duckdb-wasm": "^1.20.0",
"@quasar/extras": "^1.17.0",
"ace-builds": "^1.32.8",
"axios": "^0.21.1",
"echarts": "^5.5.1",
"file-saver": "^2.0.5",
"numeral": "^2.0.6",
"quasar": "^2.18.1",
"vue": "^3.0.0",
"vue-echarts": "^7.0.3",
"vue-router": "^4.0.0",
"vue3-ace-editor": "^2.2.2",
"xlsx": "^0.18.5"
},
"devDependencies": {
"@quasar/app-vite": "^1.11.0",
"autoprefixer": "^9.8.8",
"postcss": "^7.0.39",
"tailwindcss": "npm:@tailwindcss/postcss7-compat@^2.2.17"
},
"engines": {
"node": "^18 || ^16 || ^14.19",
"npm": ">= 6.13.4",
"yarn": ">= 1.21.1"
}
}
================================================
FILE: postcss.config.js
================================================
/* eslint-disable */
// https://github.com/michael-ciniawsky/postcss-load-config
module.exports = {
plugins: [
// https://github.com/postcss/autoprefixer
require('tailwindcss'),
require('autoprefixer')({
overrideBrowserslist: [
'last 4 Chrome versions',
'last 4 Firefox versions',
'last 4 Edge versions',
'last 4 Safari versions',
'last 4 Android versions',
'last 4 ChromeAndroid versions',
'last 4 FirefoxAndroid versions',
'last 4 iOS versions'
]
})
// https://github.com/elchininet/postcss-rtlcss
// If you want to support RTL css, then
// 1. yarn/npm install postcss-rtlcss
// 2. optionally set quasar.config.js > framework > lang to an RTL language
// 3. uncomment the following line:
// require('postcss-rtlcss')
]
}
================================================
FILE: quasar.config.js
================================================
/* eslint-env node */
/*
* This file runs in a Node context (it's NOT transpiled by Babel), so use only
* the ES6 features that are supported by your Node version. https://node.green/
*/
// Configuration for your app
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js
const { configure } = require('quasar/wrappers');
module.exports = configure(function (/* ctx */) {
return {
// https://v2.quasar.dev/quasar-cli/prefetch-feature
// preFetch: true,
// app boot file (/src/boot)
// --> boot files are part of "main.js"
// https://v2.quasar.dev/quasar-cli/boot-files
boot: [
'duckdb',
'bus',
],
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#css
css: [
'app.css'
],
// https://github.com/quasarframework/quasar/tree/dev/extras
extras: [
// 'ionicons-v4',
// 'mdi-v5',
'fontawesome-v6',
// 'eva-icons',
// 'themify',
// 'line-awesome',
// 'roboto-font-latin-ext', // this or either 'roboto-font', NEVER both!
'roboto-font', // optional, you are not bound to it
'material-icons', // optional, you are not bound to it
],
// Full list of options: https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#build
build: {
target: {
browser: [ 'es2019', 'edge88', 'firefox78', 'chrome87', 'safari13.1' ],
node: 'node16'
},
vueRouterMode: 'history', // available values: 'hash', 'history'
// vueRouterBase,
// vueDevtools,
// vueOptionsAPI: false,
// rebuildCache: true, // rebuilds Vite/linter/etc cache on startup
// publicPath: '/',
// analyze: true,
// env: {},
// rawDefine: {}
// ignorePublicFolder: true,
// minify: false,
// polyfillModulePreload: true,
// distDir
// extendViteConf (viteConf) {},
// viteVuePluginOptions: {},
// vitePlugins: [
// [ 'package-name', { ..options.. } ]
// ]
},
// Full list of options: https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#devServer
devServer: {
// https: true
open: true // opens browser window automatically
},
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#framework
framework: {
config: {},
// iconSet: 'material-icons', // Quasar icon set
// lang: 'en-US', // Quasar language pack
// For special cases outside of where the auto-import strategy can have an impact
// (like functional components as one of the examples),
// you can manually specify Quasar components/directives to be available everywhere:
//
// components: [],
// directives: [],
// Quasar plugins
plugins: ['Notify', 'Loading']
},
// animations: 'all', // --- includes all animations
// https://v2.quasar.dev/options/animations
animations: [],
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#property-sourcefiles
// sourceFiles: {
// rootComponent: 'src/App.vue',
// router: 'src/router/index',
// store: 'src/store/index',
// registerServiceWorker: 'src-pwa/register-service-worker',
// serviceWorker: 'src-pwa/custom-service-worker',
// pwaManifestFile: 'src-pwa/manifest.json',
// electronMain: 'src-electron/electron-main',
// electronPreload: 'src-electron/electron-preload'
// },
// https://v2.quasar.dev/quasar-cli/developing-ssr/configuring-ssr
ssr: {
// ssrPwaHtmlFilename: 'offline.html', // do NOT use index.html as name!
// will mess up SSR
// extendSSRWebserverConf (esbuildConf) {},
// extendPackageJson (json) {},
pwa: false,
// manualStoreHydration: true,
// manualPostHydrationTrigger: true,
prodPort: 3000, // The default port that the production server should use
// (gets superseded if process.env.PORT is specified at runtime)
middlewares: [
'render' // keep this as last one
]
},
// https://v2.quasar.dev/quasar-cli/developing-pwa/configuring-pwa
pwa: {
workboxMode: 'generateSW', // or 'injectManifest'
injectPwaMetaTags: true,
swFilename: 'sw.js',
manifestFilename: 'manifest.json',
useCredentialsForManifestTag: false,
// useFilenameHashes: true,
// extendGenerateSWOptions (cfg) {}
// extendInjectManifestOptions (cfg) {},
// extendManifestJson (json) {}
// extendPWACustomSWConf (esbuildConf) {}
},
// Full list of options: https://v2.quasar.dev/quasar-cli/developing-cordova-apps/configuring-cordova
cordova: {
// noIosLegacyBuildFlag: true, // uncomment only if you know what you are doing
},
// Full list of options: https://v2.quasar.dev/quasar-cli/developing-capacitor-apps/configuring-capacitor
capacitor: {
hideSplashscreen: true
},
// Full list of options: https://v2.quasar.dev/quasar-cli/developing-electron-apps/configuring-electron
electron: {
// extendElectronMainConf (esbuildConf)
// extendElectronPreloadConf (esbuildConf)
inspectPort: 5858,
bundler: 'packager', // 'packager' or 'builder'
packager: {
// https://github.com/electron-userland/electron-packager/blob/master/docs/api.md#options
// OS X / Mac App Store
// appBundleId: '',
// appCategoryType: '',
// osxSign: '',
// protocol: 'myapp://path',
// Windows only
// win32metadata: { ... }
},
builder: {
// https://www.electron.build/configuration/configuration
appId: 'duckdb'
}
},
// Full list of options: https://v2.quasar.dev/quasar-cli-vite/developing-browser-extensions/configuring-bex
bex: {
contentScripts: [
'my-content-script'
],
// extendBexScriptsConf (esbuildConf) {}
// extendBexManifestJson (json) {}
}
}
});
================================================
FILE: src/App.vue
================================================
<template>
<router-view />
</template>
<script>
import { defineComponent } from 'vue'
export default defineComponent({
name: 'App'
})
</script>
================================================
FILE: src/assets/Sample-Spreadsheet-10-rows.csv
================================================
1,"Eldon Base for stackable storage shelf, platinum",Muhammed MacIntyre,3,-213.25,38.94,35,Nunavut,Storage & Organization,0.8
2,"1.7 Cubic Foot Compact ""Cube"" Office Refrigerators",Barry French,293,457.81,208.16,68.02,Nunavut,Appliances,0.58
3,"Cardinal Slant-D Ring Binder, Heavy Gauge Vinyl",Barry French,293,46.71,8.69,2.99,Nunavut,Binders and Binder Accessories,0.39
4,R380,Clay Rozendal,483,1198.97,195.99,3.99,Nunavut,Telephones and Communication,0.58
5,Holmes HEPA Air Purifier,Carlos Soltero,515,30.94,21.78,5.94,Nunavut,Appliances,0.5
6,G.E. Longer-Life Indoor Recessed Floodlight Bulbs,Carlos Soltero,515,4.43,6.64,4.95,Nunavut,Office Furnishings,0.37
7,"Angle-D Binders with Locking Rings, Label Holders",Carl Jackson,613,-54.04,7.3,7.72,Nunavut,Binders and Binder Accessories,0.38
8,"SAFCO Mobile Desk Side File, Wire Frame",Carl Jackson,613,127.70,42.76,6.22,Nunavut,Storage & Organization,
9,"SAFCO Commercial Wire Shelving, Black",Monica Federle,643,-695.26,138.14,35,Nunavut,Storage & Organization,
10,Xerox 198,Dorothy Badders,678,-226.36,4.98,8.33,Nunavut,Paper,0.38
================================================
FILE: src/assets/customers-100.csv
================================================
Index,Customer Id,First Name,Last Name,Company,City,Country,Phone 1,Phone 2,Email,Subscription Date,Website
1,DD37Cf93aecA6Dc,Sheryl,Baxter,Rasmussen Group,East Leonard,Chile,229.077.5154,397.884.0519x718,zunigavanessa@smith.info,2020-08-24,http://www.stephenson.com/
2,1Ef7b82A4CAAD10,Preston,Lozano,Vega-Gentry,East Jimmychester,Djibouti,5153435776,686-620-1820x944,vmata@colon.com,2021-04-23,http://www.hobbs.com/
3,6F94879bDAfE5a6,Roy,Berry,Murillo-Perry,Isabelborough,Antigua and Barbuda,+1-539-402-0259,(496)978-3969x58947,beckycarr@hogan.com,2020-03-25,http://www.lawrence.com/
4,5Cef8BFA16c5e3c,Linda,Olsen,"Dominguez, Mcmillan and Donovan",Bensonview,Dominican Republic,001-808-617-6467x12895,+1-813-324-8756,stanleyblackwell@benson.org,2020-06-02,http://www.good-lyons.com/
5,053d585Ab6b3159,Joanna,Bender,"Martin, Lang and Andrade",West Priscilla,Slovakia (Slovak Republic),001-234-203-0635x76146,001-199-446-3860x3486,colinalvarado@miles.net,2021-04-17,https://goodwin-ingram.com/
6,2d08FB17EE273F4,Aimee,Downs,Steele Group,Chavezborough,Bosnia and Herzegovina,(283)437-3886x88321,999-728-1637,louis27@gilbert.com,2020-02-25,http://www.berger.net/
7,EA4d384DfDbBf77,Darren,Peck,"Lester, Woodard and Mitchell",Lake Ana,Pitcairn Islands,(496)452-6181x3291,+1-247-266-0963x4995,tgates@cantrell.com,2021-08-24,https://www.le.com/
8,0e04AFde9f225dE,Brett,Mullen,"Sanford, Davenport and Giles",Kimport,Bulgaria,001-583-352-7197x297,001-333-145-0369,asnow@colon.com,2021-04-12,https://hammond-ramsey.com/
9,C2dE4dEEc489ae0,Sheryl,Meyers,Browning-Simon,Robersonstad,Cyprus,854-138-4911x5772,+1-448-910-2276x729,mariokhan@ryan-pope.org,2020-01-13,https://www.bullock.net/
10,8C2811a503C7c5a,Michelle,Gallagher,Beck-Hendrix,Elaineberg,Timor-Leste,739.218.2516x459,001-054-401-0347x617,mdyer@escobar.net,2021-11-08,https://arias.com/
11,216E205d6eBb815,Carl,Schroeder,"Oconnell, Meza and Everett",Shannonville,Guernsey,637-854-0256x825,114.336.0784x788,kirksalas@webb.com,2021-10-20,https://simmons-hurley.com/
12,CEDec94deE6d69B,Jenna,Dodson,"Hoffman, Reed and Mcclain",East Andrea,Vietnam,(041)737-3846,+1-556-888-3485x42608,mark42@robbins.com,2020-11-29,http://www.douglas.net/
13,e35426EbDEceaFF,Tracey,Mata,Graham-Francis,South Joannamouth,Togo,001-949-844-8787,(855)713-8773,alex56@walls.org,2021-12-02,http://www.beck.com/
14,A08A8aF8BE9FaD4,Kristine,Cox,Carpenter-Cook,Jodyberg,Sri Lanka,786-284-3358x62152,+1-315-627-1796x8074,holdenmiranda@clarke.com,2021-02-08,https://www.brandt.com/
15,6fEaA1b7cab7B6C,Faith,Lutz,Carter-Hancock,Burchbury,Singapore,(781)861-7180x8306,207-185-3665,cassieparrish@blevins-chapman.net,2022-01-26,http://stevenson.org/
16,8cad0b4CBceaeec,Miranda,Beasley,Singleton and Sons,Desireeshire,Oman,540.085.3135x185,+1-600-462-6432x21881,vduncan@parks-hardy.com,2022-04-12,http://acosta.org/
17,a5DC21AE3a21eaA,Caroline,Foley,Winters-Mendoza,West Adriennestad,Western Sahara,936.222.4746x9924,001-469-948-6341x359,holtgwendolyn@watson-davenport.com,2021-03-10,http://www.benson-roth.com/
18,F8Aa9d6DfcBeeF8,Greg,Mata,Valentine LLC,Lake Leslie,Mozambique,(701)087-2415,(195)156-1861x26241,jaredjuarez@carroll.org,2022-03-26,http://pitts-cherry.com/
19,F160f5Db3EfE973,Clifford,Jacobson,Simon LLC,Harmonview,South Georgia and the South Sandwich Islands,001-151-330-3524x0469,(748)477-7174,joseph26@jacobson.com,2020-09-24,https://mcconnell.com/
20,0F60FF3DdCd7aB0,Joanna,Kirk,Mays-Mccormick,Jamesshire,French Polynesia,(266)131-7001x711,(283)312-5579x11543,tuckerangie@salazar.net,2021-09-24,https://www.camacho.net/
21,9F9AdB7B8A6f7F2,Maxwell,Frye,Patterson Inc,East Carly,Malta,423.262.3059,202-880-0688x7491,fgibson@drake-webb.com,2022-01-12,http://www.roberts.com/
22,FBd0Ded4F02a742,Kiara,Houston,"Manning, Hester and Arroyo",South Alvin,Netherlands,001-274-040-3582x10611,+1-528-175-0973x4684,blanchardbob@wallace-shannon.com,2020-09-15,https://www.reid-potts.com/
23,2FB0FAA1d429421,Colleen,Howard,Greer and Sons,Brittanyview,Paraguay,1935085151,(947)115-7711x5488,rsingleton@ryan-cherry.com,2020-08-19,http://paul.biz/
24,010468dAA11382c,Janet,Valenzuela,Watts-Donaldson,Veronicamouth,Lao People's Democratic Republic,354.259.5062x7538,500.433.2022,stefanie71@spence.com,2020-09-08,https://moreno.biz/
25,eC1927Ca84E033e,Shane,Wilcox,Tucker LLC,Bryanville,Albania,(429)005-9030x11004,541-116-4501,mariah88@santos.com,2021-04-06,https://www.ramos.com/
26,09D7D7C8Fe09aea,Marcus,Moody,Giles Ltd,Kaitlyntown,Panama,674-677-8623,909-277-5485x566,donnamullins@norris-barrett.org,2022-05-24,https://www.curry.com/
27,aBdfcF2c50b0bfD,Dakota,Poole,Simmons Group,Michealshire,Belarus,(371)987-8576x4720,071-152-1376,stacey67@fields.org,2022-02-20,https://sanford-wilcox.biz/
28,b92EBfdF8a3f0E6,Frederick,Harper,"Hinton, Chaney and Stokes",South Marissatown,Switzerland,+1-077-121-1558x0687,264.742.7149,jacobkhan@bright.biz,2022-05-26,https://callahan.org/
29,3B5dAAFA41AFa22,Stefanie,Fitzpatrick,Santana-Duran,Acevedoville,Saint Vincent and the Grenadines,(752)776-3286,+1-472-021-4814x85074,wterrell@clark.com,2020-07-30,https://meyers.com/
30,EDA69ca7a6e96a2,Kent,Bradshaw,Sawyer PLC,North Harold,Tanzania,+1-472-143-5037x884,126.922.6153,qjimenez@boyd.com,2020-04-26,http://maynard-ho.com/
31,64DCcDFaB9DFd4e,Jack,Tate,"Acosta, Petersen and Morrow",West Samuel,Zimbabwe,965-108-4406x20714,046.906.1442x6784,gfigueroa@boone-zavala.com,2021-09-15,http://www.hawkins-ramsey.com/
32,679c6c83DD872d6,Tom,Trujillo,Mcgee Group,Cunninghamborough,Denmark,416-338-3758,(775)890-7209,tapiagreg@beard.info,2022-01-13,http://www.daniels-klein.com/
33,7Ce381e4Afa4ba9,Gabriel,Mejia,Adkins-Salinas,Port Annatown,Liechtenstein,4077245425,646.044.0696x66800,coleolson@jennings.net,2021-04-24,https://patel-hanson.info/
34,A09AEc6E3bF70eE,Kaitlyn,Santana,Herrera Group,New Kaitlyn,United States of America,6303643286,447-710-6202x07313,georgeross@miles.org,2021-09-21,http://pham.com/
35,aA9BAFfBc3710fe,Faith,Moon,"Waters, Chase and Aguilar",West Marthaburgh,Bahamas,+1-586-217-0359x6317,+1-818-199-1403,willistonya@randolph-baker.com,2021-11-03,https://spencer-charles.info/
36,E11dfb2DB8C9f72,Tammie,Haley,"Palmer, Barnes and Houston",East Teresa,Belize,001-276-734-4113x6087,(430)300-8770,harrisisaiah@jenkins.com,2022-01-04,http://evans-simon.com/
37,889eCf90f68c5Da,Nicholas,Sosa,Jordan Ltd,South Hunter,Uruguay,(661)425-6042,975-998-1519,fwolfe@dorsey.com,2021-08-10,https://www.fleming-richards.com/
38,7a1Ee69F4fF4B4D,Jordan,Gay,Glover and Sons,South Walter,Solomon Islands,7208417020,8035336772,tiffanydavies@harris-mcfarland.org,2021-02-24,http://www.lee.org/
39,dca4f1D0A0fc5c9,Bruce,Esparza,Huerta-Mclean,Poolefurt,Montenegro,559-529-4424,001-625-000-7132x0367,preese@frye-vega.com,2021-10-22,http://www.farley.org/
40,17aD8e2dB3df03D,Sherry,Garza,Anderson Ltd,West John,Poland,001-067-713-6440x158,(978)289-8785x5766,ann48@miller.com,2021-11-01,http://spence.com/
41,2f79Cd309624Abb,Natalie,Gentry,Monroe PLC,West Darius,Dominican Republic,830.996.8238,499.122.5415,tcummings@fitzpatrick-ashley.com,2020-10-10,http://www.dorsey.biz/
42,6e5ad5a5e2bB5Ca,Bryan,Dunn,Kaufman and Sons,North Jimstad,Burkina Faso,001-710-802-5565,078.699.8982x13881,woodwardandres@phelps.com,2021-09-08,http://www.butler.com/
43,7E441b6B228DBcA,Wayne,Simpson,Perkins-Trevino,East Rebekahborough,Bolivia,(344)156-8632x1869,463-445-3702x38463,barbarapittman@holder.com,2020-12-13,https://gillespie-holder.com/
44,D3fC11A9C235Dc6,Luis,Greer,Cross PLC,North Drew,Bulgaria,001-336-025-6849x701,684.698.2911x6092,bstuart@williamson-mcclure.com,2022-05-15,https://fletcher-nielsen.com/
45,30Dfa48fe5Ede78,Rhonda,Frost,"Herrera, Shepherd and Underwood",Lake Lindaburgh,Monaco,(127)081-9339,+1-431-028-3337x3492,zkrueger@wolf-chavez.net,2021-12-06,http://www.khan.com/
46,fD780ED8dbEae7B,Joanne,Montes,"Price, Sexton and Mcdaniel",Gwendolynview,Palau,(897)726-7952,(467)886-9467x5721,juan80@henson.net,2020-07-01,http://ochoa.com/
47,300A40d3ce24bBA,Geoffrey,Guzman,Short-Wiggins,Zimmermanland,Uzbekistan,975.235.8921x269,(983)188-6873,bauercrystal@gay.com,2020-04-23,https://decker-kline.com/
48,283DFCD0Dba40aF,Gloria,Mccall,"Brennan, Acosta and Ramos",North Kerriton,Ghana,445-603-6729,001-395-959-4736x4524,bartlettjenna@zuniga-moss.biz,2022-03-11,http://burgess-frank.com/
49,F4Fc91fEAEad286,Brady,Cohen,Osborne-Erickson,North Eileenville,United Arab Emirates,741.849.0139x524,+1-028-691-7497x0894,mccalltyrone@durham-rose.biz,2022-03-10,http://hammond-barron.com/
50,80F33Fd2AcebF05,Latoya,Mccann,"Hobbs, Garrett and Sanford",Port Sergiofort,Belarus,(530)287-4548x29481,162-234-0249x32790,bobhammond@barry.biz,2021-12-02,https://www.burton.com/
51,Aa20BDe68eAb0e9,Gerald,Hawkins,"Phelps, Forbes and Koch",New Alberttown,Canada,+1-323-239-1456x96168,(092)508-0269,uwarner@steele-arias.com,2021-03-19,https://valenzuela.com/
52,e898eEB1B9FE22b,Samuel,Crawford,"May, Goodwin and Martin",South Jasmine,Algeria,802-242-7457,626.116.9535x8578,xpittman@ritter-carney.net,2021-03-27,https://guerrero.org/
53,faCEF517ae7D8eB,Patricia,Goodwin,"Christian, Winters and Ellis",Cowanfort,Swaziland,322.549.7139x70040,(111)741-4173,vaughanchristy@lara.biz,2021-03-08,http://clark.info/
54,c09952De6Cda8aA,Stacie,Richard,Byrd Inc,New Deborah,Madagascar,001-622-948-3641x24810,001-731-168-2893x8891,clinton85@colon-arias.org,2020-10-15,https://kim.com/
55,f3BEf3Be028166f,Robin,West,"Nixon, Blackwell and Sosa",Wallstown,Ecuador,698.303.4267,001-683-837-7651x525,greenemiranda@zimmerman.com,2022-01-13,https://www.mora.com/
56,C6F2Fc6a7948a4e,Ralph,Haas,Montes PLC,Lake Ellenchester,Palestinian Territory,2239271999,001-962-434-0867x649,goodmancesar@figueroa.biz,2020-05-25,http://may.com/
57,c8FE57cBBdCDcb2,Phyllis,Maldonado,Costa PLC,Lake Whitney,Saint Barthelemy,4500370767,001-508-064-6725x017,yhanson@warner-diaz.org,2021-01-25,http://www.bernard.com/
58,B5acdFC982124F2,Danny,Parrish,Novak LLC,East Jaredbury,United Arab Emirates,(669)384-8597x8794,506.731.5952x571,howelldarren@house-cohen.com,2021-03-17,http://www.parsons-hudson.com/
59,8c7DdF10798bCC3,Kathy,Hill,"Moore, Mccoy and Glass",Selenabury,South Georgia and the South Sandwich Islands,001-171-716-2175x310,888.625.0654,ncamacho@boone-simmons.org,2020-11-15,http://hayden.com/
60,C681dDd0cc422f7,Kelli,Hardy,Petty Ltd,Huangfort,Sao Tome and Principe,020.324.2191x2022,424-157-8216,kristopher62@oliver.com,2020-12-20,http://www.kidd.com/
61,a940cE42e035F28,Lynn,Pham,"Brennan, Camacho and Tapia",East Pennyshire,Portugal,846.468.6834x611,001-248-691-0006,mpham@rios-guzman.com,2020-08-21,https://www.murphy.com/
62,9Cf5E6AFE0aeBfd,Shelley,Harris,"Prince, Malone and Pugh",Port Jasminborough,Togo,423.098.0315x8373,+1-386-458-8944x15194,zachary96@mitchell-bryant.org,2020-12-10,https://www.ryan.com/
63,aEcbe5365BbC67D,Eddie,Jimenez,Caldwell Group,West Kristine,Ethiopia,+1-235-657-1073x6306,(026)401-7353x2417,kristiwhitney@bernard.com,2022-03-24,http://cherry.com/
64,FCBdfCEAe20A8Dc,Chloe,Hutchinson,Simon LLC,South Julia,Netherlands,981-544-9452,+1-288-552-4666x060,leah85@sutton-terrell.com,2022-05-15,https://mitchell.info/
65,636cBF0835E10ff,Eileen,Lynch,"Knight, Abbott and Hubbard",Helenborough,Liberia,+1-158-951-4131x53578,001-673-779-6713x680,levigiles@vincent.com,2021-01-02,http://mckay.com/
66,fF1b6c9E8Fbf1ff,Fernando,Lambert,Church-Banks,Lake Nancy,Lithuania,497.829.9038,3863743398,fisherlinda@schaefer.net,2021-04-23,https://www.vang.com/
67,2A13F74EAa7DA6c,Makayla,Cannon,Henderson Inc,Georgeport,New Caledonia,001-215-801-6392x46009,027-609-6460,scottcurtis@hurley.biz,2020-01-20,http://www.velazquez.net/
68,a014Ec1b9FccC1E,Tom,Alvarado,Donaldson-Dougherty,South Sophiaberg,Kiribati,(585)606-2980x2258,730-797-3594x5614,nicholsonnina@montgomery.info,2020-08-18,http://odom-massey.com/
69,421a109cABDf5fa,Virginia,Dudley,Warren Ltd,Hartbury,French Southern Territories,027.846.3705x14184,+1-439-171-1846x4636,zvalencia@phelps.com,2021-01-31,http://hunter-esparza.com/
70,CC68FD1D3Bbbf22,Riley,Good,Wade PLC,Erikaville,Canada,6977745822,855-436-7641,alex06@galloway.com,2020-02-03,http://conway.org/
71,CBCd2Ac8E3eBDF9,Alexandria,Buck,Keller-Coffey,Nicolasfort,Iran,078-900-4760x76668,414-112-8700x68751,lee48@manning.com,2021-02-20,https://ramsey.org/
72,Ef859092FbEcC07,Richard,Roth,Conway-Mcbride,New Jasmineshire,Morocco,581-440-6539,9857827463,aharper@maddox-townsend.org,2020-02-23,https://www.brooks.com/
73,F560f2d3cDFb618,Candice,Keller,Huynh and Sons,East Summerstad,Zimbabwe,001-927-965-8550x92406,001-243-038-4271x53076,buckleycory@odonnell.net,2020-08-22,https://www.lucero.com/
74,A3F76Be153Df4a3,Anita,Benson,Parrish Ltd,Skinnerport,Russian Federation,874.617.5668x69878,(399)820-6418x0071,angie04@oconnell.com,2020-02-09,http://oconnor.com/
75,D01Af0AF7cBbFeA,Regina,Stein,Guzman-Brown,Raystad,Solomon Islands,001-469-848-0724x4407,001-085-360-4426x00357,zrosario@rojas-hardin.net,2022-01-15,http://www.johnston.info/
76,d40e89dCade7b2F,Debra,Riddle,"Chang, Aguirre and Leblanc",Colinhaven,United States Virgin Islands,+1-768-182-6014x14336,(303)961-4491,shieldskerry@robles.com,2020-07-11,http://kaiser.info/
77,BF6a1f9bd1bf8DE,Brittany,Zuniga,Mason-Hester,West Reginald,Kyrgyz Republic,(050)136-9025,001-480-851-2496x0157,mchandler@cochran-huerta.org,2021-07-24,http://www.boyle.com/
78,FfaeFFbbbf280db,Cassidy,Mcmahon,"Mcguire, Huynh and Hopkins",Lake Sherryborough,Myanmar,5040771311,684-682-0021x1326,katrinalane@fitzgerald.com,2020-10-21,https://hurst.com/
79,CbAE1d1e9a8dCb1,Laurie,Pennington,"Sanchez, Marsh and Hale",Port Katherineville,Dominica,007.155.3406x553,+1-809-862-5566x277,cookejill@powell.com,2020-06-08,http://www.hebert.com/
80,A7F85c1DE4dB87f,Alejandro,Blair,"Combs, Waller and Durham",Thomasland,Iceland,(690)068-4641x51468,555.509.8691x2329,elizabethbarr@ewing.com,2020-09-19,https://mercado-blevins.com/
81,D6CEAfb3BDbaa1A,Leslie,Jennings,Blankenship-Arias,Coreybury,Micronesia,629.198.6346,075.256.0829,corey75@wiggins.com,2021-11-13,https://www.juarez.com/
82,Ebdb6F6F7c90b69,Kathleen,Mckay,"Coffey, Lamb and Johnson",Lake Janiceton,Saint Vincent and the Grenadines,(733)910-9968,(691)247-4128x0665,chloelester@higgins-wilkinson.com,2021-09-12,http://www.owens-mooney.com/
83,E8E7e8Cfe516ef0,Hunter,Moreno,Fitzpatrick-Lawrence,East Clinton,Isle of Man,(733)833-6754,001-761-013-7121,isaac26@benton-finley.com,2020-12-28,http://walls.info/
84,78C06E9b6B3DF20,Chad,Davidson,Garcia-Jimenez,South Joshuashire,Oman,8275702958,(804)842-4715,justinwalters@jimenez.com,2021-11-15,http://www.garner-oliver.com/
85,03A1E62ADdeb31c,Corey,Holt,"Mcdonald, Bird and Ramirez",New Glenda,Fiji,001-439-242-4986x7918,3162708934,maurice46@morgan.com,2020-02-18,http://www.watson.com/
86,C6763c99d0bd16D,Emma,Cunningham,Stephens Inc,North Jillianview,New Zealand,128-059-0206x60217,(312)164-4545x2284,walter83@juarez.org,2022-05-13,http://www.reid.info/
87,ebe77E5Bf9476CE,Duane,Woods,Montoya-Miller,Lyonsberg,Maldives,(636)544-7783x7288,(203)287-1003x5932,kmercer@wagner.com,2020-07-21,http://murray.org/
88,E4Bbcd8AD81fC5f,Alison,Vargas,"Vaughn, Watts and Leach",East Cristinabury,Benin,365-273-8144,053-308-7653x6287,vcantu@norton.com,2020-11-10,http://mason.info/
89,efeb73245CDf1fF,Vernon,Kane,Carter-Strickland,Thomasfurt,Yemen,114-854-1159x555,499-608-4612,hilljesse@barrett.info,2021-04-15,http://www.duffy-hensley.net/
90,37Ec4B395641c1E,Lori,Flowers,Decker-Mcknight,North Joeburgh,Namibia,679.415.1210,945-842-3659x4581,tyrone77@valenzuela.info,2021-01-09,http://www.deleon-crosby.com/
91,5ef6d3eefdD43bE,Nina,Chavez,Byrd-Campbell,Cassidychester,Bhutan,053-344-3205,+1-330-920-5422x571,elliserica@frank.com,2020-03-26,https://www.pugh.com/
92,98b3aeDcC3B9FF3,Shane,Foley,Rocha-Hart,South Dannymouth,Hungary,+1-822-569-0302,001-626-114-5844x55073,nsteele@sparks.com,2021-07-06,https://www.holt-sparks.com/
93,aAb6AFc7AfD0fF3,Collin,Ayers,Lamb-Peterson,South Lonnie,Anguilla,404-645-5351x012,001-257-582-8850x8516,dudleyemily@gonzales.biz,2021-06-29,http://www.ruiz.com/
94,54B5B5Fe9F1B6C5,Sherry,Young,"Lee, Lucero and Johnson",Frankchester,Solomon Islands,158-687-1764,(438)375-6207x003,alan79@gates-mclaughlin.com,2021-04-04,https://travis.net/
95,BE91A0bdcA49Bbc,Darrell,Douglas,"Newton, Petersen and Mathis",Daisyborough,Mali,001-084-845-9524x1777,001-769-564-6303,grayjean@lowery-good.com,2022-02-17,https://banks.biz/
96,cb8E23e48d22Eae,Karl,Greer,Carey LLC,East Richard,Guyana,(188)169-1674x58692,001-841-293-3519x614,hhart@jensen.com,2022-01-30,http://hayes-perez.com/
97,CeD220bdAaCfaDf,Lynn,Atkinson,"Ware, Burns and Oneal",New Bradview,Sri Lanka,+1-846-706-2218,605.413.3198,vkemp@ferrell.com,2021-07-10,https://novak-allison.com/
98,28CDbC0dFe4b1Db,Fred,Guerra,Schmitt-Jones,Ortegaland,Solomon Islands,+1-753-067-8419x7170,+1-632-666-7507x92121,swagner@kane.org,2021-09-18,https://www.ross.com/
99,c23d1D9EE8DEB0A,Yvonne,Farmer,Fitzgerald-Harrell,Lake Elijahview,Aruba,(530)311-9786,001-869-452-0943x12424,mccarthystephen@horn-green.biz,2021-08-11,http://watkins.info/
100,2354a0E336A91A1,Clarence,Haynes,"Le, Nash and Cross",Judymouth,Honduras,(753)813-6941,783.639.1472,colleen91@faulkner.biz,2020-03-11,http://www.hatfield-saunders.net/
================================================
FILE: src/boot/.gitkeep
================================================
================================================
FILE: src/boot/bus.js
================================================
import { EventBus } from 'quasar'
import { boot } from 'quasar/wrappers'
const bus = new EventBus()
export default boot(({ app }) => {
// for Options API
app.config.globalProperties.$bus = bus
// for Composition API
app.provide('bus', bus)
})
export {bus};
================================================
FILE: src/boot/duckdb.js
================================================
import {boot} from 'quasar/wrappers'
import * as duckdb from '@duckdb/duckdb-wasm';
import duckdb_wasm from '@duckdb/duckdb-wasm/dist/duckdb-mvp.wasm?url';
import mvp_worker from '@duckdb/duckdb-wasm/dist/duckdb-browser-mvp.worker.js?url';
import duckdb_wasm_next from '@duckdb/duckdb-wasm/dist/duckdb-eh.wasm?url';
import eh_worker from '@duckdb/duckdb-wasm/dist/duckdb-browser-eh.worker.js?url';
const MANUAL_BUNDLES = {
mvp: {
mainModule: duckdb_wasm,
mainWorker: mvp_worker,
},
eh: {
mainModule: duckdb_wasm_next,
mainWorker: eh_worker
},
};
export default boot(async ({app}) => {
// Select a bundle based on browser checks
const bundle = await duckdb.selectBundle(MANUAL_BUNDLES);
const worker = new Worker(bundle.mainWorker);
const logger = new duckdb.ConsoleLogger();
const db = new duckdb.AsyncDuckDB(logger, worker);
await db.instantiate(bundle.mainModule, bundle.pthreadWorker);
const conn = await db.connect(); // Connect to db
app.config.globalProperties.$conn = conn
app.config.globalProperties.$worker = worker
app.config.globalProperties.$db = db
})
// export {conn, db, worker}
================================================
FILE: src/components/EssentialLink.vue
================================================
<template>
<q-item
clickable
tag="a"
target="_blank"
:href="link"
>
<q-item-section
v-if="icon"
avatar
>
<q-icon :name="icon" />
</q-item-section>
<q-item-section>
<q-item-label>{{ title }}</q-item-label>
<q-item-label caption>{{ caption }}</q-item-label>
</q-item-section>
</q-item>
</template>
<script>
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EssentialLink',
props: {
title: {
type: String,
required: true
},
caption: {
type: String,
default: ''
},
link: {
type: String,
default: '#'
},
icon: {
type: String,
default: ''
}
}
})
</script>
================================================
FILE: src/css/app.css
================================================
/* app global css */
@tailwind base;
@tailwind components;
@tailwind utilities;
.dd-scroll::-webkit-scrollbar {
width: 4px;
height: 8px;
}
.dd-scroll::-webkit-scrollbar-track {
background-color: transparent;
}
.dd-scroll::-webkit-scrollbar-thumb {
background-color: #727272;
border-radius: 5px;
}
.scroll::-webkit-scrollbar {
width: 4px;
height: 8px;
}
.scroll::-webkit-scrollbar-track {
background-color: transparent;
}
.scroll::-webkit-scrollbar-thumb {
background-color: #727272;
border-radius: 5px;
}
.q-table--dark .q-table__bottom, .q-table--dark thead, .q-table--dark tr, .q-table--dark th, .q-table--dark td {
border-color: #202020;
}
.q-dark{
background-color: #101010;
}
================================================
FILE: src/layouts/MainLayout.vue
================================================
<template>
<q-layout view="HHh Lpr lFf">
<q-header class="tw-bg-primarybg tw-h-14" style="">
<div class="tw-flex tw-justify-between tw-h-full tw-items-center tw-px-5">
<div class="tw-flex tw-items-center">
<img src="/logo.svg" class="tw-h-6 tw-my-auto"/> <span class="tw-mt-3 tw-ml-1.5">by <a href="https://incentius.com/" target="_blank" class="tw-text-hara">incentius.com</a></span>
</div>
<div class="tw-flex tw-items-center tw-gap-9">
<div @click="openPopUp()" class="tw-flex tw-items-center tw-justify-between tw-gap-12">
<div class="tw-text-hara hover:tw-text-morehara tw-flex tw-items-center">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
stroke="currentColor" class="tw-w-6 tw-h-6 cursor-pointer">
<path stroke-linecap="round" stroke-linejoin="round"
d="M12 18v-5.25m0 0a6.01 6.01 0 001.5-.189m-1.5.189a6.01 6.01 0 01-1.5-.189m3.75 7.478a12.06 12.06 0 01-4.5 0m3.75 2.383a14.406 14.406 0 01-3 0M14.25 18v-.192c0-.983.658-1.823 1.508-2.316a7.5 7.5 0 10-7.517 0c.85.493 1.509 1.333 1.509 2.316V18"/>
</svg>
<span v-if="$q.platform.is.desktop" class="cursor-pointer tw-font-medium tw-text-sm tw-ml-3">
What's This?
</span>
</div>
</div>
<a href="https://github.com/incentius-foss/WhatTheDuck" target="_blank"><q-icon class="tw-text-hara hover:tw-text-morehara" size="sm" name="fa-brands fa-github"/></a>
</div>
</div>
</q-header>
<q-page-container>
<router-view/>
</q-page-container>
</q-layout>
</template>
<script>
import {defineComponent} from 'vue'
export default defineComponent({
name: 'MainLayout',
methods: {
openPopUp() {
this.$bus.emit('openPopUp');
},
}
})
</script>
================================================
FILE: src/pages/ErrorNotFound.vue
================================================
<template>
<div class="fullscreen bg-blue text-white text-center q-pa-md flex flex-center">
<div>
<div style="font-size: 30vh">
404
</div>
<div class="text-h2" style="opacity:.4">
Oops. Nothing here...
</div>
<q-btn
class="q-mt-xl"
color="white"
text-color="blue"
unelevated
to="/"
label="Go Home"
no-caps
/>
</div>
</div>
</template>
<script>
import { defineComponent } from 'vue'
export default defineComponent({
name: 'ErrorNotFound'
})
</script>
================================================
FILE: src/pages/IndexPage.vue
================================================
<template>
<q-page class="q-pa-sm">
<q-card class="no-border no-shadow">
<q-card-section class="q-pa-sm row">
<div class="text-h6 col-12">
CSV File
<q-uploader
label="Individual upload"
style="max-width: 300px"
@added="files"
class="float-right"
/>
</div>
</q-card-section>
<q-card-section class="row">
<div class="col-12">
<q-btn label="Filter records" class="float-right q-mt-sm q-ml-sm" @click="search" no-caps></q-btn>
<q-input v-model="filter" outlined label="Search Duration" class="float-right"></q-input>
</div>
</q-card-section>
<q-card-section>
<q-table
:rows="rows"
:columns="columns"
row-key="name"
></q-table>
</q-card-section>
</q-card>
</q-page>
</template>
<script>
import {defineComponent, ref} from 'vue'
export default defineComponent({
name: 'IndexPage',
data() {
return {
file: ref([]),
columns: [],
rows: [],
filter: ''
}
},
async created() {
let self = this;
const stmt3 = await self.$conn.prepare(`SELECT *
FROM new_tbl;`);
const res3 = await stmt3.query();
self.rows = JSON.parse(JSON.stringify(res3.toArray()))
if (self.rows.length > 0) {
Object.keys(self.rows[0]).filter(function (item) {
self.columns.push({name: item, align: 'left', label: item, field: item, sortable: true})
return item
})
}
},
methods: {
async files(files) {
// console.log(files[0])
this.file = files[0]
this.columns = []
let fileReader = new FileReader();
let text = "";
fileReader.readAsText(this.file)
let self = this;
fileReader.onload = async function () {
text = fileReader.result
// console.log(text)
await self.$db.registerFileText("sample_table.csv", text);
await self.$conn.query(`CREATE TABLE new_tbl AS
SELECT *
FROM read_csv_auto('sample_table.csv');`);
const stmt3 = await self.$conn.prepare(`SELECT *
FROM new_tbl;`);
const res3 = await stmt3.query();
self.rows = JSON.parse(JSON.stringify(res3.toArray()))
if (self.rows.length > 0) {
Object.keys(self.rows[0]).filter(function (item) {
self.columns.push({name: item, align: 'left', label: item, field: item, sortable: true})
return item
})
}
// console.log("Statement result (Table):", JSON.parse(JSON.stringify(res3.toArray())));
}
},
async search() {
const q = await this.$conn.prepare("SELECT * FROM new_tbl where duration=" + this.filter);
const r = await q.query();
this.rows = JSON.parse(JSON.stringify(r.toArray()))
}
},
// async beforeUnmount() {
// await this.$conn.close();
// await this.$db.terminate();
// await this.$worker.terminate();
// }
})
</script>
================================================
FILE: src/pages/NewIndexPage.vue
================================================
<template>
<q-drawer v-model="openDrawer" class="q-pt-sm tw-bg-secondarybg dd-scroll" show-if-above>
<div class="tw-m-5">
<div @dragover.prevent @drop="handleDrop"
class="tw-flex tw-flex-col tw-gap-2 tw-items-center tw-p-5 tw-bg-tertiarybg tw-rounded-3xl">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"
class="tw-w-9 tw-h-9 tw-text-primarytext">
<path stroke-linecap="round" stroke-linejoin="round"
d="M12 16.5V9.75m0 0l3 3m-3-3l-3 3M6.75 19.5a4.5 4.5 0 01-1.41-8.775 5.25 5.25 0 0110.233-2.33 3 3 0 013.758 3.848A3.752 3.752 0 0118 19.5H6.75z"/>
</svg>
<span class="tw-text-primarytext tw-font-medium tw-text-lg">Drag & Drop Files Here</span>
<span class="tw-text-primarytext tw-text-xs text-center tw-text-[8px]">Your data will be processed locally in your browser and won't be sent anywhere.</span>
<span class="tw-text-primarytext">or</span>
<button @click="$refs.uploaderref.pickFiles();" type="button"
class="tw-rounded-lg tw-border-2 tw-border-hara tw-bg-transparent tw-px-9 tw-py-2 tw-text-sm tw-font-semibold tw-text-hara hover:tw-bg-secondarybg focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600">
Browse
</button>
</div>
<q-item-label class="tw-text-white tw-mt-9 tw-mb-7 tw-text-base tw-font-normal">My Files</q-item-label>
<template v-for="(table, index) in tables" :key="index">
<div class="tw-mb-0.5 tw-w-full tw-flex tw-items-center hover:tw-bg-tertiarybg tw-rounded-xl tw-py-1 tw-px-3"
:class="selection===table.name ? 'tw-bg-tertiarybg' : 'tw-bg-transparent'">
<div @click="table['toggle'] = !table['toggle']" class="tw-mr-3 tw-h-full"
:class="table['toggle'] ? 'rotate' : ''">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
stroke="currentColor" class="tw-w-5 tw-h-5"
:class="selection===table.name ? 'tw-text-hara' : 'tw-text-white'">
<path stroke-linecap="round" stroke-linejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5"/>
</svg>
</div>
<div class="tw-w-full tw-flex tw-items-center tw-justify-between">
<div class="tw-w-10/12 tw-flex tw-items-center tw-cursor-pointer" @click="selectTable(table.name)">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
stroke="currentColor" class="tw-w-5 tw-h-5 tw-mr-3"
:class="selection===table.name ? 'tw-text-hara' : 'tw-text-white'">
<path stroke-linecap="round" stroke-linejoin="round"
d="M3.375 19.5h17.25m-17.25 0a1.125 1.125 0 01-1.125-1.125M3.375 19.5h7.5c.621 0 1.125-.504 1.125-1.125m-9.75 0V5.625m0 12.75v-1.5c0-.621.504-1.125 1.125-1.125m18.375 2.625V5.625m0 12.75c0 .621-.504 1.125-1.125 1.125m1.125-1.125v-1.5c0-.621-.504-1.125-1.125-1.125m0 3.75h-7.5A1.125 1.125 0 0112 18.375m9.75-12.75c0-.621-.504-1.125-1.125-1.125H3.375c-.621 0-1.125.504-1.125 1.125m19.5 0v1.5c0 .621-.504 1.125-1.125 1.125M2.25 5.625v1.5c0 .621.504 1.125 1.125 1.125m0 0h17.25m-17.25 0h7.5c.621 0 1.125.504 1.125 1.125M3.375 8.25c-.621 0-1.125.504-1.125 1.125v1.5c0 .621.504 1.125 1.125 1.125m17.25-3.75h-7.5c-.621 0-1.125.504-1.125 1.125m8.625-1.125c.621 0 1.125.504 1.125 1.125v1.5c0 .621-.504 1.125-1.125 1.125m-17.25 0h7.5m-7.5 0c-.621 0-1.125.504-1.125 1.125v1.5c0 .621.504 1.125 1.125 1.125M12 10.875v-1.5m0 1.5c0 .621-.504 1.125-1.125 1.125M12 10.875c0 .621.504 1.125 1.125 1.125m-2.25 0c.621 0 1.125.504 1.125 1.125M13.125 12h7.5m-7.5 0c-.621 0-1.125.504-1.125 1.125M20.625 12c.621 0 1.125.504 1.125 1.125v1.5c0 .621-.504 1.125-1.125 1.125m-17.25 0h7.5M12 14.625v-1.5m0 1.5c0 .621-.504 1.125-1.125 1.125M12 14.625c0 .621.504 1.125 1.125 1.125m-2.25 0c.621 0 1.125.504 1.125 1.125m0 1.5v-1.5m0 0c0-.621.504-1.125 1.125-1.125m0 0h7.5"/>
</svg>
<span style="width:138px;"
class="tw-flex-1 tw-text-white tw-text-base tw-overflow-hidden tw-text-ellipsis">{{
table.name
}}</span>
</div>
<div class="tw-w-2/12" @click="deleteTable(table.name, index)">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
stroke="currentColor" class="tw-w-5 tw-h-5 tw-ml-3 tw-text-red-500 hover:tw-text-red-400">
<path stroke-linecap="round" stroke-linejoin="round"
d="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0"/>
</svg>
</div>
</div>
</div>
<q-slide-transition>
<div v-show="table['toggle']" class="tw-w-full tw-py-1">
<div v-for="(col, col_index) in table['children']" :key="col_index">
<div class="tw-flex tw-items-center tw-pl-12 tw-my-2">
<q-icon :name="col.icon" size="xs" color="white"/>
<span class="tw-ml-2 tw-text-white tw-text-sm">
{{ col.label }}
</span>
</div>
</div>
</div>
</q-slide-transition>
</template>
<div v-if="tables.length===0" class="tw-flex tw-items-center">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"
class="tw-w-6 tw-h-6 tw-text-primarytext tw-mr-2">
<path stroke-linecap="round" stroke-linejoin="round"
d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z"/>
</svg>
<span class="tw-text-primarytext">No files available</span>
</div>
</div>
</q-drawer>
<q-uploader
label="Individual upload"
style="max-width: 300px"
@added="files"
class="float-right tw-hidden"
ref="uploaderref"
>
<template v-slot:header="scope">
<div class="row no-wrap items-center q-pa-sm q-gutter-xs">
<q-btn v-if="scope.queuedFiles.length > 0" icon="clear_all" @click="scope.removeQueuedFiles" round dense flat>
<q-tooltip>Clear All</q-tooltip>
</q-btn>
<q-btn v-if="scope.uploadedFiles.length > 0" icon="done_all" @click="scope.removeUploadedFiles" round dense
flat>
<q-tooltip>Remove Uploaded Files</q-tooltip>
</q-btn>
<q-spinner v-if="scope.isUploading" class="q-uploader__spinner"/>
<div class="col">
<div class="q-uploader__title">Upload your files</div>
<div class="q-uploader__subtitle">{{ scope.uploadSizeLabel }} / {{ scope.uploadProgressLabel }}</div>
</div>
<q-btn v-if="scope.canAddFiles" type="a" icon="add_box" @click="scope.pickFiles" round dense flat>
<q-uploader-add-trigger/>
<q-tooltip>Pick Files</q-tooltip>
</q-btn>
<q-btn v-if="scope.canUpload" icon="cloud_upload" @click="upload" round dense flat>
<q-tooltip>Upload File</q-tooltip>
</q-btn>
<q-btn v-if="scope.isUploading" icon="clear" @click="scope.abort" round dense flat>
<q-tooltip>Abort Upload</q-tooltip>
</q-btn>
</div>
</template>
</q-uploader>
<q-page class="q-pa-sm tw-bg-dark tw-flex tw-flex-col">
<q-card-section class="tw-flex-1 tw-flex tw-flex-col">
<div v-if="$q.platform.is.mobile">
<button type="button"
class="tw-py-2 tw-px-6 tw-border-hara tw-bg-transparent q-mb-md tw-text-hara tw-rounded-lg tw-border-2"
@click="openDrawer=true" no-caps>Upload
</button>
</div>
<div class="tw-h-10 tw-bg-editorborder tw-rounded-t-xl tw-flex tw-items-center tw-justify-between">
<span class="tw-text-base tw-text-white tw-ml-3 tw-font-normal">SQL Editor</span>
<span v-if="selection!==''" class="tw-text-base tw-text-hara tw-mr-3 tw-font-normal tw-overflow-hidden">{{
selection
}}</span>
</div>
<VAceEditor
class="dd-scroll"
v-model:value="query"
ref="editor"
lang="sql"
:options="{
useWorker: true,
enableBasicAutocompletion: true,
enableSnippets: true,
enableLiveAutocompletion: true,
customScrollbar: true,
fontSize:'1rem'
}"
:key="counter"
@init="initializeEditor"
theme="twilight"
style="flex: 1 1 0%; min-height:9rem;"
/>
<div class="tw-flex tw-border-t tw-border-editorborder">
<div class="tw-h-10 tw-bg-twilightbg tw-rounded-bl-xl tw-flex tw-items-center"
:class="$q.platform.is.desktop ? 'tw-w-10/12' : 'tw-w-9/12'">
</div>
<div @click="search"
class="tw-h-10 tw-bg-twilightbg hover:tw-bg-tertiarybg tw-rounded-br-xl tw-flex tw-items-center tw-justify-center tw-border-l tw-border-editorborder"
:class="$q.platform.is.desktop ? 'tw-w-2/12': 'tw-w-3/12'">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
stroke="currentColor" class="tw-w-6 tw-h-6 tw-text-hara">
<path stroke-linecap="round" stroke-linejoin="round"
d="M5.25 5.653c0-.856.917-1.398 1.667-.986l11.54 6.348a1.125 1.125 0 010 1.971l-11.54 6.347a1.125 1.125 0 01-1.667-.985V5.653z"/>
</svg>
<span class="tw-text-sm tw-text-white tw-font-normal" :class="$q.platform.is.desktop ? 'tw-ml-3' : 'tw-ml-2'">Run</span>
</div>
</div>
<!-- <div><q-btn flat color="primary" label="Filter records" class="q-ml-lg q-mt-sm" @click="search" no-caps></q-btn></div> -->
</q-card-section>
<q-card-section class="tw-flex-1">
<div class="tw-flex tw-justify-end tw-mb-3 tw-gap-3" v-if="view==='table'">
<button @click="uploadSampleFile" type="button"
class="tw-rounded-lg tw-bg-hara hover:tw-bg-morehara tw-px-3.5 tw-py-1.5 tw-text-sm tw-font-semibold tw-text-black">
Upload Sample File
</button>
<button @click="importParquetFromUrl" type="button"
class="tw-rounded-lg tw-bg-hara hover:tw-bg-morehara tw-px-3.5 tw-py-1.5 tw-text-sm tw-font-semibold tw-text-black">
Import Parquet URL
</button>
<button @click="download_csv" type="button"
class="tw-rounded-lg tw-bg-hara hover:tw-bg-morehara tw-px-3.5 tw-py-1.5 tw-text-sm tw-font-semibold tw-text-black">
Download Results
</button>
<button @click="view='chart'" type="button"
class="tw-rounded-lg tw-bg-hara hover:tw-bg-morehara tw-px-3.5 tw-py-1.5 tw-text-sm tw-font-semibold tw-text-black">
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
<path fill="black" fill-rule="evenodd"
d="M2.879 3.879C2 4.757 2 6.172 2 9v6c0 2.828 0 4.243.879 5.121C3.757 21 5.172 21 8 21h8c2.828 0 4.243 0 5.121-.879C22 19.243 22 17.828 22 15V9c0-2.828 0-4.243-.879-5.121C20.243 3 18.828 3 16 3H8c-2.828 0-4.243 0-5.121.879M16 8a1 1 0 0 1 1 1v8a1 1 0 1 1-2 0V9a1 1 0 0 1 1-1m-7 3a1 1 0 1 0-2 0v6a1 1 0 1 0 2 0zm4 2a1 1 0 1 0-2 0v4a1 1 0 1 0 2 0z"
clip-rule="evenodd"/>
</svg>
</button>
</div>
<div class="tw-flex tw-justify-end tw-mb-3 tw-gap-3" v-if="view==='chart'">
<!-- Chart Type Bar, Scatter -->
<q-select v-model="selected_chart_type" :options="chartTypes" label="Chart Type" outlined dense map-options
@update:model-value="chartChange"
emit-value
class="tw-w-1/6"
color="yellow" dark/>
<q-select v-model="selected_x_column" :options="getXAxisOptions" label="X Axis" outlined dense
class="tw-w-1/6"
color="yellow" dark/>
<q-select v-model="selected_y_column" :options="getYAxisOptions" label="Y Axis" outlined dense
class="tw-w-1/6"
color="yellow" dark/>
<button @click="shareOnTwitter" type="button"
class="tw-rounded-lg tw-bg-hara hover:tw-bg-morehara tw-px-3.5 tw-py-1.5 tw-text-sm tw-font-semibold tw-text-black">
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
<path fill="black"
d="M22.71 6.29a1 1 0 0 0-1.42 0L20 7.59V2a1 1 0 0 0-2 0v5.59l-1.29-1.3a1 1 0 0 0-1.42 1.42l3 3a1 1 0 0 0 .33.21a.94.94 0 0 0 .76 0a1 1 0 0 0 .33-.21l3-3a1 1 0 0 0 0-1.42M19 13a1 1 0 0 0-1 1v.38l-1.48-1.48a2.79 2.79 0 0 0-3.93 0l-.7.7l-2.48-2.48a2.85 2.85 0 0 0-3.93 0L4 12.6V7a1 1 0 0 1 1-1h8a1 1 0 0 0 0-2H5a3 3 0 0 0-3 3v12a3 3 0 0 0 3 3h12a3 3 0 0 0 3-3v-5a1 1 0 0 0-1-1M5 20a1 1 0 0 1-1-1v-3.57l2.9-2.9a.79.79 0 0 1 1.09 0l3.17 3.17l4.3 4.3Zm13-1a.9.9 0 0 1-.18.53L13.31 15l.7-.7a.77.77 0 0 1 1.1 0L18 17.21Z"/>
</svg>
</button>
<button @click="view='table'" type="button"
class="tw-rounded-lg tw-bg-hara hover:tw-bg-morehara tw-px-3.5 tw-py-1.5 tw-text-sm tw-font-semibold tw-text-black">
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
<path fill="none" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M3 5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2zm0 5h18M10 3v18"/>
</svg>
</button>
</div>
<q-table v-if="view==='table'"
dark
flat
color="#101010"
:rows="rows"
row-key="name"
style=""
class="dd-scroll tw-rounded-xl tw-border tw-border-editorborder"
>
<template v-slot:header="props">
<q-tr :props="props" class="tw-bg-primarybg tw-border-b">
<q-th
v-for="col in props.cols"
:key="col.name"
:props="props"
class="tw-text-sm tw-font-normal tw-text-primarytext"
>
{{ col.label }}
</q-th>
</q-tr>
</template>
<template v-slot:body="props">
<q-tr :props="props" :class="props.pageIndex % 2===0 ? 'tw-bg-tablebg' : 'tw-bg-primarybg'">
<q-td
v-for="col in props.cols"
:key="col.name"
:props="props"
class="tw-text-sm tw-font-normal tw-text-primarytext"
>
{{ props.row[col.name] }}
</q-td>
</q-tr>
</template>
</q-table>
<VueEcharts v-if="view==='chart' && selected_chart_type==='bar'" class="card_style22" ref="barchart"
:option="getBarOptions" :autoresize="true"
style="height: 400px"></VueEcharts>
<VueEcharts v-if="view==='chart' && selected_chart_type==='scatter'" class="card_style2" ref="scatterchart"
:option="getScatterOptions" :autoresize="true"
style="height: 400px"></VueEcharts>
</q-card-section>
<div class="tw-flex">
<div class="tw-flex-grow text-white">
Made with
<q-icon name="favorite" size="xs" color="red"/>
in India
</div>
<div class="tw-flex text-white">
<span>Powered by DuckDB </span>
<div class="bg-white q-pa-sm tw-flex-auto q-ml-sm">
<img style="width:50px" class="tw-rounded-lg" src="DuckDB_Logo.png"/>
</div>
</div>
</div>
<!-- Import Remote File URL Dialog -->
<q-dialog v-model="importParquetUrlDialog">
<q-card class="tw-bg-editorborder q-px-md" style="min-width: 400px;border-radius: 12px;">
<q-card-section>
<div class="text-h6 tw-text-primarytext">Import File from URL</div>
</q-card-section>
<q-card-section class="q-pt-none">
<q-input
v-model="parquetUrl"
outlined
label="Enter URL of Parquet or JSON file"
class="tw-bg-[#141414]"
dense
dark
color="hara"
/>
</q-card-section>
<q-card-actions align="right" class="tw-mb-4">
<q-btn flat label="Cancel" no-caps class="q-mr-sm" color="white" v-close-popup />
<q-btn
label="Import"
class="tw-bg-hara tw-px-20 tw-py-3 tw-font-semibold tw-rounded-lg"
@click="processRemoteFileUrl"
no-caps
:disable="!parquetUrl"
/>
</q-card-actions>
</q-card>
</q-dialog>
<q-dialog v-model="getStartedDialog">
<q-card class="get-started-card tw-bg-editorborder tw-px-10">
<img src="/welcome-duck.png" alt="duck">
<div class="row full-width text-center tw-text-primarytext tw-text-2xl tw-font-bold">
Run SQL queries on your CSV|Parquet files in browser
</div>
<q-card-section class="tw-text-primarytext" style="padding:0 20px 0 20px">
<div>
<q-carousel
v-model="slide"
transition-prev="scale"
transition-next="scale"
swipeable
animated
control-color="white"
prev-icon="arrow_left"
next-icon="arrow_right"
navigation-icon="radio_button_unchecked"
navigation
padding
arrows
height="200px"
class="tw-bg-editorborder text-white rounded-borders"
>
<template v-slot:navigation-icon="{ active, btnProps, onClick }">
<q-btn v-if="active" size="sm" icon="radio_button_checked" class="tw-text-hara" flat round dense
@click="onClick"/>
<q-btn v-else size="xs" :icon="btnProps.icon" color="white" flat round dense @click="onClick"/>
</template>
<q-carousel-slide name="step_1" class="column no-wrap flex-center">
<!-- <q-icon name="style" size="56px" /> -->
<div class="q-mt-md text-center tw-text-hara tw-text-base">
{{ step_1 }}
</div>
</q-carousel-slide>
<q-carousel-slide name="step_2" class="column no-wrap flex-center">
<!-- <q-icon name="live_tv" size="56px" /> -->
<div class="q-mt-md text-center tw-text-hara tw-text-base">
{{ step_2 }}
</div>
</q-carousel-slide>
<q-carousel-slide name="step_3" class="column no-wrap flex-center">
<!-- <q-icon name="layers" size="56px" /> -->
<div class="q-mt-md text-center tw-text-hara tw-text-base">
{{ step_3 }}
</div>
</q-carousel-slide>
<q-carousel-slide name="step_4" class="column no-wrap flex-center">
<!-- <q-icon name="terrain" size="56px" /> -->
<div class="q-mt-md text-center tw-text-hara tw-text-base">
{{ step_4 }}
</div>
</q-carousel-slide>
<q-carousel-slide name="step_5" class="column no-wrap flex-center">
<!-- <q-icon name="terrain" size="56px" /> -->
<div class="q-mt-md text-center tw-text-hara tw-text-base">
{{ step_5 }}
</div>
</q-carousel-slide>
<q-carousel-slide name="step_6" class="column no-wrap flex-center">
<!-- <q-icon name="terrain" size="56px" /> -->
<div class="q-mt-md text-center tw-text-hara tw-text-base">
{{ step_6 }}
</div>
</q-carousel-slide>
</q-carousel>
</div>
<!-- <div class="row full-width justify-center tw-text-3xl tw-font-bold">
Welcome! 🎉
</div> -->
<!-- <div class="row full-width justify-center tw-text-lg tw-mt-6">
Unlock the power of lightning-fast analytics and data
</div>
<div class="row full-width justify-center tw-text-lg">
processing with QuackDB, your ultimate data tool.
</div> -->
</q-card-section>
<q-card-actions class="justify-center tw-pb-lg">
<button
@click="getStarted()"
class="tw-bg-hara tw-px-20 tw-py-3 tw-font-semibold tw-rounded-lg">
Get Started
</button>
</q-card-actions>
</q-card>
</q-dialog>
</q-page>
</template>
<script>
import {defineComponent, ref} from 'vue'
import * as XLSX from 'xlsx/xlsx.mjs';
import {saveAs} from 'file-saver';
import {VAceEditor} from 'vue3-ace-editor';
import './ace-config';
import sample_csv from '../assets/customers-100.csv?raw';
import * as echarts from 'echarts';
import VueEcharts from 'vue-echarts'
import numeral from "numeral";
import { DuckDBDataProtocol } from '@duckdb/duckdb-wasm';
export default defineComponent({
name: 'NewIndexPage',
data() {
return {
file: [],
columns: [],
view: 'table',
rows: [],
query: '',
tables: [], /*contains objects with keys {name, length, columns} */
selection: '',
getStartedDialog: false,
importParquetUrlDialog: false,
parquetUrl: '',
slide: 'step_1',
step_1: 'Upload multiple csv or parquet files to run sql queries',
step_2: "Smart editor to write SQL queries",
step_3: "Run SQL queries to analyze data",
step_4: "Download the query result in csv format",
step_5: "For Bar Chart on X axis select a column with string values and on Y axis select a column with numeric values",
step_6: "For Scatter Chart on X axis select a column with numeric values and on Y axis select a column with numeric values",
openDrawer: false,
tableNameSuggestions: ["table1", "table2", "table3"],
counter: 0,
sample_csv: sample_csv,
string_columns: [],
num_columns: [],
selected_x_column: '',
selected_y_column: '',
chartTypes: [
{label: 'Bar', value: 'bar'},
{label: 'Scatter', value: 'scatter'}
],
selected_chart_type: 'bar'
}
},
components: {
VAceEditor,
VueEcharts
},
async created() {
this.openPopUp();
this.$bus.on('openPopUp', this.openPopUp)
this.openDrawer = this.$q.platform.is.desktop ? true : false;
},
methods: {
chartChange() {
this.selected_x_column = this.getXAxisOptions[0]
},
shareOnTwitter() {
try {
if (this.selected_chart_type === 'bar') {
const imageDataUrl = this.$refs.barchart.getDataURL({
type: 'png',
backgroundColor: '#fff',
pixelRatio: 2
});
// Download the image file for uploading on Twitter
const link = document.createElement("a");
link.href = imageDataUrl;
link.download = "result.png";
link.click();
} else {
const imageDataUrl = this.$refs.scatterchart.getDataURL({
type: 'png',
backgroundColor: '#fff',
pixelRatio: 2
});
// Download the image file for uploading on Twitter
const link = document.createElement("a");
link.href = imageDataUrl;
link.download = "result.png";
link.click();
}
// const imageDataUrl = this.$refs.barchart.getDataURL({
// type: 'png',
// backgroundColor: '#fff',
// pixelRatio: 2
// });
//
// // Download the image file for uploading on Twitter
// const link = document.createElement("a");
// link.href = imageDataUrl;
// link.download = "result.png";
// link.click();
// Open Twitter share chart
} catch (error) {
console.error("Error capturing chart image: ", error);
}
},
initializeEditor(editor) {
let self = this;
// Define a custom autocomplete function
editor.completers.push({
getCompletions: function (editor, session, pos, prefix, callback) {
callback(null, self.tables.map(function (table) {
return {
caption: table.name,
value: table.name,
meta: "table"
};
}));
}
});
self.tables.filter(function (item) {
editor.completers.push({
getCompletions: function (editor, session, pos, prefix, callback) {
callback(null, item.children.map(function (col) {
return {
caption: col.label,
value: col.label,
meta: "column"
};
}));
}
});
return item
})
},
selectTable(name) {
this.selection = name
this.query = `SELECT *
FROM ${name} LIMIT 10;`
this.search();
},
files(files) {
this.file = files[0];
this.upload();
},
async upload() {
this.columns = []
let name = this.file.name.split(".")[0];
let fileType = this.file.name.split(".")[1].toLowerCase();
if (!['csv', 'parquet', 'json'].includes(fileType)) {
this.$refs.uploaderref.reset();
this.$q.notify({
type: 'negative',
message: `Apologies! At this moment we only support CSV, Parquet, and JSON files.`,
position: 'top'
});
return
}
name = name.replace(/[^a-zA-Z0-9]/g, "_");
let self = this;
let cont = true;
for (const table of this.tables) {
if (table.name === name) {
this.$refs.uploaderref.reset();
this.$q.notify({type: 'negative', message: `Table with name "${name}" Already Exists`, position: 'top'});
return;
}
}
self.$q.loading.show();
try {
if (fileType === 'csv') {
let fileReader = new FileReader();
fileReader.readAsText(this.file);
fileReader.onload = async function () {
try {
const text = fileReader.result;
await self.$db.registerFileText("sample_table.csv", text);
await self.$conn.query(`CREATE TABLE ${name} AS
SELECT *
FROM read_csv_auto('sample_table.csv');`);
await self.processNewTable(name);
} catch (error) {
console.error('Error processing CSV:', error);
self.$q.notify({type: 'negative', message: `Error processing CSV file: ${error.message}`, position: 'top'});
self.$q.loading.hide();
}
};
fileReader.onerror = function() {
self.$q.notify({type: 'negative', message: 'Error reading the file', position: 'top'});
self.$q.loading.hide();
};
} else if (fileType === 'parquet') {
try {
await this.$db.registerFileHandle(`${name}.parquet`, this.file, DuckDBDataProtocol.BROWSER_FILEREADER, true);
await this.$conn.query(`CREATE TABLE ${name} AS
SELECT *
FROM '${name}.parquet';`);
await this.processNewTable(name);
} catch (error) {
console.error('Error processing Parquet:', error);
this.$q.notify({type: 'negative', message: `Error processing Parquet file: ${error.message}`, position: 'top'});
this.$q.loading.hide();
}
}
} catch (error) {
console.error('Error in upload:', error);
this.$q.notify({type: 'negative', message: `Error uploading file: ${error.message}`, position: 'top'});
this.$q.loading.hide();
}
this.$refs.uploaderref.reset();
},
// Helper method to process a newly created table
// Open the custom dialog for importing Parquet from URL
importParquetFromUrl() {
this.parquetUrl = '';
this.importParquetUrlDialog = true;
},
// Process the remote file URL (Parquet or JSON) entered by the user
async processRemoteFileUrl() {
try {
if (!this.parquetUrl) return;
// Close the dialog
this.importParquetUrlDialog = false;
// Generate a table name from the URL
const urlParts = this.parquetUrl.split('/');
const filename = urlParts.pop();
const filenameParts = filename.split('.');
let name = filenameParts[0];
const fileExtension = filenameParts.length > 1 ? filenameParts.pop().toLowerCase() : '';
name = name.replace(/[^a-zA-Z0-9]/g, '_');
// Check if the file type is supported
if (!['parquet', 'csv'].includes(fileExtension)) {
this.$q.notify({
type: 'negative',
message: `Unsupported file type. Only Parquet and CSV files are supported for URL imports.`,
position: 'top'
});
return;
}
// Check if table with this name already exists
for (const table of this.tables) {
if (table.name === name) {
this.$q.notify({type: 'negative', message: `Table with name "${name}" already exists`, position: 'top'});
return;
}
}
this.$q.loading.show({
message: `Fetching ${fileExtension.toUpperCase()} file...`
});
try {
if (fileExtension === 'parquet') {
try {
// First attempt: Use direct URL registration method for Parquet files
await this.$db.registerFileURL(
`${name}.parquet`,
this.parquetUrl,
DuckDBDataProtocol.HTTP,
false // allowFullHttpReads
);
// Create a table from the registered URL Parquet file
await this.$conn.query(`CREATE TABLE ${name} AS
SELECT *
FROM '${name}.parquet';`);
// Process the newly created table
await this.processNewTable(name);
this.$q.notify({
type: 'positive',
message: `Successfully imported Parquet file from URL`,
position: 'top'
});
this.$q.loading.hide();
return;
} catch (directUrlError) {
console.log('Direct URL registration failed, falling back to fetch method:', directUrlError);
// If direct URL registration fails, fall back to fetch method
}
}
// Fallback: Try to fetch the file with the browser's fetch API
const response = await fetch(this.parquetUrl);
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
// Get array buffer from the response
const arrayBuffer = await response.arrayBuffer();
const uint8Array = new Uint8Array(arrayBuffer);
if (fileExtension === 'parquet') {
// Register the buffer with DuckDB for Parquet
await this.$db.registerFileBuffer(`${name}.parquet`, uint8Array);
// Create a table from the Parquet file
await this.$conn.query(`CREATE TABLE ${name} AS
SELECT *
FROM '${name}.parquet';`);
} else if (fileExtension === 'csv') {
// For CSV files, convert buffer to text
const decoder = new TextDecoder('utf-8');
const csvText = decoder.decode(uint8Array);
// Register the CSV file with DuckDB
await this.$db.registerFileText(`${name}.csv`, csvText);
// Create table from CSV file
await this.$conn.query(`CREATE TABLE ${name} AS
SELECT *
FROM read_csv_auto('${name}.csv');`);
}
// Process the newly created table
await this.processNewTable(name);
this.$q.notify({
type: 'positive',
message: `Successfully imported ${fileExtension.toUpperCase()} file from URL`,
position: 'top'
});
} catch (fetchError) {
console.error('Fetch error:', fetchError);
// Show a more user-friendly error message about CORS issues
if (fetchError.message.includes('NetworkError') ||
fetchError.message.includes('CORS') ||
fetchError.message.includes('Failed to fetch')) {
this.$q.notify({
type: 'negative',
message: 'The server does not allow direct access to this file due to browser security restrictions (CORS). ' +
'Try downloading the file locally first and then uploading it.',
position: 'top',
timeout: 7000
});
} else {
this.$q.notify({
type: 'negative',
message: `Error fetching ${fileExtension.toUpperCase()} file: ${fetchError.message}`,
position: 'top'
});
}
this.$q.loading.hide();
}
} catch (error) {
console.error(`Error importing file from URL:`, error);
this.$q.notify({
type: 'negative',
message: `Error importing file: ${error.message}`,
position: 'top'
});
this.$q.loading.hide();
}
},
async processNewTable(name) {
try {
this.selectTable(name);
const stmt1 = await this.$conn.prepare(`SELECT CAST(COUNT(*) AS INT) FROM ${name}`);
const res1 = await stmt1.query();
const stmt2 = await this.$conn.prepare(`SELECT COLUMN_NAME, DATA_TYPE
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = '${name}'`);
const res2 = await stmt2.query();
let len = Object.values(JSON.parse(JSON.stringify(res1.toArray()))[0])[0];
let columns = JSON.parse(JSON.stringify(res2.toArray()));
console.log(columns);
this.tables.push({
name: name,
header: 'root',
toggle: false,
length: len,
children: columns.map((obj) => {
let icon = 'tag';
if (["BIGINT", "INTEGER"].includes(obj['data_type'])) {
icon = 'tag';
} else if (obj['data_type'] === "VARCHAR") {
icon = 'abc';
} else if (['DATE', "TIMESTAMP"].includes(obj['data_type'])) {
icon = 'calendar_month';
}
return {label: obj['column_name'], icon: icon, header: 'generic'};
})
});
this.$q.loading.hide();
this.counter++;
} catch (error) {
console.error('Error processing table:', error);
this.$q.notify({type: 'negative', message: `Error processing table: ${error.message}`, position: 'top'});
this.$q.loading.hide();
}
},
async search() {
this.$q.loading.show();
try {
const q = await this.$conn.prepare(this.query);
const r = await q.query();
const replacer = (key, value) => {
if (typeof value === 'bigint') {
return value.toString(); // Convert BigInt to string representation
}
return value;
};
this.rows = JSON.parse(JSON.stringify(r.toArray(), replacer))
let columns = await this.$conn.query(`PRAGMA table_info(${this.selection});`);
let col = JSON.parse(JSON.stringify(columns.toArray(), replacer))
this.num_columns = col.filter((col) => {
return col['type'] === 'INTEGER' || col['type'] === 'BIGINT' || col['type'] === 'FLOAT' || col['type'] === 'DOUBLE' || col['type'] === 'DECIMAL'
})
this.string_columns = col.filter((col) => {
return col['type'] === 'VARCHAR' || col['type'] === 'TEXT' || col['type'] === 'DATE' || col['type'] === 'TIMESTAMP'
})
this.selected_x_column = this.string_columns[0]['name']
this.selected_y_column = this.num_columns[0]['name']
this.$q.loading.hide();
} catch (err) {
this.$q.loading.hide();
this.$q.notify({
message: err.message,
color: 'negative',
textColor: 'white',
icon: 'warning',
position: 'top',
});
}
},
async deleteTable(name, index) {
const q = await this.$conn.prepare(`DROP TABLE ${name};`);
const r = await q.query();
this.tables.splice(index, 1);
if (this.tables.length === 0) {
this.selection = '';
this.query = '';
this.rows = [];
} else {
this.selectTable(this.tables[0].name);
}
this.$q.notify({type: 'positive', message: `Table with name "${name}" Deleted!!`, position: 'top'})
},
handleDrop(e) {
e.preventDefault();
this.$refs.uploaderref.addFiles(e.dataTransfer.files);
},
download_csv() {
let self = this;
function s2ab(s) {
let buf = new ArrayBuffer(s.length);
let view = new Uint8Array(buf);
for (let i = 0; i !== s.length; ++i) view[i] = s.charCodeAt(i) & 0xff;
return buf;
}
let data = self.rows
const sheet = XLSX.utils.json_to_sheet(data);
let wbout = XLSX.write({
SheetNames: ["Sheet1"],
Sheets: {"Sheet1": sheet}
}, {
bookType: 'csv',
bookSST: true,
type: 'binary'
});
saveAs(new Blob([s2ab(wbout)], {
type: "application/octet-stream"
}), `${self.selection}.csv`);
},
openPopUp() {
this.getStartedDialog = true;
},
getStarted() {
this.getStartedDialog = false,
this.slide = 'step_1';
},
uploadSampleFile() {
const blob = new Blob([this.sample_csv], {type: 'text/csv'});
const file = new File([blob], 'sample.csv', {type: 'text/csv'});
this.$refs.uploaderref.addFiles([file]);
}
},
computed: {
getBarOptions() {
return {
backgroundColor: '#0b0b0b',
grid: {
left: '5%',
right: '5%',
bottom: '20%'
},
tooltip: {},
dataZoom: [{
type: 'slider',
showDataShadow: false,
fillerColor: 'rgba(196, 249, 99)',
showDetail: false,
height: '8px',
handleIcon: 'M20 11C20 15.4183 16.4183 19 12 19C7.58172 19 4 15.4183 4 11C4 6.58172 7.58172 3 12 3C16.4183 3 20 6.58172 20 11Z',
handleSize: '200%',
handleStyle: {
borderColor: 'transparent',
color: 'rgba(196, 249, 99)',
backgroundColor: 'rgba(196, 249, 99)',
borderWidth: 0,
},
moveHandleStyle: {
opacity: 0,
color: 'rgba(196, 249, 99)',
},
dataBackground: {
lineStyle: {
type: 'dashed',
width: '0',
opacity: 0
},
areaStyle: {
color: 'rgba(196, 249, 99)'
}
},
selectedDataBackground: {
areaStyle: {
color: 'rgba(196, 249, 99)'
}
},
bottom: 50,
textStyle: {
fontSize: 0
},
show: this.rows.length > 10
}],
xAxis: {
data: this.rows.map((row) => {
return row[this.selected_x_column]
}),
axisLine: {
show: false,
},
axisTick: {
show: false
},
},
yAxis: {
axisLine: {
show: false,
},
axisTick: {
show: false
},
splitLine: {
show: false
},
axisLabel: {
formatter: function (params) {
return numeral(params).format('0,0 a').toUpperCase()
},
fontWeight: 700,
fontSize: '11.22px'
}
},
series: [{
type: 'bar',
itemStyle: {
borderRadius: [8, 8],
color: 'rgba(196, 249, 99)',
},
barGap: 0,
data: this.rows.map((row) => {
return row[this.selected_y_column]
})
}]
}
},
getXAxisOptions() {
if (this.selected_chart_type === 'scatter') {
return this.num_columns.map((col) => {
return col['name']
})
}
return this.string_columns.map((col) => {
return col['name']
})
},
getYAxisOptions() {
return this.num_columns.map((col) => {
return col['name']
})
},
getScatterOptions() {
let self = this;
let data = self.rows.map((row) => {
return [row[self.selected_x_column], row[self.selected_y_column]]
})
console.log(data)
return {
backgroundColor: '#0b0b0b',
grid: {
left: '5%',
right: '5%',
bottom: '20%'
},
tooltip: {},
dataZoom: [{
type: 'slider',
showDataShadow: false,
fillerColor: 'rgba(196, 249, 99)',
showDetail: false,
height: '8px',
handleIcon: 'M20 11C20 15.4183 16.4183 19 12 19C7.58172 19 4 15.4183 4 11C4 6.58172 7.58172 3 12 3C16.4183 3 20 6.58172 20 11Z',
handleSize: '200%',
handleStyle: {
borderColor: 'transparent',
color: 'rgba(196, 249, 99)',
backgroundColor: 'rgba(196, 249, 99)',
borderWidth: 0,
},
moveHandleStyle: {
opacity: 0,
color: 'rgba(196, 249, 99)',
},
dataBackground: {
lineStyle: {
type: 'dashed',
width: '0',
opacity: 0
},
areaStyle: {
color: 'rgba(196, 249, 99)'
}
},
selectedDataBackground: {
areaStyle: {
color: 'rgba(196, 249, 99)'
}
},
bottom: 50,
textStyle: {
fontSize: 0
},
show: this.rows.length > 10
}],
xAxis: {
axisLine: {
show: false,
},
axisTick: {
show: false
},
splitLine: {
show: false
},
},
yAxis: {
axisLine: {
show: false,
},
axisTick: {
show: false
},
splitLine: {
show: false
},
axisLabel: {
formatter: function (params) {
return numeral(params).format('0,0 a').toUpperCase()
},
fontWeight: 700,
fontSize: '11.22',
}
},
series: [{
type: 'scatter',
itemStyle: {
color: 'rgba(196, 249, 99)',
},
symbolSize: 10,
data: self.rows.map((row) => {
return [row[self.selected_x_column], row[self.selected_y_column]]
})
}]
}
}
}
// async beforeUnmount() {
// await this.$conn.close();
// await this.$db.terminate();
// await this.$worker.terminate();
// }
})
</script>
<style scoped>
.rotate {
transform: rotate(90deg);
transition-duration: 0.1s;
}
.get-started-card {
width: 30rem;
height: 39rem;
border-radius: 50px;
}
</style>
<style>
.q-field__control {
border-radius: 12px !important;
background-color: rgba(30, 30, 30) !important;
}
/*.q-field--dark .q-field__control:before{*/
/* border: 0.5px solid rgba(196, 249, 99) !important;*/
/*}*/
</style>
================================================
FILE: src/pages/ace-config.ts
================================================
import ace from 'ace-builds';
import modeJsonUrl from 'ace-builds/src-noconflict/mode-json?url';
ace.config.setModuleUrl('ace/mode/json', modeJsonUrl);
import modeJavascriptUrl from 'ace-builds/src-noconflict/mode-javascript?url';
ace.config.setModuleUrl('ace/mode/javascript', modeJavascriptUrl);
import modeHtmlUrl from 'ace-builds/src-noconflict/mode-html?url';
ace.config.setModuleUrl('ace/mode/html', modeHtmlUrl);
import modeYamlUrl from 'ace-builds/src-noconflict/mode-yaml?url';
ace.config.setModuleUrl('ace/mode/yaml', modeYamlUrl);
import themeGithubUrl from 'ace-builds/src-noconflict/theme-github?url';
ace.config.setModuleUrl('ace/theme/github', themeGithubUrl);
import themeChromeUrl from 'ace-builds/src-noconflict/theme-chrome?url';
ace.config.setModuleUrl('ace/theme/chrome', themeChromeUrl);
import themeMonokaiUrl from 'ace-builds/src-noconflict/theme-monokai?url';
ace.config.setModuleUrl('ace/theme/monokai', themeMonokaiUrl);
import workerBaseUrl from 'ace-builds/src-noconflict/worker-base?url';
ace.config.setModuleUrl('ace/mode/base', workerBaseUrl);
import workerJsonUrl from 'ace-builds/src-noconflict/worker-json?url';
ace.config.setModuleUrl('ace/mode/json_worker', workerJsonUrl);
import workerJavascriptUrl from 'ace-builds/src-noconflict/worker-javascript?url';
ace.config.setModuleUrl('ace/mode/javascript_worker', workerJavascriptUrl);
import workerHtmlUrl from 'ace-builds/src-noconflict/worker-html?url';
ace.config.setModuleUrl('ace/mode/html_worker', workerHtmlUrl);
import workerYamlUrl from 'ace-builds/src-noconflict/worker-yaml?url';
ace.config.setModuleUrl('ace/mode/yaml_worker', workerYamlUrl);
import snippetsHtmlUrl from 'ace-builds/src-noconflict/snippets/html?url';
ace.config.setModuleUrl('ace/snippets/html', snippetsHtmlUrl);
import snippetsJsUrl from 'ace-builds/src-noconflict/snippets/javascript?url';
ace.config.setModuleUrl('ace/snippets/javascript', snippetsJsUrl);
import snippetsYamlUrl from 'ace-builds/src-noconflict/snippets/yaml?url';
ace.config.setModuleUrl('ace/snippets/javascript', snippetsYamlUrl);
import snippetsJsonUrl from 'ace-builds/src-noconflict/snippets/json?url';
ace.config.setModuleUrl('ace/snippets/json', snippetsJsonUrl);
import 'ace-builds/src-noconflict/ext-language_tools';
ace.require("ace/ext/language_tools");
================================================
FILE: src/router/index.js
================================================
import { route } from 'quasar/wrappers'
import { createRouter, createMemoryHistory, createWebHistory, createWebHashHistory } from 'vue-router'
import routes from './routes'
/*
* If not building with SSR mode, you can
* directly export the Router instantiation;
*
* The function below can be async too; either use
* async/await or return a Promise which resolves
* with the Router instance.
*/
export default route(function (/* { store, ssrContext } */) {
const createHistory = process.env.SERVER
? createMemoryHistory
: (process.env.VUE_ROUTER_MODE === 'history' ? createWebHistory : createWebHashHistory)
const Router = createRouter({
scrollBehavior: () => ({ left: 0, top: 0 }),
routes,
// Leave this as is and make changes in quasar.conf.js instead!
// quasar.conf.js -> build -> vueRouterMode
// quasar.conf.js -> build -> publicPath
history: createHistory(process.env.VUE_ROUTER_BASE)
})
return Router
})
================================================
FILE: src/router/routes.js
================================================
const routes = [
{
path: '/',
component: () => import('layouts/MainLayout.vue'),
children: [
{ path: '', component: () => import('pages/NewIndexPage.vue') }
]
},
// Always leave this as last one,
// but you can also remove it
{
path: '/:catchAll(.*)*',
component: () => import('pages/ErrorNotFound.vue')
}
]
export default routes
================================================
FILE: tailwind.config.js
================================================
module.exports = {
content: ["./src/**/*.{html,js,vue}"],
prefix:'tw-',
theme: {
extend: {
colors: {
'primarybg' : '#101010',
'secondarybg': '#0D0F10',
'tertiarybg': '#1E1E1E',
'dark': "#0B0B0B",
'editorborder':'#202020',
'tablebg':'#181818',
'twilightbg':'#141414',
'primarytext': "#DFDEDF",
'secondarytext': '#666666',
'help': '#386ED2',
'setting': '#9542A1',
'hara': '#C4F963',
'morehara':'#b5f83a'
}
},
},
plugins: [],
}
gitextract_46xnnl1r/ ├── .editorconfig ├── .github/ │ └── ISSUE_TEMPLATE/ │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── .npmrc ├── .vscode/ │ ├── extensions.json │ └── settings.json ├── Dockerfile ├── LICENSE.md ├── README.md ├── docker-compose.yaml ├── index.html ├── jsconfig.json ├── package.json ├── postcss.config.js ├── quasar.config.js ├── src/ │ ├── App.vue │ ├── assets/ │ │ ├── Sample-Spreadsheet-10-rows.csv │ │ └── customers-100.csv │ ├── boot/ │ │ ├── .gitkeep │ │ ├── bus.js │ │ └── duckdb.js │ ├── components/ │ │ └── EssentialLink.vue │ ├── css/ │ │ └── app.css │ ├── layouts/ │ │ └── MainLayout.vue │ ├── pages/ │ │ ├── ErrorNotFound.vue │ │ ├── IndexPage.vue │ │ ├── NewIndexPage.vue │ │ └── ace-config.ts │ └── router/ │ ├── index.js │ └── routes.js └── tailwind.config.js
SYMBOL INDEX (1 symbols across 1 files)
FILE: src/boot/duckdb.js
constant MANUAL_BUNDLES (line 8) | const MANUAL_BUNDLES = {
Condensed preview — 32 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (101K chars).
[
{
"path": ".editorconfig",
"chars": 147,
"preview": "root = true\n\n[*]\ncharset = utf-8\nindent_style = space\nindent_size = 2\nend_of_line = lf\ninsert_final_newline = true\ntrim_"
},
{
"path": ".github/ISSUE_TEMPLATE/bug_report.md",
"chars": 834,
"preview": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Describe the b"
},
{
"path": ".github/ISSUE_TEMPLATE/feature_request.md",
"chars": 595,
"preview": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Is your fea"
},
{
"path": ".gitignore",
"chars": 429,
"preview": ".DS_Store\n.thumbs.db\nnode_modules\n\n# Quasar core related directories\n.quasar\n/dist\n\n# Cordova related directories and fi"
},
{
"path": ".npmrc",
"chars": 76,
"preview": "# pnpm-related options\nshamefully-hoist=true\nstrict-peer-dependencies=false\n"
},
{
"path": ".vscode/extensions.json",
"chars": 265,
"preview": "{\n \"recommendations\": [\n \"editorconfig.editorconfig\",\n \"vue.volar\",\n \"wayou.vscode-todo-highlight\"\n ],\n \"unw"
},
{
"path": ".vscode/settings.json",
"chars": 90,
"preview": "{\n \"editor.bracketPairColorization.enabled\": true,\n \"editor.guides.bracketPairs\": true\n}"
},
{
"path": "Dockerfile",
"chars": 490,
"preview": "# Use Node.js Alpine image as base\nFROM node:alpine\n\n# Set working directory in the container\nWORKDIR /usr/src/app\n\n# Co"
},
{
"path": "LICENSE.md",
"chars": 1071,
"preview": "MIT License\n\nCopyright (c) 2024 incentius-foss\n\nPermission is hereby granted, free of charge, to any person obtaining a "
},
{
"path": "README.md",
"chars": 4009,
"preview": "<p align=\"center\">\n <img src=\"/public/logo3.svg\" alt=\"Image Description\" height=\"120px\" width=\"320px\" />\n</p>\n\n\n\nWhatTh"
},
{
"path": "docker-compose.yaml",
"chars": 152,
"preview": "version: '3.7'\n\nservices:\n quasar:\n build:\n context: .\n dockerfile: Dockerfile\n ports:\n - \"9000:90"
},
{
"path": "index.html",
"chars": 3415,
"preview": "<!DOCTYPE html>\n<html>\n<head>\n <title>WhatTheDuck - Run SQL queries on your CSV|Parquet files in browser</title>\n\n <me"
},
{
"path": "jsconfig.json",
"chars": 616,
"preview": "{\n \"compilerOptions\": {\n \"baseUrl\": \".\",\n \"paths\": {\n \"src/*\": [\n \"src/*\"\n ],\n \"app/*\": [\n "
},
{
"path": "package.json",
"chars": 935,
"preview": "{\n \"name\": \"duckdb\",\n \"version\": \"0.0.1\",\n \"description\": \"A Quasar Project\",\n \"productName\": \"Quasar App\",\n \"autho"
},
{
"path": "postcss.config.js",
"chars": 845,
"preview": "/* eslint-disable */\n// https://github.com/michael-ciniawsky/postcss-load-config\n\nmodule.exports = {\n plugins: [\n //"
},
{
"path": "quasar.config.js",
"chars": 6009,
"preview": "/* eslint-env node */\n\n/*\n * This file runs in a Node context (it's NOT transpiled by Babel), so use only\n * the ES6 fea"
},
{
"path": "src/App.vue",
"chars": 150,
"preview": "<template>\n <router-view />\n</template>\n\n<script>\nimport { defineComponent } from 'vue'\n\nexport default defineComponent"
},
{
"path": "src/assets/Sample-Spreadsheet-10-rows.csv",
"chars": 1087,
"preview": "1,\"Eldon Base for stackable storage shelf, platinum\",Muhammed MacIntyre,3,-213.25,38.94,35,Nunavut,Storage & Organizatio"
},
{
"path": "src/assets/customers-100.csv",
"chars": 17261,
"preview": "Index,Customer Id,First Name,Last Name,Company,City,Country,Phone 1,Phone 2,Email,Subscription Date,Website\r\n1,DD37Cf93a"
},
{
"path": "src/boot/.gitkeep",
"chars": 0,
"preview": ""
},
{
"path": "src/boot/bus.js",
"chars": 269,
"preview": "import { EventBus } from 'quasar'\nimport { boot } from 'quasar/wrappers'\n\nconst bus = new EventBus()\n\nexport default boo"
},
{
"path": "src/boot/duckdb.js",
"chars": 1143,
"preview": "import {boot} from 'quasar/wrappers'\nimport * as duckdb from '@duckdb/duckdb-wasm';\nimport duckdb_wasm from '@duckdb/duc"
},
{
"path": "src/components/EssentialLink.vue",
"chars": 742,
"preview": "<template>\n <q-item\n clickable\n tag=\"a\"\n target=\"_blank\"\n :href=\"link\"\n >\n <q-item-section\n v-if=\""
},
{
"path": "src/css/app.css",
"chars": 705,
"preview": "/* app global css */\n@tailwind base;\n@tailwind components;\n@tailwind utilities;\n\n.dd-scroll::-webkit-scrollbar {\n width"
},
{
"path": "src/layouts/MainLayout.vue",
"chars": 1915,
"preview": "<template>\n <q-layout view=\"HHh Lpr lFf\">\n <q-header class=\"tw-bg-primarybg tw-h-14\" style=\"\">\n <div class=\"tw-"
},
{
"path": "src/pages/ErrorNotFound.vue",
"chars": 576,
"preview": "<template>\n <div class=\"fullscreen bg-blue text-white text-center q-pa-md flex flex-center\">\n <div>\n <div style"
},
{
"path": "src/pages/IndexPage.vue",
"chars": 3095,
"preview": "<template>\n <q-page class=\"q-pa-sm\">\n <q-card class=\"no-border no-shadow\">\n <q-card-section class=\"q-pa-sm row\""
},
{
"path": "src/pages/NewIndexPage.vue",
"chars": 44404,
"preview": "<template>\n <q-drawer v-model=\"openDrawer\" class=\"q-pt-sm tw-bg-secondarybg dd-scroll\" show-if-above>\n <div class=\"t"
},
{
"path": "src/pages/ace-config.ts",
"chars": 2316,
"preview": "import ace from 'ace-builds';\n\nimport modeJsonUrl from 'ace-builds/src-noconflict/mode-json?url';\nace.config.setModuleUr"
},
{
"path": "src/router/index.js",
"chars": 962,
"preview": "import { route } from 'quasar/wrappers'\nimport { createRouter, createMemoryHistory, createWebHistory, createWebHashHisto"
},
{
"path": "src/router/routes.js",
"chars": 375,
"preview": "\nconst routes = [\n {\n path: '/',\n component: () => import('layouts/MainLayout.vue'),\n children: [\n { path"
},
{
"path": "tailwind.config.js",
"chars": 559,
"preview": "module.exports = {\n content: [\"./src/**/*.{html,js,vue}\"],\n prefix:'tw-',\n theme: {\n extend: {\n colors: {\n "
}
]
About this extraction
This page contains the full source code of the incentius-foss/WhatTheDuck GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 32 files (93.3 KB), approximately 29.5k tokens, and a symbol index with 1 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.