Compare commits

..

1 commit

Author SHA1 Message Date
James Long d7e71158b9 Add ability to import Actual files; enable export on desktop 2022-05-25 22:42:10 -04:00
557 changed files with 109301 additions and 28239 deletions

121
.circleci/config.yml Normal file
View file

@ -0,0 +1,121 @@
default_config: &default_config
environment:
SENTRY_ORG: shift-reset-llc
SENTRY_PROJECT: actual
YARN_CACHE_FOLDER: ~/.cache/yarn
CSC_LINK: ~/windows-shift-reset-llc.p12
cached_files: &cached_files
paths:
- ~/.cache/yarn
- node_modules
- ./packages/desktop-electron/node_modules
- ./packages/loot-core/node_modules
- ./mobile/node_modules
- ./import-ynab4/node_modules
- ./api/node_modules
- ./node-libofx/node_modules
- ./loot-design/node_modules
- ./desktop-client/node_modules
key: v3-dependencies-{{ checksum "yarn.lock" }}
version_tag_only: &version_tag_only
filters:
branches:
ignore: /.*/
tags:
only: /^\d+\.\d+\.\d+$/
version: 2.1
orbs:
win: circleci/windows@2.2.0
jobs:
test:
<<: *default_config
docker:
- image: circleci/node:12.13
working_directory: ~/repo
steps:
- checkout
- restore_cache:
keys:
- v3-dependencies-{{ checksum "yarn.lock" }}
- run: yarn install --pure-lockfile
- save_cache:
<<: *cached_files
- run: yarn test
build_windows:
<<: *default_config
executor:
name: win/default
working_directory: ~/repo
steps:
- checkout
- run:
command: npm install -g @sentry/cli --unsafe-perm
shell: bash
- run:
command: echo $WINDOWS_CERT | base64 --decode >> ${HOME}/windows-shift-reset-llc.p12
shell: bash
- run:
command: yarn install --pure-lockfile
shell: bash
- run:
command: ./bin/package --release --version ${CIRCLE_TAG}
shell: bash
build_linux:
<<: *default_config
docker:
- image: circleci/node:12.13
working_directory: ~/repo
steps:
- checkout
- restore_cache:
keys:
- v3-dependencies-{{ checksum "yarn.lock" }}
- run: yarn install --pure-lockfile
- run: sudo npm install -g @sentry/cli --unsafe-perm
- run: ./bin/package --release --version ${CIRCLE_TAG}
workflows:
version: 2
test:
jobs:
- test
build_version:
jobs:
- test:
<<: *version_tag_only
- build_windows:
<<: *version_tag_only
requires:
- test
- build_linux:
<<: *version_tag_only
requires:
- test

View file

@ -1,39 +0,0 @@
module.exports = {
plugins: ['prettier', 'import'],
extends: ['react-app'],
rules: {
'prettier/prettier': 'error',
'no-unused-vars': 'off',
'no-loop-func': 'off',
'no-restricted-globals': 'off',
'import/no-useless-path-segments': 'error',
'import/order': [
'error',
{
alphabetize: {
caseInsensitive: true,
order: 'asc'
},
groups: [
'builtin', // Built-in types are first
'external',
['sibling', 'parent'], // Then sibling and parent types. They can be mingled together
'index' // Then the index file
],
'newlines-between': 'always',
pathGroups: [
// Enforce that React (and react-related packages) is the first import
{ group: 'builtin', pattern: 'react?(-*)', position: 'before' },
// Separate imports from Actual from "real" external imports
{
group: 'external',
pattern: 'loot-{core,design}/**/*',
position: 'after'
}
],
pathGroupsExcludedImportTypes: ['react']
}
]
}
};

View file

@ -1,77 +0,0 @@
name: Bug Report
description: File a bug report also known as an issue or problem.
title: '[Bug]: '
labels: ['bug', 'needs triage']
body:
- type: markdown
id: intro-md
attributes:
value: |
Thanks for taking the time to fill out this bug report! Please ensure you provide as much information as asked to better assist in confirming and identifying a fix for the bug report.
- type: dropdown
id: existing-issue
attributes:
label: 'Verified issue does not already exist?'
description: 'Please search to see if an issue already exists for the issue you encountered.'
options:
- 'I have searched and found no existing issue'
validations:
required: true
- type: textarea
id: what-happened
attributes:
label: What happened?
description: Also tell us, what did you expect to happen?
placeholder: Tell us what you see!
value: 'A bug happened!'
validations:
required: true
- type: textarea
id: errors-received
attributes:
label: 'What error did you receive?'
description: 'If you received an error or a message on the screen, please provide that here.'
validations:
required: false
- type: markdown
id: env-info
attributes:
value: '## Environment Details'
- type: dropdown
id: hosting
attributes:
label: Where are you hosting Actual?
description: Where are you running your instance of Actual from?
options:
- Locally via Yarn
- Docker
- Fly.io
- NAS
- Other
validations:
required: false
- type: dropdown
id: browsers
attributes:
label: What browsers are you seeing the problem on?
multiple: true
options:
- Firefox
- Chrome
- Safari
- Microsoft Edge
- Other
- type: dropdown
id: operating-system
attributes:
label: Operating System
description: What operating system are you using?
options:
- Windows 11
- Windows 10
- Mac OSX
- Linux
- Mobile Device
- Other
validations:
required: false

View file

@ -1,5 +0,0 @@
blank_issues_enabled: false
contact_links:
- name: Feature Request?
url: https://github.com/actualbudget/actual/discussions/new?category=ideas
about: Website is hosted via GitHub Discussions under actualbudget/actual

View file

@ -1,19 +0,0 @@
name: Setup
runs:
using: composite
steps:
- name: Install node
uses: actions/setup-node@v1
with:
node-version: 16.15.0
- name: Cache
uses: actions/cache@v2
id: cache
with:
path: '**/node_modules'
key: yarn-v1-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }}
- name: Install
run: yarn --immutable
shell: bash
if: steps.cache.outputs.cache-hit != 'true'

View file

@ -1,51 +0,0 @@
name: Build
defaults:
run:
shell: bash
env:
CI: true
on:
push:
branches:
- master
pull_request:
branches: '*'
jobs:
api:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up environment
uses: ./.github/actions/setup
- name: Build API
run: cd packages/loot-core && yarn build:api
web:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up environment
uses: ./.github/actions/setup
- name: Build Web
run: ./bin/package-browser
electron:
# As electron builds take longer, we only run them in master.
if: github.event_name != 'pull_request'
strategy:
matrix:
os:
- ubuntu-latest
- windows-latest
- macos-latest
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- name: Set up environment
uses: ./.github/actions/setup
- name: Build Electron
run: ./bin/package

View file

@ -1,18 +0,0 @@
name: Linter
on:
push:
branches:
- master
pull_request:
branches: '*'
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up environment
uses: ./.github/actions/setup
- name: Lint
run: yarn lint

View file

@ -1,14 +0,0 @@
name: Mark new issue for triage
on:
issues:
types: [opened]
jobs:
needs-triage:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-ecosystem/action-add-labels@v1
with:
labels: needs triage

View file

@ -1,22 +0,0 @@
name: Close inactive issues
on:
schedule:
- cron: "30 1 * * *"
jobs:
close-issues:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v5
with:
days-before-issue-stale: 90
days-before-issue-close: -1
stale-issue-label: "stale"
stale-issue-message: "🚧🚨 This issue is being marked as stale due to 90 days of inactivity. 🚧🚨"
days-before-pr-stale: -1
days-before-pr-close: -1
only-labels: 'needs triage'
repo-token: ${{ secrets.GITHUB_TOKEN }}

View file

@ -1,18 +0,0 @@
name: Test
on:
push:
branches:
- master
pull_request:
branches: '*'
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up environment
uses: ./.github/actions/setup
- name: Test
run: yarn test

11
.gitignore vendored
View file

@ -18,14 +18,3 @@ bundle.mobile.js
bundle.mobile.js.map
.sentryclirc
export-2020-01-10.csv
**/*.log
# Yarn
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
!.yarn/versions

1
.nvmrc
View file

@ -1 +0,0 @@
v16.15.0

View file

@ -1,4 +0,0 @@
{
"singleQuote": true,
"trailingComma": "none"
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,7 +0,0 @@
nodeLinker: node-modules
plugins:
- path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs
spec: "@yarnpkg/plugin-workspace-tools"
yarnPath: .yarn/releases/yarn-3.2.0.cjs

View file

@ -1,39 +1,73 @@
## Getting Started
Actual is a local-first personal finance tool. It is 100% free and open-source, written in NodeJS, it has a synchronization element so that all your changes can move between devices without any heavy lifting.
This is the source code for [Actual](https://actualbudget.com), a local-first personal finance tool. It is 100% free and open-source.
If you are interested in contributing, or want to know how development works, see [CONTRIBUTING.md](https://github.com/actualbudget/actual/blob/master/CONTRIBUTING.md) we would love to have you.
If you are only interested in running the latest version, you don't need this repo. You can get the latest version through npm.
Want to say thanks? Click the ⭐ at the top of the page.
More docs are available in the [docs](https://github.com/actualbudget/actual/tree/master/docs) folder.
## Key Links
If you are interested in contributing, or want to know how development works, see [CONTRIBUTING.md](https://github.com/actualbudget/actual/blob/master/CONTRIBUTING.md)
* Actual [discord](https://discord.gg/pRYNYr4W5A) community.
* Actual [Community Documentation](https://actualbudget.github.io/docs)
Join the [discord](https://discord.gg/pRYNYr4W5A)!
## Installation
If you are only interested in running the latest version and not contributing to the source code, you don't need to clone this repo. You can get the latest version through npm.
**Please Note:** While the Actual repository holds source code for the mobile applications that were supported when Actual was closed source, these are no longer supported on the Open Source version of Actual.
### The easy way: using a server (recommended)
The easiest way to get Actual running is to use the [actual-server](https://github.com/actualbudget/actual-server) project. That is the server for syncing changes across devices, and it comes with the latest version of Actual. The server will provide both the web project and a server for syncing.
You can get up and running quickly and easily by following our [Running Actual Locally Guide](https://actualbudget.github.io/docs/Installing/Local/your-own-machine)
```
git clone https://github.com/actualbudget/actual-server.git
cd actual-server
yarn install
yarn start
```
## Documentation
Navigate to https://localhost:5006 in your browser and you will see Actual.
We have a wide range of documentation on how to use Actual, this is all available in our [Community Documentation](https://actualbudget.github.io/docs), this includes topics on Budgeting, Account Management, Tips & Tricks and some documentation for developers.
You should deploy the server somewhere so you can access your data from anywhere. See instructions on the [actual-server](https://github.com/actualbudget/actual-server) repo.
### Without a server
This will give you a fully local web app without a server. This npm package is the `packages/desktop-client` package in this repo built for production:
```
yarn add @actual-app/web
```
Now you need to serve the files in `node_modules/@actual-app/web/build`. One way to do it:
```
cd node_modules/@actual-app/web/build
npx http-server .
```
Navigate to http://localhost:8080 and you should see Actual.
## Building
If you want to build the latest version, see [releasing.md](https://github.com/actualbudget/actual/blob/master/docs/releasing.md). It provides instructions for building this code into the same artifacts that come from npm.
## Run locally
Both the electron and web app can started with a single command. When running in development, it will store data in a `data` directory in the root of the `actual` directory.
First, make sure to run `yarn install` to install all dependencies.
In the root of the project:
```
yarn start # Run the electron app
yarn start:browser # Run the web app
```
## Code structure
The Actual app is split up into a few packages:
The app is split up into a few packages:
* loot-core - The core application that runs on any platform
* loot-design - The generic design components that make up the UI
* desktop-client - The desktop UI
* desktop-electron - The desktop app
* mobile - The mobile app
More information on the project structure is available in our [community documentation](https://actualbudget.github.io/docs/Developers/project-layout).
More docs are available in the [docs](https://github.com/actualbudget/actual/tree/master/docs) folder.

View file

@ -6,7 +6,6 @@ VERSION=""
BETA=""
RELEASE=""
RELEASE_NOTES=""
CI=${CI:-false}
cd "$ROOT/.."
@ -65,29 +64,41 @@ if [ -n "$RELEASE" ]; then
fi
if [ "$OSTYPE" == "msys" ]; then
if [ $CI != true ]; then
if [ -z "$CIRCLE_TAG" ]; then
read -s -p "Windows certificate password: " -r CSC_KEY_PASSWORD
export CSC_KEY_PASSWORD
elif [ -n "$CIRCLE_TAG" ]; then
# We only want to run this on CircleCI as Github doesn't have the CSC_KEY_PASSWORD secret set.
else
certutil -f -p ${CSC_KEY_PASSWORD} -importPfx ~/windows-shift-reset-llc.p12
fi
fi
# We only need to run linting once (and this doesn't seem to work on
# Windows for some reason)
if [[ $CI != true && "$OSTYPE" == "darwin"* ]]; then
if [[ "$OSTYPE" == "darwin"* ]]; then
yarn lint
fi
yarn patch-package
./node_modules/.bin/patch-package
yarn workspace mobile patch-package
(
cd packages/loot-design;
../../node_modules/.bin/patch-package
)
yarn workspace loot-core build:node
(
cd packages/mobile;
../../node_modules/.bin/patch-package
)
yarn workspace @actual-app/web build
(
cd packages/loot-core;
NODE_ENV=production yarn build:node
)
(
cd packages/desktop-client;
yarn build
)
rm -fr packages/desktop-electron/client-build
cp -r packages/desktop-client/build packages/desktop-electron/client-build
@ -120,7 +131,7 @@ fi
(
cd packages/desktop-electron;
yarn clean;
rm -rf dist;
export npm_config_better_sqlite3_binary_host="https://static.actualbudget.com/prebuild/better-sqlite3"
@ -128,11 +139,11 @@ fi
if [ -f ../../.secret-tokens ]; then
source ../../.secret-tokens
fi
yarn build --publish always -c.releaseInfo.releaseNotes="$RELEASE_NOTES" --arm64 --x64
./node_modules/.bin/electron-builder --publish always -c.releaseInfo.releaseNotes="$RELEASE_NOTES" --arm64 --x64
echo "\nCreated release $VERSION with release notes \"$RELEASE_NOTES\""
elif [ "$RELEASE" == "beta" ]; then
yarn build --publish never --arm64 --x64
./node_modules/.bin/electron-builder --publish never --arm64 --x64
WINDOWS_FILE="./dist/Actual Setup $VERSION.exe"
if [ -f "$WINDOWS_FILE" ]; then
@ -158,6 +169,6 @@ fi
echo "No linux file found"
fi
else
SKIP_NOTARIZATION=true yarn build --publish never --x64
SKIP_NOTARIZATION=true ./node_modules/.bin/electron-builder --publish never --x64
fi
)

View file

@ -1,9 +1,8 @@
#!/bin/bash -e
#!/bin/sh -e
ROOT=`dirname $0`
VERSION=""
RELEASE=""
CI=${CI:-false}
cd "$ROOT/.."
@ -51,13 +50,21 @@ if [ -n "$RELEASE" ]; then
fi
fi
# There's no need to check linting in CI as it'll be done in a different step.
if [ $CI != true ]; then
yarn lint
fi
yarn lint
ACTUAL_RELEASE_TYPE=$RELEASE yarn workspace loot-core build:browser
(
cd packages/loot-design;
../../node_modules/.bin/patch-package
)
REACT_APP_RELEASE_TYPE=$RELEASE yarn workspace @actual-app/web build:browser
(
cd packages/loot-core;
ACTUAL_RELEASE_TYPE=$RELEASE yarn build:browser
)
(
cd packages/desktop-client;
REACT_APP_RELEASE_TYPE=$RELEASE yarn build:browser
)
echo "packages/desktop-client/build"

20
docs/API.md Normal file
View file

@ -0,0 +1,20 @@
Previous docs for the API are [here](https://actualbudget.com/docs/developers/using-the-API/). The API is currently being improved. Previously, the API connected to an existing running instance of Actual. Now the API is bundled and fully isolated, capable of running all of Actual itself. Setting up the API is different because of this.
You need to call `init` and pass it the directory where your files live. Call `load-budget` to load the file you want to work on. After that, you can use the same API as before.
Example:
```js
let actual = require('@actual-app/api');
await actual.init({
config: {
dataDir: join(__dirname, 'user-files')
}
});
await actual.internal.send('load-budget', { id: 'My-Finances' });
await actual.getAccounts();
```

44
docs/releasing.md Normal file
View file

@ -0,0 +1,44 @@
# How to cut a release
In the open-source version of Actual, all updates go through npm. There are two libraries:
* `@actual-app/api`: The API for the underlying functionality. This includes the entire backend of Actual, meant to be used with node.
* `@actual-app/web`: A web build that will serve the app with a web frontend. This includes both the frontend and backend of Actual. It includes the backend as well because it's built to be used as a Web Worker.
Both the API and web libraries are versioned together. This may change in the future, but because the web library also brings along its own backend it's easier to maintain a single version for now. That makes it clear which version the backend is regardless of library.
## Releasing `@actual-app/api`
This generates a bundle for the API:
```
cd packages/loot-core
yarn build:api
```
The built files live in `lib-dist`, so we need to copy them to the API package:
```
cp lib-dist/bundle.api* ../api/app
```
Next, bump the version on package.json. Finally, publish it:
```
npm publish
```
## Releasing `@actual-app/web`
In the root of `actual` (not just `desktop-client`), run this:
```
./bin/package-browser
```
This will compile both the backend and the frontend into a single directory in `packages/desktop-client/build`. This directory is all the files that need to be published. After bumping the version, publish `desktop-client`:
```
cd packages/desktop-client
npm publish
```

View file

@ -0,0 +1,24 @@
const { join, resolve } = require('path');
const { createTransformer } = require('babel-jest');
const packagePath = resolve('./');
const packageGlob = join(packagePath, 'packages/*');
module.exports = createTransformer({
babelrcRoots: packageGlob,
// TODO: This is awful and a mess and we should fix it.
//
// Forcing this on allows certain packages in node_modules to be
// exported as ESM, which jest usually errors on. node_modules are
// usually not transformed, but you can allowlist one in the
// `jest.config.js` for your project like this:
//
// transformIgnorePatterns: [
// '/node_modules/(?!absurd-sql)'
// ],
//
// Without this explicit plugin, even though Jest transforms the
// module it won't recognize ESM
plugins: ['@babel/plugin-transform-modules-commonjs']
});

View file

@ -0,0 +1,9 @@
const { join, resolve } = require('path');
const { createTransformer } = require('babel-jest');
const packagePath = resolve('./');
const packageGlob = join(packagePath, 'packages/*');
module.exports = createTransformer({
babelrcRoots: packageGlob
});

8
jest.config.js Normal file
View file

@ -0,0 +1,8 @@
module.exports = {
projects: [
'<rootDir>/packages/loot-core',
'<rootDir>/packages/loot-core/jest.web.config.js',
'<rootDir>/packages/loot-design',
'<rootDir>/packages/loot-design/jest.rn.config.js'
]
};

View file

@ -15,39 +15,77 @@
"workspaces": {
"packages": [
"packages/*"
],
"nohoist": [
"**/better-sqlite3",
"**/better-sqlite3/**",
"mobile/react-native",
"mobile/react-native-*",
"mobile/rn-fetch-blob",
"mobile/**/event-target-shim",
"mobile/@sentry/react-native",
"mobile/nodejs-mobile-react-native",
"**/mobile/nodejs-mobile-react-native/**",
"**/@react-native-community/**",
"**/@react-navigation/**",
"mobile/react-navigation",
"mobile/react-navigation-tabs",
"mobile/rn-snoopy",
"mobile/rn-snoopy/**",
"mobile/detox",
"mobile/detox/**",
"mobile/jsc-android",
"mobile/jsc-android/**",
"**/react-native-web",
"**/react-native-web/**",
"**/@sentry/cli"
]
},
"scripts": {
"start": "npm-run-all --parallel 'start:desktop-*'",
"start:desktop-node": "yarn workspace loot-core watch:node",
"start:desktop-client": "yarn workspace @actual-app/web watch",
"start:desktop-electron": "yarn workspace Actual watch",
"start:browser": "npm-run-all --parallel 'start:browser-*'",
"start:browser-backend": "yarn workspace loot-core watch:browser",
"start:browser-frontend": "yarn workspace @actual-app/web start:browser",
"test": "yarn workspaces foreach --parallel --verbose run test",
"test:debug": "yarn workspaces foreach --verbose run test",
"bootstrap": "./bootstrap",
"start": "npm-run-all --parallel start:desktop-*",
"start:desktop-node": "cd packages/loot-core && yarn watch:node",
"start:desktop-client": "cd packages/desktop-client && yarn watch",
"start:desktop-electron": "cd packages/desktop-electron && yarn watch",
"start:browser": "npm-run-all --parallel start:browser-*",
"start:browser-backend": "cd packages/loot-core && yarn watch:browser",
"start:browser-frontend": "cd packages/desktop-client && yarn start:browser",
"test": "./node_modules/.bin/jest --maxWorkers=4",
"test:debug": "node ./node_modules/.bin/jest --runInBand --useStderr",
"test:debug-brk": "node --inspect-brk ./node_modules/.bin/jest --runInBand",
"make-test-db": "./packages/loot-core/src/bin/make-test-db ./data",
"make-fresh-db": "./packages/loot-core/src/bin/migrate reset --db ./data/user1/db.sqlite",
"rebuild-electron": "./node_modules/.bin/electron-rebuild -f -m ./packages/loot-core",
"rebuild-node": "yarn workspace loot-core rebuild",
"lint": "yarn workspaces foreach --verbose run lint",
"rebuild-node": "cd packages/loot-core && npm rebuild",
"lint": "cd packages/loot-core && yarn lint",
"postinstall": "rm -rf ./packages/loot-design/node_modules/react && rm -rf ./packages/mobile/node_modules/react && rm -rf ./node_modules/react-native && patch-package"
},
"devDependencies": {
"@babel/plugin-transform-modules-commonjs": "^7.15.0",
"cross-env": "^5.1.5",
"husky": "^3.0.4",
"npm-run-all": "^4.1.3",
"patch-package": "^6.1.2",
"prettier": "^1.18.1",
"pretty-quick": "^1.11.1",
"shelljs": "^0.8.2",
"source-map-support": "^0.5.21"
},
"eslintConfig": {
"extends": "react-app",
"rules": {
"no-unused-vars": "off",
"no-loop-func": "off",
"no-restricted-globals": "off"
}
},
"prettier": {
"singleQuote": true,
"trailingComma": "none"
},
"resolutions": {
"@babel/preset-env": "^7.15.1",
"@babel/core": "^7.15.1",
"@babel/runtime": "^7.15.1",
"@babel/helper-plugin-utils": "^7.14.5",
"react-error-overlay": "6.0.9"
},
"packageManager": "yarn@3.2.0"
"@babel/helper-plugin-utils": "^7.14.5"
}
}

View file

@ -1 +0,0 @@
app/bundle.api.js*

69607
packages/api/app/bundle.api.js Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,7 +1,8 @@
let bundle = require('./app/bundle.api.js');
let injected = require('./injected');
let methods = require('./methods');
let utils = require('./utils');
let injected = require('./injected');
let actualApp;
async function init({ budgetId, config } = {}) {

View file

@ -1,21 +1,13 @@
{
"name": "@actual-app/api",
"version": "4.1.5",
"version": "4.0.2",
"license": "MIT",
"description": "An API for Actual",
"main": "index.js",
"files": [
"app",
"default-db.sqlite",
"index.js",
"injected.js",
"methods.js",
"migrations",
"utils.js"
],
"license": "MIT",
"dependencies": {
"better-sqlite3": "^7.5.0",
"node-fetch": "^1.6.3",
"uuid": "3.3.2"
"uuid": "3.3.2",
"node-fetch": "^1.6.3"
}
}

View file

@ -1,5 +1,5 @@
function amountToInteger(n) {
return Math.round(n * 100);
return Math.round(n * 100) | 0;
}
function integerToAmount(n) {

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,178 @@
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 100;
font-display: swap;
src: url("Inter-Thin.woff2?v=3.12") format("woff2"),
url("Inter-Thin.woff?v=3.12") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: italic;
font-weight: 100;
font-display: swap;
src: url("Inter-ThinItalic.woff2?v=3.12") format("woff2"),
url("Inter-ThinItalic.woff?v=3.12") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 200;
font-display: swap;
src: url("Inter-ExtraLight.woff2?v=3.12") format("woff2"),
url("Inter-ExtraLight.woff?v=3.12") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: italic;
font-weight: 200;
font-display: swap;
src: url("Inter-ExtraLightItalic.woff2?v=3.12") format("woff2"),
url("Inter-ExtraLightItalic.woff?v=3.12") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 300;
font-display: swap;
src: url("Inter-Light.woff2?v=3.12") format("woff2"),
url("Inter-Light.woff?v=3.12") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: italic;
font-weight: 300;
font-display: swap;
src: url("Inter-LightItalic.woff2?v=3.12") format("woff2"),
url("Inter-LightItalic.woff?v=3.12") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url("Inter-Regular.woff2?v=3.12") format("woff2"),
url("Inter-Regular.woff?v=3.12") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: italic;
font-weight: 400;
font-display: swap;
src: url("Inter-Italic.woff2?v=3.12") format("woff2"),
url("Inter-Italic.woff?v=3.12") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url("Inter-Medium.woff2?v=3.12") format("woff2"),
url("Inter-Medium.woff?v=3.12") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: italic;
font-weight: 500;
font-display: swap;
src: url("Inter-MediumItalic.woff2?v=3.12") format("woff2"),
url("Inter-MediumItalic.woff?v=3.12") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url("Inter-SemiBold.woff2?v=3.12") format("woff2"),
url("Inter-SemiBold.woff?v=3.12") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: italic;
font-weight: 600;
font-display: swap;
src: url("Inter-SemiBoldItalic.woff2?v=3.12") format("woff2"),
url("Inter-SemiBoldItalic.woff?v=3.12") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url("Inter-Bold.woff2?v=3.12") format("woff2"),
url("Inter-Bold.woff?v=3.12") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: italic;
font-weight: 700;
font-display: swap;
src: url("Inter-BoldItalic.woff2?v=3.12") format("woff2"),
url("Inter-BoldItalic.woff?v=3.12") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 800;
font-display: swap;
src: url("Inter-ExtraBold.woff2?v=3.12") format("woff2"),
url("Inter-ExtraBold.woff?v=3.12") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: italic;
font-weight: 800;
font-display: swap;
src: url("Inter-ExtraBoldItalic.woff2?v=3.12") format("woff2"),
url("Inter-ExtraBoldItalic.woff?v=3.12") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 900;
font-display: swap;
src: url("Inter-Black.woff2?v=3.12") format("woff2"),
url("Inter-Black.woff?v=3.12") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: italic;
font-weight: 900;
font-display: swap;
src: url("Inter-BlackItalic.woff2?v=3.12") format("woff2"),
url("Inter-BlackItalic.woff?v=3.12") format("woff");
}
/* -------------------------------------------------------
Variable font.
Usage:
html { font-family: 'Inter', sans-serif; }
@supports (font-variation-settings: normal) {
html { font-family: 'Inter var', sans-serif; }
}
*/
@font-face {
font-family: 'Inter var';
font-weight: 100 900;
font-display: swap;
font-style: normal;
font-named-instance: 'Regular';
src: url("Inter-roman.var.woff2?v=3.12") format("woff2");
}
@font-face {
font-family: 'Inter var';
font-weight: 100 900;
font-display: swap;
font-style: italic;
font-named-instance: 'Italic';
src: url("Inter-italic.var.woff2?v=3.12") format("woff2");
}

View file

@ -0,0 +1,10 @@
/*
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
Content-Security-Policy: default-src 'self' https://sentry.io blob:; script-src 'self' 'unsafe-eval' blob:; style-src 'self' 'unsafe-inline'; connect-src 'self' https://*.actualbudget.com https://api.mixpanel.com https://sentry.io;
/kcab/*
Content-Security-Policy: default-src 'self' https://sentry.io blob:; script-src 'self' 'unsafe-eval' blob:; style-src 'self' 'unsafe-inline'; connect-src 'self' https://*.actualbudget.com https://api.mixpanel.com https://sentry.io;
/*.wasm
Content-Type: application/wasm

View file

@ -0,0 +1 @@
/* /index.html 200

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

View file

@ -0,0 +1,22 @@
{
"main.js": "/static/js/main.a27b6a9d.chunk.js",
"main.js.map": "/static/js/main.a27b6a9d.chunk.js.map",
"static/js/1.5b0e59dd.chunk.js": "/static/js/1.5b0e59dd.chunk.js",
"static/js/1.5b0e59dd.chunk.js.map": "/static/js/1.5b0e59dd.chunk.js.map",
"static/js/2.c0b1e834.chunk.js": "/static/js/2.c0b1e834.chunk.js",
"static/js/2.c0b1e834.chunk.js.map": "/static/js/2.c0b1e834.chunk.js.map",
"static/css/3.d37e3633.chunk.css": "/static/css/3.d37e3633.chunk.css",
"static/js/3.03227275.chunk.js": "/static/js/3.03227275.chunk.js",
"static/js/3.03227275.chunk.js.map": "/static/js/3.03227275.chunk.js.map",
"runtime~main.js": "/static/js/runtime~main.d358c073.js",
"runtime~main.js.map": "/static/js/runtime~main.d358c073.js.map",
"static/js/browser-server.06b54be5.worker.js": "/static/js/browser-server.06b54be5.worker.js",
"static/js/browser-server.06b54be5.worker.js.map": "/static/js/browser-server.06b54be5.worker.js.map",
"static/media/DateSelect.left.png": "/static/media/DateSelect.left.1ae92674.png",
"static/media/DateSelect.right.png": "/static/media/DateSelect.right.2a470dc0.png",
"static/media/bg.svg": "/static/media/bg.ad9dce3b.svg",
"static/css/3.d37e3633.chunk.css.map": "/static/css/3.d37e3633.chunk.css.map",
"index.html": "/index.html",
"precache-manifest.dd195c6a2e68b333b66a2a33a8dce9a0.js": "/precache-manifest.dd195c6a2e68b333b66a2a33a8dce9a0.js",
"service-worker.js": "/service-worker.js"
}

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<square150x150logo src="/mstile-150x150.png"/>
<TileColor>#da532c</TileColor>
</tile>
</msapplication>
</browserconfig>

View file

@ -0,0 +1,341 @@
/* BASICS */
.CodeMirror {
/* Set height, width, borders, and global font properties here */
font-family: monospace;
height: 300px;
color: black;
}
/* PADDING */
.CodeMirror-lines {
padding: 4px 0; /* Vertical padding around content */
}
.CodeMirror pre {
padding: 0 4px; /* Horizontal padding of content */
}
.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
background-color: white; /* The little square between H and V scrollbars */
}
/* GUTTER */
.CodeMirror-gutters {
border-right: 1px solid #ddd;
background-color: #f7f7f7;
white-space: nowrap;
}
.CodeMirror-linenumbers {}
.CodeMirror-linenumber {
padding: 0 3px 0 5px;
min-width: 20px;
text-align: right;
color: #999;
white-space: nowrap;
}
.CodeMirror-guttermarker { color: black; }
.CodeMirror-guttermarker-subtle { color: #999; }
/* CURSOR */
.CodeMirror-cursor {
border-left: 1px solid black;
border-right: none;
width: 0;
}
/* Shown when moving in bi-directional text */
.CodeMirror div.CodeMirror-secondarycursor {
border-left: 1px solid silver;
}
.cm-fat-cursor .CodeMirror-cursor {
width: auto;
border: 0 !important;
background: #7e7;
}
.cm-fat-cursor div.CodeMirror-cursors {
z-index: 1;
}
.cm-animate-fat-cursor {
width: auto;
border: 0;
-webkit-animation: blink 1.06s steps(1) infinite;
-moz-animation: blink 1.06s steps(1) infinite;
animation: blink 1.06s steps(1) infinite;
background-color: #7e7;
}
@-moz-keyframes blink {
0% {}
50% { background-color: transparent; }
100% {}
}
@-webkit-keyframes blink {
0% {}
50% { background-color: transparent; }
100% {}
}
@keyframes blink {
0% {}
50% { background-color: transparent; }
100% {}
}
/* Can style cursor different in overwrite (non-insert) mode */
.CodeMirror-overwrite .CodeMirror-cursor {}
.cm-tab { display: inline-block; text-decoration: inherit; }
.CodeMirror-rulers {
position: absolute;
left: 0; right: 0; top: -50px; bottom: -20px;
overflow: hidden;
}
.CodeMirror-ruler {
border-left: 1px solid #ccc;
top: 0; bottom: 0;
position: absolute;
}
/* DEFAULT THEME */
.cm-s-default .cm-header {color: blue;}
.cm-s-default .cm-quote {color: #090;}
.cm-negative {color: #d44;}
.cm-positive {color: #292;}
.cm-header, .cm-strong {font-weight: bold;}
.cm-em {font-style: italic;}
.cm-link {text-decoration: underline;}
.cm-strikethrough {text-decoration: line-through;}
.cm-s-default .cm-keyword {color: #708;}
.cm-s-default .cm-atom {color: #219;}
.cm-s-default .cm-number {color: #164;}
.cm-s-default .cm-def {color: #00f;}
.cm-s-default .cm-variable,
.cm-s-default .cm-punctuation,
.cm-s-default .cm-property,
.cm-s-default .cm-operator {}
.cm-s-default .cm-variable-2 {color: #05a;}
.cm-s-default .cm-variable-3 {color: #085;}
.cm-s-default .cm-comment {color: #a50;}
.cm-s-default .cm-string {color: #a11;}
.cm-s-default .cm-string-2 {color: #f50;}
.cm-s-default .cm-meta {color: #555;}
.cm-s-default .cm-qualifier {color: #555;}
.cm-s-default .cm-builtin {color: #30a;}
.cm-s-default .cm-bracket {color: #997;}
.cm-s-default .cm-tag {color: #170;}
.cm-s-default .cm-attribute {color: #00c;}
.cm-s-default .cm-hr {color: #999;}
.cm-s-default .cm-link {color: #00c;}
.cm-s-default .cm-error {color: #f00;}
.cm-invalidchar {color: #f00;}
.CodeMirror-composing { border-bottom: 2px solid; }
/* Default styles for common addons */
div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
.CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); }
.CodeMirror-activeline-background {background: #e8f2ff;}
/* STOP */
/* The rest of this file contains styles related to the mechanics of
the editor. You probably shouldn't touch them. */
.CodeMirror {
position: relative;
overflow: hidden;
background: white;
}
.CodeMirror-scroll {
overflow: scroll !important; /* Things will break if this is overridden */
/* 30px is the magic margin used to hide the element's real scrollbars */
/* See overflow: hidden in .CodeMirror */
margin-bottom: -30px; margin-right: -30px;
padding-bottom: 30px;
height: 100%;
outline: none; /* Prevent dragging from highlighting the element */
position: relative;
}
.CodeMirror-sizer {
position: relative;
border-right: 30px solid transparent;
}
/* The fake, visible scrollbars. Used to force redraw during scrolling
before actual scrolling happens, thus preventing shaking and
flickering artifacts. */
.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
position: absolute;
z-index: 6;
display: none;
}
.CodeMirror-vscrollbar {
right: 0; top: 0;
overflow-x: hidden;
overflow-y: scroll;
}
.CodeMirror-hscrollbar {
bottom: 0; left: 0;
overflow-y: hidden;
overflow-x: scroll;
}
.CodeMirror-scrollbar-filler {
right: 0; bottom: 0;
}
.CodeMirror-gutter-filler {
left: 0; bottom: 0;
}
.CodeMirror-gutters {
position: absolute; left: 0; top: 0;
min-height: 100%;
z-index: 3;
}
.CodeMirror-gutter {
white-space: normal;
height: 100%;
display: inline-block;
vertical-align: top;
margin-bottom: -30px;
}
.CodeMirror-gutter-wrapper {
position: absolute;
z-index: 4;
background: none !important;
border: none !important;
}
.CodeMirror-gutter-background {
position: absolute;
top: 0; bottom: 0;
z-index: 4;
}
.CodeMirror-gutter-elt {
position: absolute;
cursor: default;
z-index: 4;
}
.CodeMirror-gutter-wrapper {
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
}
.CodeMirror-lines {
cursor: text;
min-height: 1px; /* prevents collapsing before first draw */
}
.CodeMirror pre {
/* Reset some styles that the rest of the page might have set */
-moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
border-width: 0;
background: transparent;
font-family: inherit;
font-size: inherit;
margin: 0;
white-space: pre;
word-wrap: normal;
line-height: inherit;
color: inherit;
z-index: 2;
position: relative;
overflow: visible;
-webkit-tap-highlight-color: transparent;
-webkit-font-variant-ligatures: contextual;
font-variant-ligatures: contextual;
}
.CodeMirror-wrap pre {
word-wrap: break-word;
white-space: pre-wrap;
word-break: normal;
}
.CodeMirror-linebackground {
position: absolute;
left: 0; right: 0; top: 0; bottom: 0;
z-index: 0;
}
.CodeMirror-linewidget {
position: relative;
z-index: 2;
overflow: auto;
}
.CodeMirror-widget {}
.CodeMirror-code {
outline: none;
}
/* Force content-box sizing for the elements where we expect it */
.CodeMirror-scroll,
.CodeMirror-sizer,
.CodeMirror-gutter,
.CodeMirror-gutters,
.CodeMirror-linenumber {
-moz-box-sizing: content-box;
box-sizing: content-box;
}
.CodeMirror-measure {
position: absolute;
width: 100%;
height: 0;
overflow: hidden;
visibility: hidden;
}
.CodeMirror-cursor {
position: absolute;
pointer-events: none;
}
.CodeMirror-measure pre { position: static; }
div.CodeMirror-cursors {
visibility: hidden;
position: relative;
z-index: 3;
}
div.CodeMirror-dragcursors {
visibility: visible;
}
.CodeMirror-focused div.CodeMirror-cursors {
visibility: visible;
}
.CodeMirror-selected { background: #d9d9d9; }
.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
.CodeMirror-crosshair { cursor: crosshair; }
.CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; }
.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; }
.cm-searching {
background: #ffa;
background: rgba(255, 255, 0, .4);
}
/* Used to force a border model for a node */
.cm-force-border { padding-right: .1px; }
@media print {
/* Hide the cursor when printing */
.CodeMirror div.CodeMirror-cursors {
visibility: hidden;
}
}
/* See issue #2901 */
.cm-tab-wrap-hack:after { content: ''; }
/* Help users use markselection to safely style text background */
span.CodeMirror-selectedtext { background: none; }

View file

@ -0,0 +1,17 @@
default-db.sqlite
migrations/1632571489012_remove_cache.js
migrations/1615745967948_meta.sql
migrations/1555786194328_remove_category_group_unique.sql
migrations/1567699552727_budget.sql
migrations/1608652596044_trans_views.sql
migrations/1548957970627_remove-db-version.sql
migrations/1561751833510_indexes.sql
migrations/1550601598648_payees.sql
migrations/1582384163573_cleared.sql
migrations/1618975177358_schedules.sql
migrations/.force-copy-windows
migrations/1597756566448_rules.sql
migrations/1616167010796_accounts_order.sql
migrations/1608652596043_parent_field.sql
migrations/1612625548236_optimize.sql
migrations/1614782639336_trans_views2.sql

Binary file not shown.

View file

@ -0,0 +1,5 @@
BEGIN TRANSACTION;
DROP TABLE db_version;
COMMIT;

View file

@ -0,0 +1,23 @@
BEGIN TRANSACTION;
CREATE TABLE payees
(id TEXT PRIMARY KEY,
name TEXT,
category TEXT,
tombstone INTEGER DEFAULT 0,
transfer_acct TEXT);
CREATE TABLE payee_rules
(id TEXT PRIMARY KEY,
payee_id TEXT,
type TEXT,
value TEXT,
tombstone INTEGER DEFAULT 0);
CREATE INDEX payee_rules_lowercase_index ON payee_rules(LOWER(value));
CREATE TABLE payee_mapping
(id TEXT PRIMARY KEY,
targetId TEXT);
COMMIT;

View file

@ -0,0 +1,25 @@
BEGIN TRANSACTION;
CREATE TEMPORARY TABLE category_groups_tmp
(id TEXT PRIMARY KEY,
name TEXT UNIQUE,
is_income INTEGER DEFAULT 0,
sort_order REAL,
tombstone INTEGER DEFAULT 0);
INSERT INTO category_groups_tmp SELECT * FROM category_groups;
DROP TABLE category_groups;
CREATE TABLE category_groups
(id TEXT PRIMARY KEY,
name TEXT,
is_income INTEGER DEFAULT 0,
sort_order REAL,
tombstone INTEGER DEFAULT 0);
INSERT INTO category_groups SELECT * FROM category_groups_tmp;
DROP TABLE category_groups_tmp;
COMMIT;

View file

@ -0,0 +1,7 @@
BEGIN TRANSACTION;
CREATE INDEX trans_category_date ON transactions(category, date);
CREATE INDEX trans_category ON transactions(category);
CREATE INDEX trans_date ON transactions(date);
COMMIT;

View file

@ -0,0 +1,38 @@
BEGIN TRANSACTION;
DELETE FROM spreadsheet_cells WHERE
name NOT LIKE '%!budget\_%' ESCAPE '\' AND
name NOT LIKE '%!carryover\_%' ESCAPE '\' AND
name NOT LIKE '%!buffered';
UPDATE OR REPLACE spreadsheet_cells SET name = REPLACE(name, '_', '-');
UPDATE OR REPLACE spreadsheet_cells SET
name =
SUBSTR(name, 1, 28) ||
'-' ||
SUBSTR(name, 29, 4) ||
'-' ||
SUBSTR(name, 33, 4) ||
'-' ||
SUBSTR(name, 37, 4) ||
'-' ||
SUBSTR(name, 41, 12)
WHERE name LIKE '%!budget-%' AND LENGTH(name) = 52;
UPDATE OR REPLACE spreadsheet_cells SET
name =
SUBSTR(name, 1, 31) ||
'-' ||
SUBSTR(name, 32, 4) ||
'-' ||
SUBSTR(name, 36, 4) ||
'-' ||
SUBSTR(name, 40, 4) ||
'-' ||
SUBSTR(name, 44, 12)
WHERE name LIKE '%!carryover-%' AND LENGTH(name) = 55;
UPDATE spreadsheet_cells SET expr = SUBSTR(expr, 2) WHERE name LIKE '%!carryover-%';
COMMIT;

View file

@ -0,0 +1,6 @@
BEGIN TRANSACTION;
ALTER TABLE transactions ADD COLUMN cleared INTEGER DEFAULT 1;
ALTER TABLE transactions ADD COLUMN pending INTEGER DEFAULT 0;
COMMIT;

View file

@ -0,0 +1,10 @@
BEGIN TRANSACTION;
CREATE TABLE rules
(id TEXT PRIMARY KEY,
stage TEXT,
conditions TEXT,
actions TEXT,
tombstone INTEGER DEFAULT 0);
COMMIT;

View file

@ -0,0 +1,13 @@
BEGIN TRANSACTION;
ALTER TABLE transactions ADD COLUMN parent_id TEXT;
UPDATE transactions SET
parent_id = CASE
WHEN isChild THEN SUBSTR(id, 1, INSTR(id, '/') - 1)
ELSE NULL
END;
CREATE INDEX trans_parent_id ON transactions(parent_id);
COMMIT;

View file

@ -0,0 +1,56 @@
BEGIN TRANSACTION;
DROP VIEW IF EXISTS v_transactions_layer2;
CREATE VIEW v_transactions_layer2 AS
SELECT
t.id AS id,
t.isParent AS is_parent,
t.isChild AS is_child,
t.acct AS account,
CASE WHEN t.isChild = 0 THEN NULL ELSE t.parent_id END AS parent_id,
CASE WHEN t.isParent = 1 THEN NULL ELSE cm.transferId END AS category,
pm.targetId AS payee,
t.imported_description AS imported_payee,
IFNULL(t.amount, 0) AS amount,
t.notes AS notes,
t.date AS date,
t.financial_id AS imported_id,
t.error AS error,
t.starting_balance_flag AS starting_balance_flag,
t.transferred_id AS transfer_id,
t.sort_order AS sort_order,
t.cleared AS cleared,
t.tombstone AS tombstone
FROM transactions t
LEFT JOIN category_mapping cm ON cm.id = t.category
LEFT JOIN payee_mapping pm ON pm.id = t.description
WHERE
t.date IS NOT NULL AND
t.acct IS NOT NULL;
CREATE INDEX trans_sorted ON transactions(date desc, starting_balance_flag, sort_order desc, id);
DROP VIEW IF EXISTS v_transactions_layer1;
CREATE VIEW v_transactions_layer1 AS
SELECT t.* FROM v_transactions_layer2 t
LEFT JOIN transactions t2 ON (t.is_child = 1 AND t2.id = t.parent_id)
WHERE IFNULL(t.tombstone, 0) = 0 AND IFNULL(t2.tombstone, 0) = 0;
DROP VIEW IF EXISTS v_transactions;
CREATE VIEW v_transactions AS
SELECT t.* FROM v_transactions_layer1 t
ORDER BY t.date desc, t.starting_balance_flag, t.sort_order desc, t.id;
DROP VIEW IF EXISTS v_categories;
CREATE VIEW v_categories AS
SELECT
id,
name,
is_income,
cat_group AS "group",
sort_order,
tombstone
FROM categories;
COMMIT;

View file

@ -0,0 +1,7 @@
BEGIN TRANSACTION;
CREATE INDEX messages_crdt_search ON messages_crdt(dataset, row, column, timestamp);
ANALYZE;
COMMIT;

View file

@ -0,0 +1,33 @@
BEGIN TRANSACTION;
-- This adds the isChild/parent_id constraint in `where`
DROP VIEW IF EXISTS v_transactions_layer2;
CREATE VIEW v_transactions_layer2 AS
SELECT
t.id AS id,
t.isParent AS is_parent,
t.isChild AS is_child,
t.acct AS account,
CASE WHEN t.isChild = 0 THEN NULL ELSE t.parent_id END AS parent_id,
CASE WHEN t.isParent = 1 THEN NULL ELSE cm.transferId END AS category,
pm.targetId AS payee,
t.imported_description AS imported_payee,
IFNULL(t.amount, 0) AS amount,
t.notes AS notes,
t.date AS date,
t.financial_id AS imported_id,
t.error AS error,
t.starting_balance_flag AS starting_balance_flag,
t.transferred_id AS transfer_id,
t.sort_order AS sort_order,
t.cleared AS cleared,
t.tombstone AS tombstone
FROM transactions t
LEFT JOIN category_mapping cm ON cm.id = t.category
LEFT JOIN payee_mapping pm ON pm.id = t.description
WHERE
t.date IS NOT NULL AND
t.acct IS NOT NULL AND
(t.isChild = 0 OR t.parent_id IS NOT NULL);
COMMIT;

View file

@ -0,0 +1,10 @@
BEGIN TRANSACTION;
CREATE TABLE __meta__ (key TEXT PRIMARY KEY, value TEXT);
DROP VIEW IF EXISTS v_transactions_layer2;
DROP VIEW IF EXISTS v_transactions_layer1;
DROP VIEW IF EXISTS v_transactions;
DROP VIEW IF EXISTS v_categories;
COMMIT;

View file

@ -0,0 +1,5 @@
BEGIN TRANSACTION;
ALTER TABLE accounts ADD COLUMN sort_order REAL;
COMMIT;

View file

@ -0,0 +1,28 @@
BEGIN TRANSACTION;
CREATE TABLE schedules
(id TEXT PRIMARY KEY,
rule TEXT,
active INTEGER DEFAULT 0,
completed INTEGER DEFAULT 0,
posts_transaction INTEGER DEFAULT 0,
tombstone INTEGER DEFAULT 0);
CREATE TABLE schedules_next_date
(id TEXT PRIMARY KEY,
schedule_id TEXT,
local_next_date INTEGER,
local_next_date_ts INTEGER,
base_next_date INTEGER,
base_next_date_ts INTEGER);
CREATE TABLE schedules_json_paths
(schedule_id TEXT PRIMARY KEY,
payee TEXT,
account TEXT,
amount TEXT,
date TEXT);
ALTER TABLE transactions ADD COLUMN schedule TEXT;
COMMIT;

View file

@ -0,0 +1,135 @@
export default async function runMigration(db, uuid) {
function getValue(node) {
return node.expr != null ? node.expr : node.cachedValue;
}
db.execQuery(`
CREATE TABLE zero_budget_months
(id TEXT PRIMARY KEY,
buffered INTEGER DEFAULT 0);
CREATE TABLE zero_budgets
(id TEXT PRIMARY KEY,
month INTEGER,
category TEXT,
amount INTEGER DEFAULT 0,
carryover INTEGER DEFAULT 0);
CREATE TABLE reflect_budgets
(id TEXT PRIMARY KEY,
month INTEGER,
category TEXT,
amount INTEGER DEFAULT 0,
carryover INTEGER DEFAULT 0);
CREATE TABLE notes
(id TEXT PRIMARY KEY,
note TEXT);
CREATE TABLE kvcache (key TEXT PRIMARY KEY, value TEXT);
CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
`);
// Migrate budget amounts and carryover
let budget = db.runQuery(
`SELECT * FROM spreadsheet_cells WHERE name LIKE 'budget%!budget-%'`,
[],
true
);
db.transaction(() => {
budget.map(monthBudget => {
let match = monthBudget.name.match(
/^(budget-report|budget)(\d+)!budget-(.+)$/
);
if (match == null) {
console.log('Warning: invalid budget month name', monthBudget.name);
return;
}
let type = match[1];
let month = match[2].slice(0, 4) + '-' + match[2].slice(4);
let dbmonth = parseInt(match[2]);
let cat = match[3];
let amount = parseInt(getValue(monthBudget));
if (isNaN(amount)) {
amount = 0;
}
let sheetName = monthBudget.name.split('!')[0];
let carryover = db.runQuery(
'SELECT * FROM spreadsheet_cells WHERE name = ?',
[`${sheetName}!carryover-${cat}`],
true
);
let table = type === 'budget-report' ? 'reflect_budgets' : 'zero_budgets';
db.runQuery(
`INSERT INTO ${table} (id, month, category, amount, carryover) VALUES (?, ?, ?, ?, ?)`,
[
`${month}-${cat}`,
dbmonth,
cat,
amount,
carryover.length > 0 && getValue(carryover[0]) === 'true' ? 1 : 0
]
);
});
});
// Migrate buffers
let buffers = db.runQuery(
`SELECT * FROM spreadsheet_cells WHERE name LIKE 'budget%!buffered'`,
[],
true
);
db.transaction(() => {
buffers.map(buffer => {
let match = buffer.name.match(/^budget(\d+)!buffered$/);
if (match) {
let month = match[1].slice(0, 4) + '-' + match[1].slice(4);
let amount = parseInt(getValue(buffer));
if (isNaN(amount)) {
amount = 0;
}
db.runQuery(
`INSERT INTO zero_budget_months (id, buffered) VALUES (?, ?)`,
[month, amount]
);
}
});
});
// Migrate notes
let notes = db.runQuery(
`SELECT * FROM spreadsheet_cells WHERE name LIKE 'notes!%'`,
[],
true
);
let parseNote = str => {
try {
let value = JSON.parse(str);
return value && value !== '' ? value : null;
} catch (e) {
return null;
}
};
db.transaction(() => {
notes.forEach(note => {
let parsed = parseNote(getValue(note));
if (parsed) {
let [, id] = note.name.split('!');
db.runQuery(`INSERT INTO notes (id, note) VALUES (?, ?)`, [id, parsed]);
}
});
});
db.execQuery(`
DROP TABLE spreadsheet_cells;
ANALYZE;
VACUUM;
`);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 978 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View file

@ -0,0 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/><title>Actual</title><link rel="canonical" href="/"/><link rel="shortcut icon" href="/favicon.ico"/><link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png"/><link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png"/><link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png"/><link rel="manifest" href="/site.webmanifest"/><link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5"/><meta name="msapplication-TileColor" content="#da532c"/><meta name="theme-color" content="#ffffff"/><link rel="stylesheet" href="/Inter/inter.css"/><style>body,html{margin:0;padding:0;font-size:13px;color:#272630}body,button,html,input{font-family:Inter,-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,Ubuntu,Cantarell,'Fira Sans','Droid Sans','Helvetica Neue',Helvetica,Arial,sans-serif}a{color:#272630;text-decoration-skip:ink}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:focus{outline-color:#2b8fed}input,textarea{font-size:1em;font-family:Inter,-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,Ubuntu,Cantarell,'Fira Sans','Droid Sans','Helvetica Neue',Helvetica,Arial,sans-serif}#root,body,html{height:100%}body{overflow:hidden}.view{align-items:stretch;border-width:0;border-style:solid;box-sizing:border-box;display:flex;flex-direction:column;margin:0;padding:0;position:relative;min-height:0;min-width:0}.js-focus-visible :focus:not(.focus-visible){outline:0}</style><link href="/static/css/3.d37e3633.chunk.css" rel="stylesheet"></head><body><div id="root"></div><script src="/static/js/runtime~main.d358c073.js"></script><script src="/static/js/3.03227275.chunk.js"></script><script src="/static/js/main.a27b6a9d.chunk.js"></script></body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

View file

@ -0,0 +1,36 @@
/* Based on Sublime Text's Monokai theme */
.cm-s-monokai.CodeMirror { background: #272822; color: #f8f8f2; }
.cm-s-monokai div.CodeMirror-selected { background: #49483E; }
.cm-s-monokai .CodeMirror-line::selection, .cm-s-monokai .CodeMirror-line > span::selection, .cm-s-monokai .CodeMirror-line > span > span::selection { background: rgba(73, 72, 62, .99); }
.cm-s-monokai .CodeMirror-line::-moz-selection, .cm-s-monokai .CodeMirror-line > span::-moz-selection, .cm-s-monokai .CodeMirror-line > span > span::-moz-selection { background: rgba(73, 72, 62, .99); }
.cm-s-monokai .CodeMirror-gutters { background: #272822; border-right: 0px; }
.cm-s-monokai .CodeMirror-guttermarker { color: white; }
.cm-s-monokai .CodeMirror-guttermarker-subtle { color: #d0d0d0; }
.cm-s-monokai .CodeMirror-linenumber { color: #d0d0d0; }
.cm-s-monokai .CodeMirror-cursor { border-left: 1px solid #f8f8f0; }
.cm-s-monokai span.cm-comment { color: #75715e; }
.cm-s-monokai span.cm-atom { color: #ae81ff; }
.cm-s-monokai span.cm-number { color: #ae81ff; }
.cm-s-monokai span.cm-property, .cm-s-monokai span.cm-attribute { color: #a6e22e; }
.cm-s-monokai span.cm-keyword { color: #f92672; }
.cm-s-monokai span.cm-builtin { color: #66d9ef; }
.cm-s-monokai span.cm-string { color: #e6db74; }
.cm-s-monokai span.cm-variable { color: #f8f8f2; }
.cm-s-monokai span.cm-variable-2 { color: #9effff; }
.cm-s-monokai span.cm-variable-3 { color: #66d9ef; }
.cm-s-monokai span.cm-def { color: #fd971f; }
.cm-s-monokai span.cm-bracket { color: #f8f8f2; }
.cm-s-monokai span.cm-tag { color: #f92672; }
.cm-s-monokai span.cm-header { color: #ae81ff; }
.cm-s-monokai span.cm-link { color: #ae81ff; }
.cm-s-monokai span.cm-error { background: #f92672; color: #f8f8f0; }
.cm-s-monokai .CodeMirror-activeline-background { background: #373831; }
.cm-s-monokai .CodeMirror-matchingbracket {
text-decoration: underline;
color: white !important;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

View file

@ -0,0 +1,46 @@
self.__precacheManifest = [
{
"revision": "1fcb7caccc77d0f3c261",
"url": "/static/js/main.1fcb7cac.chunk.js"
},
{
"revision": "5b0e59ddd8f2dcde4b62",
"url": "/static/js/1.5b0e59dd.chunk.js"
},
{
"revision": "c0b1e834aabc0ce7ac71",
"url": "/static/js/2.c0b1e834.chunk.js"
},
{
"revision": "03227275d0a4e6e35efc",
"url": "/static/css/3.d37e3633.chunk.css"
},
{
"revision": "03227275d0a4e6e35efc",
"url": "/static/js/3.03227275.chunk.js"
},
{
"revision": "d358c073b1236447eec3",
"url": "/static/js/runtime~main.d358c073.js"
},
{
"revision": "b06a06edac78bd66f3a21911f6813c03",
"url": "/static/js/browser-server.50678a55.worker.js"
},
{
"revision": "1ae926741322a7cdb33553c7d00c1318",
"url": "/static/media/DateSelect.left.1ae92674.png"
},
{
"revision": "2a470dc002854e63925688e856f2a400",
"url": "/static/media/DateSelect.right.2a470dc0.png"
},
{
"revision": "ad9dce3bddd59033381d5755528c1809",
"url": "/static/media/bg.ad9dce3b.svg"
},
{
"revision": "9c2bc68d2c8e1f9f4672d87a61b119c9",
"url": "/index.html"
}
];

View file

@ -0,0 +1,46 @@
self.__precacheManifest = [
{
"revision": "61681a371def69c6ede8",
"url": "/static/js/main.61681a37.chunk.js"
},
{
"revision": "5b0e59ddd8f2dcde4b62",
"url": "/static/js/1.5b0e59dd.chunk.js"
},
{
"revision": "c0b1e834aabc0ce7ac71",
"url": "/static/js/2.c0b1e834.chunk.js"
},
{
"revision": "03227275d0a4e6e35efc",
"url": "/static/css/3.d37e3633.chunk.css"
},
{
"revision": "03227275d0a4e6e35efc",
"url": "/static/js/3.03227275.chunk.js"
},
{
"revision": "d358c073b1236447eec3",
"url": "/static/js/runtime~main.d358c073.js"
},
{
"revision": "3a105e2b7f58f5e27a500fe39f8fd164",
"url": "/static/js/browser-server.06b54be5.worker.js"
},
{
"revision": "1ae926741322a7cdb33553c7d00c1318",
"url": "/static/media/DateSelect.left.1ae92674.png"
},
{
"revision": "2a470dc002854e63925688e856f2a400",
"url": "/static/media/DateSelect.right.2a470dc0.png"
},
{
"revision": "ad9dce3bddd59033381d5755528c1809",
"url": "/static/media/bg.ad9dce3b.svg"
},
{
"revision": "d2912b97b214a1315357572ba41d025e",
"url": "/index.html"
}
];

View file

@ -0,0 +1,46 @@
self.__precacheManifest = [
{
"revision": "a27b6a9d6bd78e3f78fe",
"url": "/static/js/main.a27b6a9d.chunk.js"
},
{
"revision": "5b0e59ddd8f2dcde4b62",
"url": "/static/js/1.5b0e59dd.chunk.js"
},
{
"revision": "c0b1e834aabc0ce7ac71",
"url": "/static/js/2.c0b1e834.chunk.js"
},
{
"revision": "03227275d0a4e6e35efc",
"url": "/static/css/3.d37e3633.chunk.css"
},
{
"revision": "03227275d0a4e6e35efc",
"url": "/static/js/3.03227275.chunk.js"
},
{
"revision": "d358c073b1236447eec3",
"url": "/static/js/runtime~main.d358c073.js"
},
{
"revision": "3a105e2b7f58f5e27a500fe39f8fd164",
"url": "/static/js/browser-server.06b54be5.worker.js"
},
{
"revision": "1ae926741322a7cdb33553c7d00c1318",
"url": "/static/media/DateSelect.left.1ae92674.png"
},
{
"revision": "2a470dc002854e63925688e856f2a400",
"url": "/static/media/DateSelect.right.2a470dc0.png"
},
{
"revision": "ad9dce3bddd59033381d5755528c1809",
"url": "/static/media/bg.ad9dce3b.svg"
},
{
"revision": "9e85f42da39e9b59bad602f6fae652a2",
"url": "/index.html"
}
];

View file

@ -0,0 +1,27 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="512.000000pt" height="512.000000pt" viewBox="0 0 512.000000 512.000000"
preserveAspectRatio="xMidYMid meet">
<metadata>
Created by potrace 1.11, written by Peter Selinger 2001-2013
</metadata>
<g transform="translate(0.000000,512.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M2547 4964 c-1 -1 -47 -4 -102 -7 -582 -32 -1150 -289 -1571 -711
-101 -102 -228 -250 -261 -306 -9 -16 -21 -30 -25 -30 -4 0 -8 -4 -8 -9 0 -6
-13 -29 -29 -53 -88 -130 -186 -327 -247 -496 -36 -99 -98 -324 -108 -397 -4
-23 -8 -50 -10 -62 -3 -12 -8 -53 -11 -90 -4 -37 -9 -81 -11 -98 -6 -46 -5
-312 1 -385 16 -176 46 -332 100 -515 75 -253 226 -548 390 -761 91 -118 100
-128 220 -249 117 -116 123 -122 225 -200 36 -28 67 -52 70 -55 17 -19 196
-129 292 -179 352 -187 740 -282 1143 -281 165 1 255 8 410 35 90 16 247 51
275 62 8 4 17 7 20 8 3 1 25 8 50 15 25 7 47 14 50 15 3 1 19 7 35 14 17 6 55
22 85 33 92 36 292 142 386 203 365 238 655 555 849 930 118 226 211 501 240
710 3 17 7 40 9 52 3 12 7 50 11 85 3 35 8 81 10 103 6 48 5 280 0 350 -7 99
-22 207 -41 305 -25 128 -79 314 -114 395 -5 11 -25 58 -45 105 -38 90 -123
253 -154 298 -10 15 -36 54 -57 87 -119 184 -335 415 -524 560 -36 28 -67 52
-70 55 -16 17 -190 125 -275 171 -240 130 -561 237 -798 265 -29 4 -55 8 -58
10 -9 5 -347 23 -352 18z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -0,0 +1,34 @@
/**
* Welcome to your Workbox-powered service worker!
*
* You'll need to register this file in your web app and you should
* disable HTTP caching for this file too.
* See https://goo.gl/nhQhGp
*
* The rest of the code is auto-generated. Please don't update this file
* directly; instead, make changes to your Workbox build configuration
* and re-run your build process.
* See https://goo.gl/2aRDsh
*/
importScripts("https://storage.googleapis.com/workbox-cdn/releases/3.6.3/workbox-sw.js");
importScripts(
"/precache-manifest.dd195c6a2e68b333b66a2a33a8dce9a0.js"
);
workbox.clientsClaim();
/**
* The workboxSW.precacheAndRoute() method efficiently caches and responds to
* requests for URLs in the manifest.
* See https://goo.gl/S9QRab
*/
self.__precacheManifest = [].concat(self.__precacheManifest || []);
workbox.precaching.suppressWarnings();
workbox.precaching.precacheAndRoute(self.__precacheManifest, {});
workbox.routing.registerNavigationRoute("/index.html", {
blacklist: [/^\/_/,/\/[^/]+\.[^/]+$/],
});

View file

@ -0,0 +1,19 @@
{
"name": "",
"short_name": "",
"icons": [
{
"src": "/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
}

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,7 @@
@charset "UTF-8";:root{--reach-listbox:1}[data-reach-listbox-popover]{display:block;position:absolute;min-width:-moz-fit-content;min-width:-webkit-min-content;min-width:-moz-min-content;min-width:min-content;padding:.25rem 0;background:#fff;outline:none;border:1px solid rgba(0,0,0,.25)}[data-reach-listbox-popover]:focus-within{box-shadow:0 0 4px Highlight;outline:4px auto -webkit-focus-ring-color}[data-reach-listbox-popover][hidden]{display:none}[data-reach-listbox-list]{margin:0;padding:0;list-style:none}[data-reach-listbox-list]:focus{box-shadow:none;outline:none}[data-reach-listbox-option]{display:block;margin:0;padding:.25rem .5rem;white-space:nowrap;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}[data-reach-listbox-option][aria-selected=true]{background:#1672d4;color:#fff}[data-reach-listbox-option][data-current]{font-weight:bolder}[data-reach-listbox-option][data-current][data-confirming]{-webkit-animation:flash .1s;animation:flash .1s;-webkit-animation-iteration-count:1;animation-iteration-count:1}[data-reach-listbox-option][aria-disabled=true]{opacity:.5}[data-reach-listbox-button]{display:inline-flex;align-items:center;justify-content:space-between;padding:1px 10px 2px;border-color:#d8d8d8 #d1d1d1 #bababa;border-style:solid;border-width:1px;cursor:default;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}[data-reach-listbox-button][aria-disabled=true]{opacity:.5}[data-reach-listbox-arrow]{margin-left:.5rem;display:block;font-size:.5em}[data-reach-listbox-group-label]{display:block;margin:0;padding:.25rem .5rem;white-space:nowrap;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;font-weight:bolder}@-webkit-keyframes flash{0%{background:#1159a6;color:#fff;opacity:1}50%{opacity:.5;background:inherit;color:inherit}to{background:#1159a6;color:#fff;opacity:1}}@keyframes flash{0%{background:#1159a6;color:#fff;opacity:1}50%{opacity:.5;background:inherit;color:inherit}to{background:#1159a6;color:#fff;opacity:1}}
/*!
* Pikaday
* Copyright © 2014 David Bushell | BSD & MIT license | https://dbushell.com/
*/.pika-single{z-index:9999;display:block;position:relative;color:#333;background:#fff;border:1px solid;border-color:#ccc #ccc #bbb;font-family:Helvetica Neue,Helvetica,Arial,sans-serif}.pika-single:after,.pika-single:before{content:" ";display:table}.pika-single:after{clear:both}.pika-single.is-hidden{display:none}.pika-single.is-bound{position:absolute;box-shadow:0 5px 15px -5px rgba(0,0,0,.5)}.pika-lendar{float:left;width:240px;margin:8px}.pika-title{position:relative;text-align:center}.pika-label{display:inline-block;position:relative;z-index:9999;overflow:hidden;margin:0;padding:5px 3px;font-size:14px;line-height:20px;font-weight:700;background-color:#fff}.pika-title select{cursor:pointer;position:absolute;z-index:9998;margin:0;left:0;top:5px;opacity:0}.pika-next,.pika-prev{display:block;cursor:pointer;position:relative;outline:none;border:0;padding:0;width:20px;height:30px;text-indent:20px;white-space:nowrap;overflow:hidden;background-color:transparent;background-position:50%;background-repeat:no-repeat;background-size:75% 75%;opacity:.5}.pika-next:hover,.pika-prev:hover{opacity:1}.is-rtl .pika-next,.pika-prev{float:left;background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAUklEQVR42u3VMQoAIBADQf8Pgj+OD9hG2CtONJB2ymQkKe0HbwAP0xucDiQWARITIDEBEnMgMQ8S8+AqBIl6kKgHiXqQqAeJepBo/z38J/U0uAHlaBkBl9I4GwAAAABJRU5ErkJggg==")}.is-rtl .pika-prev,.pika-next{float:right;background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAU0lEQVR42u3VOwoAMAgE0dwfAnNjU26bYkBCFGwfiL9VVWoO+BJ4Gf3gtsEKKoFBNTCoCAYVwaAiGNQGMUHMkjGbgjk2mIONuXo0nC8XnCf1JXgArVIZAQh5TKYAAAAASUVORK5CYII=")}.pika-next.is-disabled,.pika-prev.is-disabled{cursor:default;opacity:.2}.pika-select{display:inline-block}.pika-table{width:100%;border-collapse:collapse;border-spacing:0;border:0}.pika-table td,.pika-table th{width:14.285714285714286%;padding:0}.pika-table th{color:#999;font-size:12px;line-height:25px;font-weight:700;text-align:center}.pika-button{cursor:pointer;display:block;box-sizing:border-box;-moz-box-sizing:border-box;outline:none;border:0;margin:0;width:100%;padding:5px;color:#666;font-size:12px;line-height:15px;text-align:right;background:#f5f5f5;height:auto}.pika-week{font-size:11px;color:#999}.is-today .pika-button{color:#3af;font-weight:700}.has-event .pika-button,.is-selected .pika-button{color:#fff;font-weight:700;background:#3af;box-shadow:inset 0 1px 3px #178fe5;border-radius:3px}.has-event .pika-button{background:#005da9;box-shadow:inset 0 1px 3px #0076c9}.is-disabled .pika-button,.is-inrange .pika-button{background:#d5e9f7}.is-startrange .pika-button{color:#fff;background:#6cb31d;box-shadow:none;border-radius:3px}.is-endrange .pika-button{color:#fff;background:#3af;box-shadow:none;border-radius:3px}.is-disabled .pika-button{pointer-events:none;cursor:default;color:#999;opacity:.3}.is-outside-current-month .pika-button{color:#999;opacity:.3}.is-selection-disabled{pointer-events:none;cursor:default}.pika-button:hover,.pika-row.pick-whole-week:hover .pika-button{color:#fff;background:#ff8000;box-shadow:none;border-radius:3px}.pika-table abbr{border-bottom:none;cursor:help}
/*# sourceMappingURL=3.d37e3633.chunk.css.map */

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show more