├── sample-data-old
├── tasktxt.txt
├── _BHxrxC3nSY2cEBjWlaEFW.metadata
├── bitcoin-price.txt
├── math-pack.txt
├── hikes.txt
├── crm.txt
├── time-tracking.txt
├── todos.txt
├── bitcoin-price.metadata
├── all-ingredients.metadata
├── stonks-portfolio.txt
├── splitwise.txt
├── goodreads.1.highlighter
├── goodreads.3.highlighter
├── hikes.2.highlighter
├── movie-log.2.highlighter
├── crm.2.highlighter
├── crm.1.highlighter
├── movie-log.3.highlighter
├── splitwise.3.highlighter
├── trip-plan.city.highlighter
├── _hCDH5F2LuEr2CrRiS42Gv.highlighter
├── goodreads.2.highlighter
├── pizza-dough.txt
├── bar.price.highlighter
├── splitwise.2.highlighter
├── splitwise.4.highlighter
├── blocks.block.highlighter
├── tasktxt.2.highlighter
├── trip-plan.day.highlighter
├── common.date.highlighter
├── time-tracking.time.highlighter
├── movie-log.1.highlighter
├── workshop.txt
├── math-pack.operator.highlighter
├── aeropress.txt
├── movie-log.txt
├── goodreads.txt
├── recipe-pack.txt
├── _1F0eRt4v6Onwr8fDfaXQU.highlighter
├── crm.metadata
├── common.emoji.highlighter
├── hikes.1.highlighter
├── hikes.metadata
├── todos.metadata
├── welcome.metadata
├── common.number.highlighter
├── plant-watering.txt
├── stonks-portfolio.metadata
├── youtube.highlighter
├── bar.txt
├── bar.total.highlighter
├── trip-plan.time.highlighter
├── default-searches.txt
├── _qvqxfvCRfMf4jMbieqxcv.highlighter
├── common.duration.highlighter
├── trip-plan.timeSpan.highlighter
├── all-ingredients.names.highlighter
├── workshop.time.highlighter
├── trip-plan.txt
├── stonks-portfolio.1.highlighter
├── tasktxt.1.highlighter
├── todos.1.highlighter
├── food.scale.highlighter
├── _BHxrxC3nSY2cEBjWlaEFW.txt
├── bitcoin-price.1.highlighter
├── goodreads.metadata
├── pizza.metadata
├── todos.2.highlighter
├── workout.metadata
├── pizza-dough.inputs.highlighter
├── plant-watering.metadata
├── workshop.metadata
├── math-pack.metadata
├── _AJ5yvielohvnk3xvY1ea5.txt
├── pizza-dough.metadata
├── ice-cream.metadata
├── recipe-pack.metadata
├── time-tracking.open-time.highlighter
├── stonks-portfolio.2.highlighter
├── tasktxt.metadata
├── movie-log.metadata
├── splitwise.metadata
├── tasktxt.3.highlighter
├── workout.txt
├── trip-plan.activity.highlighter
├── blocks.example-ingredient.highlighter
├── plant-watering.1.highlighter
├── splitwise.1.highlighter
├── food.quantity.highlighter
├── trip-plan.openingHours.highlighter
├── blocks.collapse.highlighter
├── ice-cream.txt
├── time-tracking.metadata
├── markdown.highlighter
├── gochujang-pork.metadata
├── time-tracking.project.highlighter
├── trip-plan.openingRange.highlighter
├── _EPSPtlS2leXEhRmSEoQZh.highlighter
├── blocks.shuffle.highlighter
├── bar.metadata
├── plant-watering.2.highlighter
├── aeropress.metadata
├── blocks.sort.highlighter
├── workout.exercise.highlighter
├── blocks.bla.highlighter
├── blocks.remove.highlighter
├── pizza-dough.water.highlighter
├── default-searches.metadata
├── blocks.txt
├── pizza-dough.number-of-balls.highlighter
├── _AJ5yvielohvnk3xvY1ea5.metadata
├── _oHItjl0ecKP4fV4fh15L6.metadata
├── food.ingredient.highlighter
├── bar.sales.highlighter
├── workout.workout.highlighter
├── math-pack.formula.highlighter
├── blocks.metadata
├── _iiy0UQcJ4SleIwm7XOkYg.txt
├── blocks.sorting-hat.highlighter
├── _oHItjl0ecKP4fV4fh15L6.txt
├── tasktxt.4.highlighter
├── trip-plan.metadata
├── workshop.duration.highlighter
├── _iiy0UQcJ4SleIwm7XOkYg.metadata
├── pizza.txt
└── gochujang-pork.txt
├── src
├── vite-env.d.ts
├── assets
│ ├── plus-icon.svg
│ ├── chevron-forward.svg
│ ├── slider-handle.svg
│ ├── stop-icon.svg
│ ├── pause-icon.svg
│ ├── play-icon.svg
│ ├── slider-track.svg
│ ├── asterisk-icon.svg
│ ├── whiteout.svg
│ └── splat-underline-2-default.svg
├── refactor
│ ├── cleanup-highlighters.mjs
│ ├── utils.mjs
│ └── watcher.mjs
├── embedded-main.tsx
├── main.tsx
├── data
│ ├── officialFoods.ts
│ └── measure_unit.csv
├── EmbeddedDocument.tsx
├── NumberSliderComponent.tsx
├── markdown.ts
├── index.css
├── utils.ts
├── HighlightHoverCard.tsx
├── SheetCalendar.tsx
├── compute.ts
└── highlight.ts
├── documents
├── video.mp4
├── todos.json
├── stonks.json
├── plant-tracker.js
└── coffee.json
├── postcss.config.js
├── sample-data
├── hikes.txt
├── stonks-portfolio.txt
├── todos.txt
├── bar.txt
├── time-tracking.txt
├── workshop.txt
├── pizza-dough.txt
├── all-ingredients.metadata
├── math-pack.txt
├── splitwise.3.highlighter
├── splitwise.txt
├── _hCDH5F2LuEr2CrRiS42Gv.highlighter
├── bar.price.highlighter
├── splitwise.2.highlighter
├── splitwise.4.highlighter
├── common.date.highlighter
├── math-pack.operator.highlighter
├── aeropress.txt
├── recipe-pack.txt
├── _1F0eRt4v6Onwr8fDfaXQU.highlighter
├── common.emoji.highlighter
├── hikes.1.highlighter
├── hikes.metadata
├── common.number.highlighter
├── plant-watering.txt
├── hikes.2.highlighter
├── _obENYhG63Nd13x43qDRr0.highlighter
├── youtube.highlighter
├── _wqlqE65AnWhAK9dX8pm14.highlighter
├── _qvqxfvCRfMf4jMbieqxcv.highlighter
├── common.duration.highlighter
├── all-ingredients.names.highlighter
├── time-tracking.time.highlighter
├── workshop.time.highlighter
├── todos.1.highlighter
├── default-searches.txt
├── todos.metadata
├── food.scale.highlighter
├── todos.2.highlighter
├── pizza-dough.inputs.highlighter
├── math-pack.metadata
├── plant-watering.metadata
├── stonks-portfolio.metadata
├── bar.total.highlighter
├── pizza-dough.metadata
├── recipe-pack.metadata
├── time-tracking.open-time.highlighter
├── _nzNLA8UWxTsnQ01510U1e.highlighter
├── stonks-portfolio.1.highlighter
├── splitwise.metadata
├── workshop.metadata
├── welcome.metadata
├── workout.metadata
├── plant-watering.1.highlighter
├── splitwise.1.highlighter
├── food.quantity.highlighter
├── bar.sales.highlighter
├── _p8Ot2ukI8aaITNa5bY5JU.highlighter
├── workout.txt
├── markdown.highlighter
├── gochujang-pork.metadata
├── time-tracking.project.highlighter
├── _EPSPtlS2leXEhRmSEoQZh.highlighter
├── bar.metadata
├── aeropress.metadata
├── workout.exercise.highlighter
├── plant-watering.2.highlighter
├── default-searches.metadata
├── stonks-portfolio.2.highlighter
├── pizza-dough.water.highlighter
├── pizza-dough.number-of-balls.highlighter
├── meeting.metadata
├── food.ingredient.highlighter
├── time-tracking.metadata
├── workout.workout.highlighter
├── math-pack.formula.highlighter
├── chilli.txt
├── meeting.txt
├── chilli.metadata
├── workshop.duration.highlighter
└── gochujang-pork.txt
├── tsconfig.node.json
├── tailwind.config.js
├── .gitignore
├── embedded.html
├── tsconfig.json
├── index.html
├── vite.config.ts
├── README.md
├── embedded-test.html
└── package.json
/sample-data-old/tasktxt.txt:
--------------------------------------------------------------------------------
1 | tasktxt
2 | [ ] one task 15s
--------------------------------------------------------------------------------
/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/sample-data-old/_BHxrxC3nSY2cEBjWlaEFW.metadata:
--------------------------------------------------------------------------------
1 | {
2 | "sheets": []
3 | }
--------------------------------------------------------------------------------
/sample-data-old/bitcoin-price.txt:
--------------------------------------------------------------------------------
1 | bitcoin price tracker
2 | $EUR
3 | $USD
4 | $GBP
5 | $CAD
--------------------------------------------------------------------------------
/documents/video.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/inkandswitch/potluck/HEAD/documents/video.mp4
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/sample-data/hikes.txt:
--------------------------------------------------------------------------------
1 | Hikes
2 | 🌲 Big Pine Creek 14 miles
3 | 🌲 Sea to Summit 7 miles
4 | 🌲 Redwood Regional Park 8 miles
5 |
--------------------------------------------------------------------------------
/sample-data/stonks-portfolio.txt:
--------------------------------------------------------------------------------
1 | Stock portfolio
2 | # Stocks
3 |
4 | $GME 250@$5.00
5 | $AMC 100@$30.00
6 | $GOOGL 100@$20
7 |
8 | Portfolio value:
--------------------------------------------------------------------------------
/sample-data/todos.txt:
--------------------------------------------------------------------------------
1 | Todos
2 | # Todos
3 |
4 | [ ] Read work email
5 | [x] Finish wordle
6 | [ ] Do laundry
7 | [ ] Pick up order from bookstore
8 |
--------------------------------------------------------------------------------
/sample-data-old/math-pack.txt:
--------------------------------------------------------------------------------
1 | math pack
2 |
3 | Evaluates math expressions in your document
4 |
5 | 1 + 2
6 |
7 | 3000 $ / 4 people
8 |
9 | (100 / 50) + 5 * 2
--------------------------------------------------------------------------------
/sample-data/bar.txt:
--------------------------------------------------------------------------------
1 | Cash register
2 | ## Prices
3 | cake 🍰 = $ 4
4 | coffee ☕ = $ 2
5 | cupcake 🧁 = $ 2
6 |
7 | ## Sales
8 | 🍰
9 | 🧁☕️
10 |
11 | Total:
--------------------------------------------------------------------------------
/sample-data-old/hikes.txt:
--------------------------------------------------------------------------------
1 | hikes
2 | using emoji as semantic marker is fun
3 |
4 | 🌲 Big Pine Creek 14miles
5 | 🌲 Sea to Summit 7 miles
6 | 🌲 Redwood Regional Park 8miles
7 |
--------------------------------------------------------------------------------
/sample-data/time-tracking.txt:
--------------------------------------------------------------------------------
1 | Timekeeping
2 | # Website relaunch
3 |
4 | 9/10
5 | 14:10 - 15:00 work on logo
6 | 15:00 - 17:00 redesign feedback form
7 |
8 | Total:
9 |
10 |
--------------------------------------------------------------------------------
/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "module": "esnext",
5 | "moduleResolution": "node"
6 | },
7 | "include": ["vite.config.ts"]
8 | }
9 |
--------------------------------------------------------------------------------
/sample-data/workshop.txt:
--------------------------------------------------------------------------------
1 | Workshop
2 | # Kickoff meeting
3 |
4 | Start 19:00
5 |
6 | 30 minutes Introduction
7 | 30 minutes Discuss goals
8 | 60 minutes Breakup into groups
9 | 30 minutes Wrap up
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | content: [
3 | "./index.html",
4 | "./src/**/*.{js,ts,jsx,tsx}",
5 | ],
6 | theme: {
7 | extend: {},
8 | },
9 | plugins: [],
10 | }
--------------------------------------------------------------------------------
/sample-data-old/crm.txt:
--------------------------------------------------------------------------------
1 | CRM
2 | using double newline as
3 |
4 | sonnentag
5 | he owes me money
6 | (123) 456-7890
7 |
8 | max
9 | doesn't owe me money
10 |
11 | geoffrey
12 | (123) 456-7890
13 |
--------------------------------------------------------------------------------
/sample-data-old/time-tracking.txt:
--------------------------------------------------------------------------------
1 | time tracking
2 | # Website relaunch
3 |
4 | 18:55 - 18:56
5 | 17:19 - 17:20
6 | 17:00 - 17:17
7 |
8 |
9 | # Bottle app
10 | 17:19 - 17:20
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/sample-data-old/todos.txt:
--------------------------------------------------------------------------------
1 | natto todos
2 | simple todo list with conditional formatting
3 |
4 | [ ] make a gallery view
5 | [ ] announce multiplayer 08/15/22
6 | [ ] add keyboard shortcuts
7 | [x] add zoom ability
--------------------------------------------------------------------------------
/sample-data/pizza-dough.txt:
--------------------------------------------------------------------------------
1 | Ooni pizza dough
2 | number of dough balls: 10
3 | ball weight: 40
4 | water %: 50%
5 | salt: 3%
6 | oil: 1%
7 | proof hours: 3
8 | room temp: 75 F
9 |
10 | Flour:
11 | Water:
--------------------------------------------------------------------------------
/sample-data-old/bitcoin-price.metadata:
--------------------------------------------------------------------------------
1 | {
2 | "sheets": [
3 | {
4 | "id": "_AbeJEC5Ryyyjb5IDRccEN",
5 | "configId": "bitcoin-price.1",
6 | "hideHighlightsInDocument": false
7 | }
8 | ]
9 | }
--------------------------------------------------------------------------------
/sample-data/all-ingredients.metadata:
--------------------------------------------------------------------------------
1 | {
2 | "sheets": [
3 | {
4 | "id": "Jr0j5bubFWrDIxolnsfdq",
5 | "configId": "all-ingredients.names",
6 | "hideHighlightsInDocument": false
7 | }
8 | ]
9 | }
--------------------------------------------------------------------------------
/sample-data-old/all-ingredients.metadata:
--------------------------------------------------------------------------------
1 | {
2 | "sheets": [
3 | {
4 | "id": "Jr0j5bubFWrDIxolnsfdq",
5 | "configId": "all-ingredients.names",
6 | "hideHighlightsInDocument": false
7 | }
8 | ]
9 | }
--------------------------------------------------------------------------------
/sample-data-old/stonks-portfolio.txt:
--------------------------------------------------------------------------------
1 | stonks portfolio
2 | would like to import outside data
3 | doing row-wise computation is pretty straightforward in potluck
4 |
5 | $GME 250@$5.00
6 | $AMC 100@$1.00
7 |
8 | portfolio value
--------------------------------------------------------------------------------
/sample-data-old/splitwise.txt:
--------------------------------------------------------------------------------
1 | splitwise
2 | 6/1/22
3 | scarlett $50
4 | sean $20
5 | paul $10
6 |
7 | 6/2/22
8 | sean $50
9 | sean $30
10 | paul $20
11 |
12 | total [scarlett]
13 | total [sean]
14 | total [paul]
15 |
16 |
17 |
--------------------------------------------------------------------------------
/sample-data/math-pack.txt:
--------------------------------------------------------------------------------
1 | Math pack
2 |
3 | Evaluates math expressions in your document
4 |
5 | 1 + 2
6 |
7 | 3000 $ / 4 people
8 |
9 | (100 / 50) + 5 * 2
10 |
11 |
12 | I have 10 guests
13 |
14 |
15 | vegetarian guests =
--------------------------------------------------------------------------------
/sample-data-old/goodreads.1.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "goodreads.1",
3 | "name": "tags",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "#{/\\\\S+/}",
8 | "visibility": "HIDDEN"
9 | }
10 | ]
11 | }
--------------------------------------------------------------------------------
/sample-data-old/goodreads.3.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "goodreads.3",
3 | "name": "#inbox",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "#inbox",
8 | "visibility": "HIDDEN"
9 | }
10 | ]
11 | }
--------------------------------------------------------------------------------
/sample-data-old/hikes.2.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "hikes.2",
3 | "name": "distance",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "{number}miles",
8 | "visibility": "HIDDEN"
9 | }
10 | ]
11 | }
--------------------------------------------------------------------------------
/sample-data-old/movie-log.2.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "movie-log.2",
3 | "name": "rating",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "{/⭐+/}",
8 | "visibility": "HIDDEN"
9 | }
10 | ]
11 | }
--------------------------------------------------------------------------------
/sample-data-old/crm.2.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "crm.2",
3 | "name": "person",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "{/\\\\n\\\\n/}{/.*/:name}",
8 | "visibility": "HIDDEN"
9 | }
10 | ]
11 | }
--------------------------------------------------------------------------------
/sample-data/splitwise.3.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "splitwise.3",
3 | "name": "money",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "${number:amount}",
8 | "visibility": "HIDDEN"
9 | }
10 | ]
11 | }
--------------------------------------------------------------------------------
/sample-data-old/crm.1.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "crm.1",
3 | "name": "phone",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "({number}) {number}-{number}",
8 | "visibility": "HIDDEN"
9 | }
10 | ]
11 | }
--------------------------------------------------------------------------------
/sample-data-old/movie-log.3.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "movie-log.3",
3 | "name": "tmdb",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "tmdb{/\\\\d+/:id}",
8 | "visibility": "HIDDEN"
9 | }
10 | ]
11 | }
--------------------------------------------------------------------------------
/sample-data-old/splitwise.3.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "splitwise.3",
3 | "name": "money",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "${number:amount}",
8 | "visibility": "HIDDEN"
9 | }
10 | ]
11 | }
--------------------------------------------------------------------------------
/sample-data-old/trip-plan.city.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "trip-plan.city",
3 | "name": "city",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "{markdown.h1}",
8 | "visibility": "HIDDEN"
9 | }
10 | ]
11 | }
--------------------------------------------------------------------------------
/sample-data/splitwise.txt:
--------------------------------------------------------------------------------
1 | Split expenses
2 | 6/1/22
3 | scarlett $50 for tickets
4 | sean $20
5 | paul $10
6 |
7 | 6/2/22
8 | sean $50
9 | sean $30
10 | paul $20
11 |
12 | total [scarlett]
13 | total [sean]
14 | total [paul]
15 |
16 |
17 |
--------------------------------------------------------------------------------
/sample-data-old/_hCDH5F2LuEr2CrRiS42Gv.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "_hCDH5F2LuEr2CrRiS42Gv",
3 | "name": "time",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "time",
8 | "visibility": "HIDDEN"
9 | }
10 | ]
11 | }
--------------------------------------------------------------------------------
/sample-data-old/goodreads.2.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "goodreads.2",
3 | "name": "#read",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "#read-{number:rating}",
8 | "visibility": "HIDDEN"
9 | }
10 | ]
11 | }
--------------------------------------------------------------------------------
/sample-data-old/pizza-dough.txt:
--------------------------------------------------------------------------------
1 | ooni pizza dough
2 | number of dough balls: 10
3 | ball weight: 40
4 | water %: 50%
5 | salt: 3%
6 | oil: 1%
7 | proof hours: 3
8 | room temp: 75 F
9 |
10 | ---
11 |
12 | Flour:
13 | Water:
14 | Salt:
15 | Yeast:
16 | Oil
--------------------------------------------------------------------------------
/sample-data/_hCDH5F2LuEr2CrRiS42Gv.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "_hCDH5F2LuEr2CrRiS42Gv",
3 | "name": "time",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "time",
8 | "visibility": "HIDDEN"
9 | }
10 | ]
11 | }
--------------------------------------------------------------------------------
/sample-data/bar.price.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "bar.price",
3 | "name": "price",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "{emoji:item} = $ {number:price} ",
8 | "visibility": "HIDDEN"
9 | }
10 | ]
11 | }
--------------------------------------------------------------------------------
/sample-data/splitwise.2.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "splitwise.2",
3 | "name": "userMoney",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "{name:name} {money:money}",
8 | "visibility": "HIDDEN"
9 | }
10 | ]
11 | }
--------------------------------------------------------------------------------
/sample-data/splitwise.4.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "splitwise.4",
3 | "name": "name",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "{/scarlett|yiwen|paul|sean/}",
8 | "visibility": "HIDDEN"
9 | }
10 | ]
11 | }
--------------------------------------------------------------------------------
/src/assets/plus-icon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/sample-data-old/bar.price.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "bar.price",
3 | "name": "price",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "{emoji:item} = {number:price} Euro",
8 | "visibility": "HIDDEN"
9 | }
10 | ]
11 | }
--------------------------------------------------------------------------------
/sample-data-old/splitwise.2.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "splitwise.2",
3 | "name": "userMoney",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "{name:name} {money:money}",
8 | "visibility": "HIDDEN"
9 | }
10 | ]
11 | }
--------------------------------------------------------------------------------
/sample-data-old/splitwise.4.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "splitwise.4",
3 | "name": "name",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "{/scarlett|yiwen|paul|sean/}",
8 | "visibility": "HIDDEN"
9 | }
10 | ]
11 | }
--------------------------------------------------------------------------------
/sample-data-old/blocks.block.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "blocks.block",
3 | "name": "block",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "{/\\\\[(\\\\n|[^\\\\]])+\\\\]/}",
8 | "visibility": "HIDDEN"
9 | }
10 | ]
11 | }
--------------------------------------------------------------------------------
/sample-data-old/tasktxt.2.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "tasktxt.2",
3 | "name": "startTime",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "[{number:h}:{number:m}:{number:s}]",
8 | "visibility": "HIDDEN"
9 | }
10 | ]
11 | }
--------------------------------------------------------------------------------
/sample-data-old/trip-plan.day.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "trip-plan.day",
3 | "name": "day",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "{/Mon|Tue|Wed|Thu|Fri|Sat|Sun/}",
8 | "visibility": "HIDDEN"
9 | }
10 | ]
11 | }
--------------------------------------------------------------------------------
/sample-data/common.date.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "common.date",
3 | "name": "dates",
4 | "properties": [
5 | {
6 | "name": "date",
7 | "formula": "{number:month}/{number:day}/{number:year}",
8 | "visibility": "HIDDEN"
9 | }
10 | ]
11 | }
--------------------------------------------------------------------------------
/sample-data-old/common.date.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "common.date",
3 | "name": "dates",
4 | "properties": [
5 | {
6 | "name": "date",
7 | "formula": "{number:month}/{number:day}/{number:year}",
8 | "visibility": "HIDDEN"
9 | }
10 | ]
11 | }
--------------------------------------------------------------------------------
/sample-data-old/time-tracking.time.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "time-tracking.time",
3 | "name": "time",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "{number:hours}:{number:minutes}",
8 | "visibility": "HIDDEN"
9 | }
10 | ]
11 | }
--------------------------------------------------------------------------------
/src/assets/chevron-forward.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/sample-data-old/movie-log.1.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "movie-log.1",
3 | "name": "{dates} {tmdb} {rating} {/.*/}",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "{dates} {tmdb} {rating} {/.*/}",
8 | "visibility": "HIDDEN"
9 | }
10 | ]
11 | }
--------------------------------------------------------------------------------
/sample-data-old/workshop.txt:
--------------------------------------------------------------------------------
1 | Plan schedule of workshop
2 |
3 | make it easy to come up with a schedule and rearrange items, insert new ones
4 |
5 | Start 19:00
6 |
7 | 30 minutes Introduction
8 | 30 minutes Discuss goals
9 | 60 minutes Breakup into groups
10 | 30 minutes Wrap up
--------------------------------------------------------------------------------
/sample-data/math-pack.operator.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "math-pack.operator",
3 | "name": "operator",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "{/\\\\/|\\\\+|\\\\*|\\\\-|\\\\(|\\\\)/}",
8 | "visibility": "HIDDEN"
9 | }
10 | ]
11 | }
--------------------------------------------------------------------------------
/sample-data-old/math-pack.operator.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "math-pack.operator",
3 | "name": "operator",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "{/\\\\/|\\\\+|\\\\*|\\\\-|\\\\(|\\\\)/}",
8 | "visibility": "HIDDEN"
9 | }
10 | ]
11 | }
--------------------------------------------------------------------------------
/sample-data/aeropress.txt:
--------------------------------------------------------------------------------
1 | James Hoffmann Aeropress
2 | ## Recipe
3 | Grind 11 g coffee, medium-fine.
4 | Add 200 g water, brew 2 minutes, plunge!
5 |
6 | scale by
7 |
8 | ## Notes
9 | 6/22/22: Pretty good, but forgot to swirl.
10 | 6/23/22: Felt weak and under-extracted. Grind finer?
--------------------------------------------------------------------------------
/sample-data-old/aeropress.txt:
--------------------------------------------------------------------------------
1 | ☕️ James Hoffmann Aeropress
2 | ## Recipe
3 | Grind 11 g coffee, medium-fine.
4 | Add 200 g water, brew 2 minutes, plunge!
5 |
6 | scale by
7 |
8 | ## Notes
9 | 6/22/22: Pretty good, but forgot to swirl.
10 | 6/23/22: Felt weak and under-extracted. Grind finer?
--------------------------------------------------------------------------------
/sample-data-old/movie-log.txt:
--------------------------------------------------------------------------------
1 | movie log
2 | fun to use num star emoji as rating although inefficient to input
3 |
4 | 06/12/22 tmdb453395 ⭐⭐ Doctor Strange in the Multiverse of Madness
5 | this movie was better than people said
6 | 06/01/22 tmdb453395 ⭐⭐⭐⭐ Top Gun
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/sample-data-old/goodreads.txt:
--------------------------------------------------------------------------------
1 | goodreads
2 | extracting tags and having rating inside tag
3 |
4 | Seeing like a State #inbox
5 | Whole Earth #read-5 8/1/22
6 | - many lives of Stewart Brand
7 | The Whole Earth Catalog #read-3
8 | A Gentleman in Moscow #read-4
9 | - really enjoyed this book by Amor Towles
--------------------------------------------------------------------------------
/sample-data/recipe-pack.txt:
--------------------------------------------------------------------------------
1 | Recipe pack
2 | This pack of searches gives you the following features!
3 |
4 | Durations are turned into timers
5 | 1 minute
6 |
7 | Quantities are recognized
8 | 15 g of flour and 2 cups of water
9 |
10 | which can be scaled by when you use the text "scale by"
11 | scale by
--------------------------------------------------------------------------------
/sample-data-old/recipe-pack.txt:
--------------------------------------------------------------------------------
1 | recipe pack
2 | This pack of searches gives you the following features!
3 |
4 | Durations are turned into timers
5 | 1 minute
6 |
7 | Quantities are recognized
8 | 15 g of flour and 2 cups of water
9 |
10 | which can be scaled by when you use the text "scale by"
11 | scale by
--------------------------------------------------------------------------------
/src/assets/slider-handle.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/sample-data/_1F0eRt4v6Onwr8fDfaXQU.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "_1F0eRt4v6Onwr8fDfaXQU",
3 | "name": "highlightedTeammate",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "Put your own name here to highlight yours specifically: {teammate:teammate}",
8 | "visibility": "HIDDEN"
9 | }
10 | ]
11 | }
--------------------------------------------------------------------------------
/sample-data-old/_1F0eRt4v6Onwr8fDfaXQU.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "_1F0eRt4v6Onwr8fDfaXQU",
3 | "name": "highlightedTeammate",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "Put your own name here to highlight yours specifically: {teammate:teammate}",
8 | "visibility": "HIDDEN"
9 | }
10 | ]
11 | }
--------------------------------------------------------------------------------
/sample-data-old/crm.metadata:
--------------------------------------------------------------------------------
1 | {
2 | "sheets": [
3 | {
4 | "id": "_hKJvOA9WQ0xR3GLVX1ZDr",
5 | "configId": "crm.1",
6 | "hideHighlightsInDocument": false
7 | },
8 | {
9 | "id": "_ST62pNjoINl4A0k8zGaBh",
10 | "configId": "crm.2",
11 | "hideHighlightsInDocument": false
12 | }
13 | ]
14 | }
--------------------------------------------------------------------------------
/sample-data/common.emoji.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "common.emoji",
3 | "name": "emoji",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "{/(\\u00a9|\\u00ae|[\\u2000-\\u3300]|\\ud83c[\\ud000-\\udfff]|\\ud83d[\\ud000-\\udfff]|\\ud83e[\\ud000-\\udfff])/}",
8 | "visibility": "HIDDEN"
9 | }
10 | ]
11 | }
--------------------------------------------------------------------------------
/sample-data/hikes.1.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "hikes.1",
3 | "name": "hikes",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "🌲",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "col2",
12 | "formula": "TextAfter($)",
13 | "visibility": "HIDDEN"
14 | }
15 | ]
16 | }
--------------------------------------------------------------------------------
/sample-data/hikes.metadata:
--------------------------------------------------------------------------------
1 | {
2 | "sheets": [
3 | {
4 | "id": "_iePFJrk4l7HU0IS6H18Yb",
5 | "configId": "hikes.1",
6 | "hideHighlightsInDocument": false
7 | },
8 | {
9 | "id": "_RLKSxiJoTh8r5YvnWUzpE",
10 | "configId": "hikes.2",
11 | "hideHighlightsInDocument": false
12 | }
13 | ]
14 | }
--------------------------------------------------------------------------------
/sample-data-old/common.emoji.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "common.emoji",
3 | "name": "emoji",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "{/(\\u00a9|\\u00ae|[\\u2000-\\u3300]|\\ud83c[\\ud000-\\udfff]|\\ud83d[\\ud000-\\udfff]|\\ud83e[\\ud000-\\udfff])/}",
8 | "visibility": "HIDDEN"
9 | }
10 | ]
11 | }
--------------------------------------------------------------------------------
/sample-data-old/hikes.1.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "hikes.1",
3 | "name": "hikes",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "🌲",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "col2",
12 | "formula": "TextAfter($)",
13 | "visibility": "HIDDEN"
14 | }
15 | ]
16 | }
--------------------------------------------------------------------------------
/sample-data-old/hikes.metadata:
--------------------------------------------------------------------------------
1 | {
2 | "sheets": [
3 | {
4 | "id": "_iePFJrk4l7HU0IS6H18Yb",
5 | "configId": "hikes.1",
6 | "hideHighlightsInDocument": false
7 | },
8 | {
9 | "id": "_RLKSxiJoTh8r5YvnWUzpE",
10 | "configId": "hikes.2",
11 | "hideHighlightsInDocument": false
12 | }
13 | ]
14 | }
--------------------------------------------------------------------------------
/sample-data-old/todos.metadata:
--------------------------------------------------------------------------------
1 | {
2 | "sheets": [
3 | {
4 | "id": "_Do0kWwRB2norSG0x4paId",
5 | "configId": "todos.1",
6 | "hideHighlightsInDocument": false
7 | },
8 | {
9 | "id": "_LyD5a1lA4LFoMpVDuEz2g",
10 | "configId": "todos.2",
11 | "hideHighlightsInDocument": false
12 | }
13 | ]
14 | }
--------------------------------------------------------------------------------
/sample-data-old/welcome.metadata:
--------------------------------------------------------------------------------
1 | {
2 | "sheets": [
3 | {
4 | "id": "_sDm7E0wtxnm19VrCbnfFh",
5 | "configId": "youtube",
6 | "hideHighlightsInDocument": false
7 | },
8 | {
9 | "id": "_ww9wvgHRl83zAO8h9CtoG",
10 | "configId": "markdown",
11 | "hideHighlightsInDocument": true
12 | }
13 | ]
14 | }
--------------------------------------------------------------------------------
/sample-data/common.number.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "common.number",
3 | "name": "number",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "{/[0-9][0-9\\.]*/}",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "value",
12 | "formula": "ParseFloat($)",
13 | "visibility": "HIDDEN"
14 | }
15 | ]
16 | }
--------------------------------------------------------------------------------
/src/assets/stop-icon.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/sample-data-old/common.number.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "common.number",
3 | "name": "number",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "{/[0-9][0-9\\.]*/}",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "value",
12 | "formula": "ParseFloat($)",
13 | "visibility": "HIDDEN"
14 | }
15 | ]
16 | }
--------------------------------------------------------------------------------
/sample-data-old/plant-watering.txt:
--------------------------------------------------------------------------------
1 | Plant Watering Tracker
2 | ## Plants
3 |
4 | 🌱 Fiddle leaf: every 6 days, last watered on 8/19/2022
5 | 🌱 Montsera: every 4 days, last watered on 8/21/2022
6 | 🌱 Yuzu tree: every 5 days, last watered on 8/17/2022
7 | - The Yuzu tree needs some holes in the pot so that water can drain.
8 | 🌱 Pine Bonsai: every 4 days, last watered on 8/19/2022
9 |
--------------------------------------------------------------------------------
/sample-data/plant-watering.txt:
--------------------------------------------------------------------------------
1 | Plant Tracker
2 | 🌱 Fiddle leaf (every 6 days)
3 | - last watered on 8/19/2022
4 | 🌱 Montsera (every 4 days)
5 | - last watered on 8/21/2022
6 | 🌱 Yuzu tree (every 5 days)
7 | - last watered on 8/17/2022
8 | - needs some holes in the pot so that water can drain
9 | 🌱 Pine Bonsai (every 4 days)
10 | - last watered on 8/19/2022
--------------------------------------------------------------------------------
/sample-data-old/stonks-portfolio.metadata:
--------------------------------------------------------------------------------
1 | {
2 | "sheets": [
3 | {
4 | "id": "_vYbP2uJkzwO2zAXyV9ZDY",
5 | "configId": "stonks-portfolio.1",
6 | "hideHighlightsInDocument": false
7 | },
8 | {
9 | "id": "_ns9sZr9wgelKJDzzVezCe",
10 | "configId": "stonks-portfolio.2",
11 | "hideHighlightsInDocument": false
12 | }
13 | ]
14 | }
--------------------------------------------------------------------------------
/sample-data/hikes.2.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "hikes.2",
3 | "name": "distance",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "{number:miles} miles",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "km",
12 | "formula": "`= ${Round(miles * 1.60934, 1)} km`",
13 | "visibility": "INLINE"
14 | }
15 | ]
16 | }
--------------------------------------------------------------------------------
/sample-data/_obENYhG63Nd13x43qDRr0.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "_obENYhG63Nd13x43qDRr0",
3 | "name": "hint",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "ℹ️{/.*/}",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "background-color",
12 | "formula": "\"#fff9c4\"",
13 | "visibility": "STYLE"
14 | }
15 | ]
16 | }
--------------------------------------------------------------------------------
/sample-data/youtube.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "youtube",
3 | "name": "video",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "https://www.youtube.com/watch?v={/.{11}/:id}",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "col2",
12 | "formula": "YoutubeVideo($, id)",
13 | "visibility": "REPLACE"
14 | }
15 | ]
16 | }
--------------------------------------------------------------------------------
/embedded.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Potluck
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/sample-data-old/youtube.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "youtube",
3 | "name": "youtube",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "https://www.youtube.com/watch?v={/.{11}/:id}",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "col2",
12 | "formula": "YoutubeVideo($, id)",
13 | "visibility": "REPLACE"
14 | }
15 | ]
16 | }
--------------------------------------------------------------------------------
/sample-data-old/bar.txt:
--------------------------------------------------------------------------------
1 | bake sale
2 | # Prices
3 |
4 | cake 🍰 = 4 Euro
5 | cupcake 🧁 = 3 Euro
6 | apple juice 🍎 = 2 Euro
7 | coke🥤 = 2 Euro
8 | coffee ☕ = 2 Euro
9 |
10 | # Sales
11 | enter emojis for each order on a new line
12 |
13 | 🍎🥤
14 | 🧁
15 | 🧁☕️
16 | ☕️
17 | 🍰
18 |
19 | Cake is too expensive 🍰 = 3 Euros
20 |
21 | 🍰
22 |
23 | Total:
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/sample-data-old/bar.total.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "bar.total",
3 | "name": "sheet2",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "Total: ",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "col7",
12 | "formula": "FindAll(\"sales\").reduce((sum, sale) => sum + sale.data.sum, 0)",
13 | "visibility": "INLINE"
14 | }
15 | ]
16 | }
--------------------------------------------------------------------------------
/sample-data-old/trip-plan.time.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "trip-plan.time",
3 | "name": "time",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "{number:hours}:{number:minutes}",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "time",
12 | "formula": "DateTime.fromObject({hours, minutes})",
13 | "visibility": "HIDDEN"
14 | }
15 | ]
16 | }
--------------------------------------------------------------------------------
/sample-data/_wqlqE65AnWhAK9dX8pm14.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "_wqlqE65AnWhAK9dX8pm14",
3 | "name": "link",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "[{/[^\\\\]]+/:label}]({/[^\\\\)]+/:url})",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "col2",
12 | "formula": "Link($, url, label)",
13 | "visibility": "REPLACE"
14 | }
15 | ]
16 | }
--------------------------------------------------------------------------------
/sample-data-old/default-searches.txt:
--------------------------------------------------------------------------------
1 | Default Searches
2 | Here are all the searches that are installed by default on docs in Potluck.
3 |
4 | Of course, you can always remove any/all of these from a given doc, and add more searches as well.
5 |
6 | Markdown: **bold**, *italic*
7 | number: 123
8 | duration: 2 minutes
9 | tags: #tag
10 | phone: (123) 456-7890
11 | date: 1/1/22
12 | youtube: https://www.youtube.com/watch?v=jNQXAC9IVRw
13 |
--------------------------------------------------------------------------------
/sample-data/_qvqxfvCRfMf4jMbieqxcv.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "_qvqxfvCRfMf4jMbieqxcv",
3 | "name": "search1",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "## Ingredients",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "col2",
12 | "formula": "`\\n${FindAll(\"ingredients\").map(h => TextOfHighlight(h)).join(\"\\n\")}`",
13 | "visibility": "INLINE"
14 | }
15 | ]
16 | }
--------------------------------------------------------------------------------
/sample-data/common.duration.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "common.duration",
3 | "name": "durations",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "{/((\\\\d+\\\\s+(hours?|minutes?|seconds?))\\\\s*)*(\\\\d+\\\\s+(hours?|minutes?|seconds?))/}",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "timer",
12 | "formula": "Timer($)",
13 | "visibility": "INLINE"
14 | }
15 | ]
16 | }
--------------------------------------------------------------------------------
/sample-data-old/_qvqxfvCRfMf4jMbieqxcv.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "_qvqxfvCRfMf4jMbieqxcv",
3 | "name": "search1",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "## Ingredients",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "col2",
12 | "formula": "`\\n${FindAll(\"ingredients\").map(h => TextOfHighlight(h)).join(\"\\n\")}`",
13 | "visibility": "INLINE"
14 | }
15 | ]
16 | }
--------------------------------------------------------------------------------
/sample-data-old/common.duration.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "common.duration",
3 | "name": "durations",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "{/((\\\\d+\\\\s+(hours?|minutes?|seconds?))\\\\s*)*(\\\\d+\\\\s+(hours?|minutes?|seconds?))/}",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "timer",
12 | "formula": "Timer($)",
13 | "visibility": "INLINE"
14 | }
15 | ]
16 | }
--------------------------------------------------------------------------------
/sample-data-old/trip-plan.timeSpan.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "trip-plan.timeSpan",
3 | "name": "timeSpan",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "{time:start} - {time:end}",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "isActive",
12 | "formula": "start.data.time <= DateTime.now() &&\nend.data.time >= DateTime.now()",
13 | "visibility": "HIDDEN"
14 | }
15 | ]
16 | }
--------------------------------------------------------------------------------
/sample-data/all-ingredients.names.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "all-ingredients.names",
3 | "name": "allIngredients",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "=SplitLines(\",\")",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "officialName",
12 | "formula": "First(Filter(MatchRegexp(\"USDA name: (.*),?\"), SameLine($)))",
13 | "visibility": "HIDDEN"
14 | }
15 | ]
16 | }
--------------------------------------------------------------------------------
/sample-data-old/all-ingredients.names.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "all-ingredients.names",
3 | "name": "allIngredients",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "=SplitLines(\",\")",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "officialName",
12 | "formula": "First(Filter(MatchRegexp(\"USDA name: (.*),?\"), SameLine($)))",
13 | "visibility": "HIDDEN"
14 | }
15 | ]
16 | }
--------------------------------------------------------------------------------
/sample-data-old/workshop.time.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "workshop.time",
3 | "name": "time",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "{number:hours}:{number:minutes}",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "dateTime",
12 | "formula": "DateTime.fromObject({\n hours: ParseInt(hours),\n minutes: ParseInt(minutes)\n})",
13 | "visibility": "HIDDEN"
14 | }
15 | ]
16 | }
--------------------------------------------------------------------------------
/sample-data/time-tracking.time.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "time-tracking.time",
3 | "name": "time2",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "{number:hours}:{number:minutes}",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "time",
12 | "formula": "DateTime.fromObject({\n hour: $.data.hours,\n minutes: $.data.minutes\n})",
13 | "visibility": "HIDDEN"
14 | }
15 | ]
16 | }
--------------------------------------------------------------------------------
/sample-data/workshop.time.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "workshop.time",
3 | "name": "time",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "{number:hours}:{number:minutes}",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "dateTime",
12 | "formula": "DateTime.fromObject({\n hours: ParseInt(hours),\n minutes: ParseInt(minutes)\n})",
13 | "visibility": "HIDDEN"
14 | }
15 | ]
16 | }
--------------------------------------------------------------------------------
/src/assets/pause-icon.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/sample-data-old/trip-plan.txt:
--------------------------------------------------------------------------------
1 | vacation - activities
2 | # La Spezia
3 |
4 | ## Grande Mercato
5 | Local food market, great vegetables and fresh fish
6 |
7 | Open: Mon - Sun 12:00 - 20:00
8 |
9 | # Genua
10 |
11 | ## Acquario di Genova
12 | biggest in europe
13 | book in advance to avoid queue
14 | Price: 27 Euro
15 | Open: Mon - Sun 9:00 - 20:00
16 |
17 | ## Lugano Cathedral
18 |
19 | Open:
20 | Wed - Fri 8:00 - 12:00, 15:00 - 19:00
21 | Mon - Tue 8:00 - 13:00
--------------------------------------------------------------------------------
/sample-data/todos.1.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "todos.1",
3 | "name": "completed",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "[x]",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "task",
12 | "formula": "TextAfter($)",
13 | "visibility": "HIDDEN"
14 | },
15 | {
16 | "name": "text-decoration",
17 | "formula": "\"line-through\"",
18 | "visibility": "STYLE"
19 | }
20 | ]
21 | }
--------------------------------------------------------------------------------
/sample-data-old/stonks-portfolio.1.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "stonks-portfolio.1",
3 | "name": "portfolio value",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "portfolio value",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "col8",
12 | "formula": "FindAll(\"holdings\").map(h => h.data.currentPrice * TextOfHighlight(h.data.shares)).reduce((a, b) => a + b, 0)",
13 | "visibility": "INLINE"
14 | }
15 | ]
16 | }
--------------------------------------------------------------------------------
/sample-data-old/tasktxt.1.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "tasktxt.1",
3 | "name": "[x]",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "[x]",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "col10",
12 | "formula": "TextAfter($)",
13 | "visibility": "HIDDEN"
14 | },
15 | {
16 | "name": "text-decoration",
17 | "formula": "\"line-through\"",
18 | "visibility": "STYLE"
19 | }
20 | ]
21 | }
--------------------------------------------------------------------------------
/sample-data-old/todos.1.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "todos.1",
3 | "name": "completed",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "[x]",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "task",
12 | "formula": "TextAfter($)",
13 | "visibility": "HIDDEN"
14 | },
15 | {
16 | "name": "text-decoration",
17 | "formula": "\"line-through\"",
18 | "visibility": "STYLE"
19 | }
20 | ]
21 | }
--------------------------------------------------------------------------------
/sample-data/default-searches.txt:
--------------------------------------------------------------------------------
1 | Default Searches
2 | Here are all the searches that are installed by default on docs in Potluck.
3 |
4 | Of course, you can always remove any/all of these from a given doc, and add more searches as well.
5 |
6 | Markdown: **bold**, *italic*
7 | number: 123
8 | duration: 2 minutes
9 | tags: #tag
10 | phone: (123) 456-7890
11 | date: 1/1/22
12 | link [ink & switch](https://inkandswitch.com)
13 | youtube: https://www.youtube.com/watch?v=jNQXAC9IVRw
14 |
--------------------------------------------------------------------------------
/sample-data/todos.metadata:
--------------------------------------------------------------------------------
1 | {
2 | "sheets": [
3 | {
4 | "id": "_Qd5pNiak8SHmdcMeWiOWv",
5 | "configId": "markdown",
6 | "hideHighlightsInDocument": false
7 | },
8 | {
9 | "id": "_Do0kWwRB2norSG0x4paId",
10 | "configId": "todos.1",
11 | "hideHighlightsInDocument": false
12 | },
13 | {
14 | "id": "_LyD5a1lA4LFoMpVDuEz2g",
15 | "configId": "todos.2",
16 | "hideHighlightsInDocument": false
17 | }
18 | ]
19 | }
--------------------------------------------------------------------------------
/sample-data/food.scale.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "food.scale",
3 | "name": "scale",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "scale by",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "slider",
12 | "formula": "Slider($)",
13 | "visibility": "SUPERSCRIPT"
14 | },
15 | {
16 | "name": "sliderValue",
17 | "formula": "slider.data.value",
18 | "visibility": "INLINE"
19 | }
20 | ]
21 | }
--------------------------------------------------------------------------------
/sample-data-old/food.scale.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "food.scale",
3 | "name": "scale",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "scale by",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "slider",
12 | "formula": "Slider($)",
13 | "visibility": "SUPERSCRIPT"
14 | },
15 | {
16 | "name": "sliderValue",
17 | "formula": "slider.data.value",
18 | "visibility": "INLINE"
19 | }
20 | ]
21 | }
--------------------------------------------------------------------------------
/sample-data-old/_BHxrxC3nSY2cEBjWlaEFW.txt:
--------------------------------------------------------------------------------
1 | Geoffrey's feedback
2 | - hard to find docs because they're sorted alpha not chrono
3 | - hard to copy all the relevant sheets from one doc to another (eg, all the todos stuff.) Requires adding them by name 1 by 1. Also need to learn to use them. Should each highlighter come with directions / examples? Maybe it's time for "packs"?
4 | - bug: FindAll("highlightedTeammate") doesn't work in the meeting notes tracker?
5 | - would be nice to have cmd-b for markdown bold
--------------------------------------------------------------------------------
/sample-data-old/bitcoin-price.1.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "bitcoin-price.1",
3 | "name": "test",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "${/[A-Z]+/:currency}",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "price",
12 | "formula": "FetchJSON(`https://api.coindesk.com/v1/bpi/currentprice/${TextOfHighlight(currency)}.json`, 5)?.bpi[TextOfHighlight(currency)].rate_float",
13 | "visibility": "INLINE"
14 | }
15 | ]
16 | }
--------------------------------------------------------------------------------
/sample-data-old/goodreads.metadata:
--------------------------------------------------------------------------------
1 | {
2 | "sheets": [
3 | {
4 | "id": "_gWD0cCuaOLXBxH0g5LLum",
5 | "configId": "goodreads.1",
6 | "hideHighlightsInDocument": false
7 | },
8 | {
9 | "id": "_NfwjFmOEW90pWR4TAUYR5",
10 | "configId": "goodreads.2",
11 | "hideHighlightsInDocument": false
12 | },
13 | {
14 | "id": "_gs5UdLFsi9yZ67aSBmbFI",
15 | "configId": "goodreads.3",
16 | "hideHighlightsInDocument": false
17 | }
18 | ]
19 | }
--------------------------------------------------------------------------------
/sample-data-old/pizza.metadata:
--------------------------------------------------------------------------------
1 | {
2 | "sheets": [
3 | {
4 | "id": "NEUs942EyHg11EAbM0NAy",
5 | "configId": "food.ingredient",
6 | "hideHighlightsInDocument": false
7 | },
8 | {
9 | "id": "Lnj0VUjOlSZrK39rWUhuh",
10 | "configId": "common.number",
11 | "hideHighlightsInDocument": false
12 | },
13 | {
14 | "id": "RmXyk98yDMNoLFZeDwTt2",
15 | "configId": "food.quantity",
16 | "hideHighlightsInDocument": false
17 | }
18 | ]
19 | }
--------------------------------------------------------------------------------
/sample-data/todos.2.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "todos.2",
3 | "name": "incomplete",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "[ ]",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "task",
12 | "formula": "TextAfter($)",
13 | "visibility": "HIDDEN"
14 | },
15 | {
16 | "name": "duedate",
17 | "formula": "Filter(FindAll(\"dates\"), a => SameLine($, a))",
18 | "visibility": "HIDDEN"
19 | }
20 | ]
21 | }
--------------------------------------------------------------------------------
/sample-data-old/todos.2.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "todos.2",
3 | "name": "incomplete",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "[ ]",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "task",
12 | "formula": "TextAfter($)",
13 | "visibility": "HIDDEN"
14 | },
15 | {
16 | "name": "duedate",
17 | "formula": "Filter(FindAll(\"dates\"), a => SameLine($, a))",
18 | "visibility": "HIDDEN"
19 | }
20 | ]
21 | }
--------------------------------------------------------------------------------
/sample-data-old/workout.metadata:
--------------------------------------------------------------------------------
1 | {
2 | "sheets": [
3 | {
4 | "id": "_telDp1hoD0hM9mc47lhN8",
5 | "configId": "workout.exercise",
6 | "hideHighlightsInDocument": false
7 | },
8 | {
9 | "id": "34sdhiogfz1c6INpAO6Y7",
10 | "configId": "common.number",
11 | "hideHighlightsInDocument": false
12 | },
13 | {
14 | "id": "HACBcetciF14iiDndby8U",
15 | "configId": "common.date",
16 | "hideHighlightsInDocument": false
17 | }
18 | ]
19 | }
--------------------------------------------------------------------------------
/sample-data/pizza-dough.inputs.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "pizza-dough.inputs",
3 | "name": "dough inputs",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "{/[0-9][0-9\\.]*/}",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "value",
12 | "formula": "ParseFloat($)",
13 | "visibility": "HIDDEN"
14 | },
15 | {
16 | "name": "label",
17 | "formula": "TextBefore($)",
18 | "visibility": "HIDDEN"
19 | }
20 | ]
21 | }
--------------------------------------------------------------------------------
/sample-data-old/pizza-dough.inputs.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "pizza-dough.inputs",
3 | "name": "dough inputs",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "{/[0-9][0-9\\.]*/}",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "value",
12 | "formula": "ParseFloat($)",
13 | "visibility": "HIDDEN"
14 | },
15 | {
16 | "name": "label",
17 | "formula": "TextBefore($)",
18 | "visibility": "HIDDEN"
19 | }
20 | ]
21 | }
--------------------------------------------------------------------------------
/sample-data-old/plant-watering.metadata:
--------------------------------------------------------------------------------
1 | {
2 | "sheets": [
3 | {
4 | "id": "_j3k9Ygb6rLwbSyd5vinuv",
5 | "configId": "plant-watering.2",
6 | "hideHighlightsInDocument": false
7 | },
8 | {
9 | "id": "_a8dvDDZ1nZurQsdKLnzjR",
10 | "configId": "plant-watering.1",
11 | "hideHighlightsInDocument": false
12 | },
13 | {
14 | "id": "_FcKOW7acW3QAUd18PMGAt",
15 | "configId": "markdown",
16 | "hideHighlightsInDocument": true
17 | }
18 | ]
19 | }
--------------------------------------------------------------------------------
/sample-data-old/workshop.metadata:
--------------------------------------------------------------------------------
1 | {
2 | "sheets": [
3 | {
4 | "id": "_ELCuyeUPoC9ZD6qbloCCk",
5 | "configId": "workshop.time",
6 | "hideHighlightsInDocument": false
7 | },
8 | {
9 | "id": "_V9L2URHFvfxK92PthY22T",
10 | "configId": "workshop.duration",
11 | "hideHighlightsInDocument": false
12 | },
13 | {
14 | "id": "_L2fgDJxV9czPfbYn3PB3E",
15 | "configId": "common.number",
16 | "hideHighlightsInDocument": false
17 | }
18 | ]
19 | }
--------------------------------------------------------------------------------
/sample-data/math-pack.metadata:
--------------------------------------------------------------------------------
1 | {
2 | "sheets": [
3 | {
4 | "id": "_ZdLqzeZVmLp5mdwVJIoP7",
5 | "configId": "math-pack.formula",
6 | "hideHighlightsInDocument": false
7 | },
8 | {
9 | "id": "_YPMob42qojduJMcd8ww5f",
10 | "configId": "math-pack.operator",
11 | "hideHighlightsInDocument": false
12 | },
13 | {
14 | "id": "_4UesOgfjwlOg0KMa3G8Vu",
15 | "configId": "common.number",
16 | "hideHighlightsInDocument": false
17 | }
18 | ]
19 | }
--------------------------------------------------------------------------------
/sample-data/plant-watering.metadata:
--------------------------------------------------------------------------------
1 | {
2 | "sheets": [
3 | {
4 | "id": "_j3k9Ygb6rLwbSyd5vinuv",
5 | "configId": "plant-watering.2",
6 | "hideHighlightsInDocument": false
7 | },
8 | {
9 | "id": "_a8dvDDZ1nZurQsdKLnzjR",
10 | "configId": "plant-watering.1",
11 | "hideHighlightsInDocument": false
12 | },
13 | {
14 | "id": "_FcKOW7acW3QAUd18PMGAt",
15 | "configId": "markdown",
16 | "hideHighlightsInDocument": true
17 | }
18 | ]
19 | }
--------------------------------------------------------------------------------
/sample-data-old/math-pack.metadata:
--------------------------------------------------------------------------------
1 | {
2 | "sheets": [
3 | {
4 | "id": "_ZdLqzeZVmLp5mdwVJIoP7",
5 | "configId": "math-pack.formula",
6 | "hideHighlightsInDocument": false
7 | },
8 | {
9 | "id": "_YPMob42qojduJMcd8ww5f",
10 | "configId": "math-pack.operator",
11 | "hideHighlightsInDocument": false
12 | },
13 | {
14 | "id": "_4UesOgfjwlOg0KMa3G8Vu",
15 | "configId": "common.number",
16 | "hideHighlightsInDocument": false
17 | }
18 | ]
19 | }
--------------------------------------------------------------------------------
/sample-data/stonks-portfolio.metadata:
--------------------------------------------------------------------------------
1 | {
2 | "sheets": [
3 | {
4 | "id": "_HiRMeImbNYHY5LnCjALFd",
5 | "configId": "markdown",
6 | "hideHighlightsInDocument": false
7 | },
8 | {
9 | "id": "_vYbP2uJkzwO2zAXyV9ZDY",
10 | "configId": "stonks-portfolio.1",
11 | "hideHighlightsInDocument": false
12 | },
13 | {
14 | "id": "_ns9sZr9wgelKJDzzVezCe",
15 | "configId": "stonks-portfolio.2",
16 | "hideHighlightsInDocument": false
17 | }
18 | ]
19 | }
--------------------------------------------------------------------------------
/src/assets/play-icon.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/sample-data-old/_AJ5yvielohvnk3xvY1ea5.txt:
--------------------------------------------------------------------------------
1 | 8/16 meeting
2 | - standup 10 minutes
3 | - writing check-in 10 minutes
4 | - i&s essay guide
5 | - block actions! 10 minutes
6 | - auto-run searches? 10 minutes
7 |
8 | next steps:
9 |
10 | - [ ] persistence improvements / pinned notes / chronological sort @shen
11 | - [ ] add more default highlighters @geoffrey
12 | - [ ] add a simple grouping mechanism (naming convention based) (display grouped in sidebar; add as a group) @shen
13 | - [ ] add readmes for searches
14 |
15 |
--------------------------------------------------------------------------------
/sample-data/bar.total.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "bar.total",
3 | "name": "sheet2",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "Total: ",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "total",
12 | "formula": "FindAll(\"sales\").reduce((sum, sale) => sum + sale.data.sum, 0)",
13 | "visibility": "HIDDEN"
14 | },
15 | {
16 | "name": "col2",
17 | "formula": "`$ ${total}`",
18 | "visibility": "INLINE"
19 | }
20 | ]
21 | }
--------------------------------------------------------------------------------
/sample-data/pizza-dough.metadata:
--------------------------------------------------------------------------------
1 | {
2 | "sheets": [
3 | {
4 | "id": "cUDHSgTWKnDpZFr5T7lqY",
5 | "configId": "pizza-dough.inputs",
6 | "hideHighlightsInDocument": false
7 | },
8 | {
9 | "id": "FAAxgnKxU0RFIdA75whQf",
10 | "configId": "pizza-dough.number-of-balls",
11 | "hideHighlightsInDocument": false
12 | },
13 | {
14 | "id": "OPDhVR1ZH5zXQ51JOCO76",
15 | "configId": "pizza-dough.water",
16 | "hideHighlightsInDocument": false
17 | }
18 | ]
19 | }
--------------------------------------------------------------------------------
/sample-data-old/pizza-dough.metadata:
--------------------------------------------------------------------------------
1 | {
2 | "sheets": [
3 | {
4 | "id": "cUDHSgTWKnDpZFr5T7lqY",
5 | "configId": "pizza-dough.inputs",
6 | "hideHighlightsInDocument": false
7 | },
8 | {
9 | "id": "FAAxgnKxU0RFIdA75whQf",
10 | "configId": "pizza-dough.number-of-balls",
11 | "hideHighlightsInDocument": false
12 | },
13 | {
14 | "id": "OPDhVR1ZH5zXQ51JOCO76",
15 | "configId": "pizza-dough.water",
16 | "hideHighlightsInDocument": false
17 | }
18 | ]
19 | }
--------------------------------------------------------------------------------
/sample-data-old/ice-cream.metadata:
--------------------------------------------------------------------------------
1 | {
2 | "sheets": [
3 | {
4 | "id": "oOb1B0Bhe4d9xo7FITnNV",
5 | "configId": "food.ingredient",
6 | "highlightSearchRange": [
7 | 12,
8 | 117
9 | ],
10 | "hideHighlightsInDocument": false
11 | },
12 | {
13 | "id": "OJfD4K5OMu5qEHvZiRONd",
14 | "configId": "common.number",
15 | "hideHighlightsInDocument": false
16 | },
17 | {
18 | "id": "dGc1oJZ2MSClD14RZNPe4",
19 | "configId": "food.quantity",
20 | "hideHighlightsInDocument": false
21 | }
22 | ]
23 | }
--------------------------------------------------------------------------------
/sample-data/recipe-pack.metadata:
--------------------------------------------------------------------------------
1 | {
2 | "sheets": [
3 | {
4 | "id": "_iKYWj3K4sDt13KilVZVMq",
5 | "configId": "food.scale",
6 | "highlightSearchRange": [
7 | 217,
8 | 225
9 | ],
10 | "hideHighlightsInDocument": false
11 | },
12 | {
13 | "id": "_AsFd1yr4rZzvExxiJopfi",
14 | "configId": "food.quantity",
15 | "hideHighlightsInDocument": false
16 | },
17 | {
18 | "id": "_OMwt6fLRimCRHPoEniDbN",
19 | "configId": "common.duration",
20 | "hideHighlightsInDocument": false
21 | }
22 | ]
23 | }
--------------------------------------------------------------------------------
/sample-data/time-tracking.open-time.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "time-tracking.open-time",
3 | "name": "openTime",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "{time2:time}{/-$/}",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "end",
12 | "formula": "DateTime.fromJSDate(NowDate()).toLocaleString(DateTime.TIME_24_SIMPLE)",
13 | "visibility": "INLINE"
14 | },
15 | {
16 | "name": "col12",
17 | "formula": "TemplateButton($, \"stop\", ` ${end}`)",
18 | "visibility": "INLINE"
19 | }
20 | ]
21 | }
--------------------------------------------------------------------------------
/sample-data-old/recipe-pack.metadata:
--------------------------------------------------------------------------------
1 | {
2 | "sheets": [
3 | {
4 | "id": "_iKYWj3K4sDt13KilVZVMq",
5 | "configId": "food.scale",
6 | "highlightSearchRange": [
7 | 217,
8 | 225
9 | ],
10 | "hideHighlightsInDocument": false
11 | },
12 | {
13 | "id": "_AsFd1yr4rZzvExxiJopfi",
14 | "configId": "food.quantity",
15 | "hideHighlightsInDocument": false
16 | },
17 | {
18 | "id": "_OMwt6fLRimCRHPoEniDbN",
19 | "configId": "common.duration",
20 | "hideHighlightsInDocument": false
21 | }
22 | ]
23 | }
--------------------------------------------------------------------------------
/sample-data-old/time-tracking.open-time.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "time-tracking.open-time",
3 | "name": "openTime",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "{time:time}{/-$/}",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "end",
12 | "formula": "DateTime.fromJSDate(NowDate()).toLocaleString(DateTime.TIME_24_SIMPLE)",
13 | "visibility": "INLINE"
14 | },
15 | {
16 | "name": "col12",
17 | "formula": "TemplateButton($, \"stop\", ` ${end}`)",
18 | "visibility": "INLINE"
19 | }
20 | ]
21 | }
--------------------------------------------------------------------------------
/sample-data/_nzNLA8UWxTsnQ01510U1e.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "_nzNLA8UWxTsnQ01510U1e",
3 | "name": "duration2",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "{time2:start} - {time2:end}",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "hours",
12 | "formula": "$.data.end.data.time\n.diff($.data.start.data.time).toMillis() / 1000 / 60 / 60",
13 | "visibility": "HIDDEN"
14 | },
15 | {
16 | "name": "col4",
17 | "formula": "`${Round(hours, 2)} hours`",
18 | "visibility": "INLINE"
19 | }
20 | ]
21 | }
--------------------------------------------------------------------------------
/sample-data/stonks-portfolio.1.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "stonks-portfolio.1",
3 | "name": "portfolio value",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "Portfolio value:",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "sum",
12 | "formula": "FindAll(\"holdings\").map(h => h.data.currentPrice * TextOfHighlight(h.data.shares)).reduce((a, b) => a + b, 0)",
13 | "visibility": "HIDDEN"
14 | },
15 | {
16 | "name": "col7",
17 | "formula": "`$${sum}`",
18 | "visibility": "INLINE"
19 | }
20 | ]
21 | }
--------------------------------------------------------------------------------
/sample-data-old/stonks-portfolio.2.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "stonks-portfolio.2",
3 | "name": "holdings",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "${/\\\\w+/:ticker} {number:shares}@${/[\\\\d\\.]+/:cost}",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "currentPrice",
12 | "formula": "TextOfHighlight(ticker) === \"GME\" ? 40 : 20",
13 | "visibility": "SUPERSCRIPT"
14 | },
15 | {
16 | "name": "col10",
17 | "formula": "`$${currentPrice * ParseInt(shares)}`",
18 | "visibility": "INLINE"
19 | }
20 | ]
21 | }
--------------------------------------------------------------------------------
/sample-data-old/tasktxt.metadata:
--------------------------------------------------------------------------------
1 | {
2 | "sheets": [
3 | {
4 | "id": "_2DSwSYTXo80t4t73tzSBP",
5 | "configId": "tasktxt.1",
6 | "hideHighlightsInDocument": false
7 | },
8 | {
9 | "id": "_J6Co9yyWhKW8Z4OF3zA3x",
10 | "configId": "tasktxt.2",
11 | "hideHighlightsInDocument": false
12 | },
13 | {
14 | "id": "_b4gF5DmbOcg95YEM7ffLt",
15 | "configId": "tasktxt.3",
16 | "hideHighlightsInDocument": false
17 | },
18 | {
19 | "id": "_aM6LJPm8p0uhoyIKVH6sj",
20 | "configId": "tasktxt.4",
21 | "hideHighlightsInDocument": false
22 | }
23 | ]
24 | }
--------------------------------------------------------------------------------
/sample-data/splitwise.metadata:
--------------------------------------------------------------------------------
1 | {
2 | "sheets": [
3 | {
4 | "id": "_OJPBy5ppBRj9TYuDYPy4l",
5 | "configId": "splitwise.1",
6 | "hideHighlightsInDocument": false
7 | },
8 | {
9 | "id": "_z1eraSVFW8v0jrntqmA9q",
10 | "configId": "splitwise.2",
11 | "hideHighlightsInDocument": false
12 | },
13 | {
14 | "id": "_AH0O2WKt2k09K6xkjC2so",
15 | "configId": "splitwise.3",
16 | "hideHighlightsInDocument": false
17 | },
18 | {
19 | "id": "_ErJjBAypL8RM4ojIYcHwb",
20 | "configId": "splitwise.4",
21 | "hideHighlightsInDocument": false
22 | }
23 | ]
24 | }
--------------------------------------------------------------------------------
/sample-data-old/movie-log.metadata:
--------------------------------------------------------------------------------
1 | {
2 | "sheets": [
3 | {
4 | "id": "_twysdJf5L2oWng1VP75uJ",
5 | "configId": "movie-log.1",
6 | "hideHighlightsInDocument": false
7 | },
8 | {
9 | "id": "_jbTqffI9b2LRuqpgmsP9e",
10 | "configId": "movie-log.2",
11 | "hideHighlightsInDocument": false
12 | },
13 | {
14 | "id": "_YrPYq5mwS7x7Xg6Wtjy0Z",
15 | "configId": "movie-log.3",
16 | "hideHighlightsInDocument": false
17 | },
18 | {
19 | "id": "_xsbsEirMvVNcOGJc8bdfY",
20 | "configId": "common.date",
21 | "hideHighlightsInDocument": false
22 | }
23 | ]
24 | }
--------------------------------------------------------------------------------
/sample-data-old/splitwise.metadata:
--------------------------------------------------------------------------------
1 | {
2 | "sheets": [
3 | {
4 | "id": "_OJPBy5ppBRj9TYuDYPy4l",
5 | "configId": "splitwise.1",
6 | "hideHighlightsInDocument": false
7 | },
8 | {
9 | "id": "_z1eraSVFW8v0jrntqmA9q",
10 | "configId": "splitwise.2",
11 | "hideHighlightsInDocument": false
12 | },
13 | {
14 | "id": "_AH0O2WKt2k09K6xkjC2so",
15 | "configId": "splitwise.3",
16 | "hideHighlightsInDocument": false
17 | },
18 | {
19 | "id": "_ErJjBAypL8RM4ojIYcHwb",
20 | "configId": "splitwise.4",
21 | "hideHighlightsInDocument": false
22 | }
23 | ]
24 | }
--------------------------------------------------------------------------------
/sample-data/workshop.metadata:
--------------------------------------------------------------------------------
1 | {
2 | "sheets": [
3 | {
4 | "id": "_97H0IvPoYbnh2SIWrSnDp",
5 | "configId": "markdown",
6 | "hideHighlightsInDocument": false
7 | },
8 | {
9 | "id": "_ELCuyeUPoC9ZD6qbloCCk",
10 | "configId": "workshop.time",
11 | "hideHighlightsInDocument": false
12 | },
13 | {
14 | "id": "_V9L2URHFvfxK92PthY22T",
15 | "configId": "workshop.duration",
16 | "hideHighlightsInDocument": false
17 | },
18 | {
19 | "id": "_L2fgDJxV9czPfbYn3PB3E",
20 | "configId": "common.number",
21 | "hideHighlightsInDocument": false
22 | }
23 | ]
24 | }
--------------------------------------------------------------------------------
/sample-data-old/tasktxt.3.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "tasktxt.3",
3 | "name": "duration",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "{number:number}s",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "startTime",
12 | "formula": "First(Filter(FindAll(\"startTime\"), SameLine))",
13 | "visibility": "HIDDEN"
14 | },
15 | {
16 | "name": "button",
17 | "formula": "startTime === undefined ? TemplateButton($, \"start\", () => ` / [${DateTime.now().toLocaleString(DateTime.TIME_24_WITH_SECONDS)}]`) : undefined",
18 | "visibility": "INLINE"
19 | }
20 | ]
21 | }
--------------------------------------------------------------------------------
/sample-data/welcome.metadata:
--------------------------------------------------------------------------------
1 | {
2 | "sheets": [
3 | {
4 | "id": "_u8JDE2Z4TtaNIrzgiaK23",
5 | "configId": "_obENYhG63Nd13x43qDRr0",
6 | "hideHighlightsInDocument": true
7 | },
8 | {
9 | "id": "_POxhxKIcPl6vD6PhKXlqj",
10 | "configId": "_wqlqE65AnWhAK9dX8pm14",
11 | "hideHighlightsInDocument": false
12 | },
13 | {
14 | "id": "_sDm7E0wtxnm19VrCbnfFh",
15 | "configId": "youtube",
16 | "hideHighlightsInDocument": false
17 | },
18 | {
19 | "id": "_ww9wvgHRl83zAO8h9CtoG",
20 | "configId": "markdown",
21 | "hideHighlightsInDocument": true
22 | }
23 | ]
24 | }
--------------------------------------------------------------------------------
/sample-data-old/workout.txt:
--------------------------------------------------------------------------------
1 | workout
2 | Gym 7/20/22
3 |
4 | Bench 30 Squat 40, Maintain next time.
5 |
6 | Gym 7/18/22
7 |
8 | Bench 30kg 10x3
9 | Squat 35kg 10x3 (easy, could increase weights next)
10 |
11 | Gym 7/16/22
12 |
13 | Squat 30kg 10x3
14 | Bench 35kg 10x3
15 |
16 | Try to focus on form more
17 |
18 | Gym 7/13/22
19 |
20 | Squat 30 10x3
21 | Bench 35 10x3
22 |
23 | Gym 7/12/22
24 |
25 | run 10 km
26 |
27 | Squat 30 10x3
28 | Bench 35 10x3
29 |
30 | 7/7/22 gym: elliptical + bench
31 |
32 | 7/6/22 gym: squat 30, bench 30
33 |
34 | 7/4/22 gym: run + squat
35 |
36 | 7/1/22 gym:
37 |
38 | Squat 30 10x3
39 | Bench 35 10x3, felt a bit sore
--------------------------------------------------------------------------------
/sample-data/workout.metadata:
--------------------------------------------------------------------------------
1 | {
2 | "sheets": [
3 | {
4 | "id": "_6O4u00zmQSuLKgN23bflN",
5 | "configId": "_obENYhG63Nd13x43qDRr0",
6 | "hideHighlightsInDocument": true
7 | },
8 | {
9 | "id": "_telDp1hoD0hM9mc47lhN8",
10 | "configId": "workout.exercise",
11 | "hideHighlightsInDocument": false
12 | },
13 | {
14 | "id": "34sdhiogfz1c6INpAO6Y7",
15 | "configId": "common.number",
16 | "hideHighlightsInDocument": false
17 | },
18 | {
19 | "id": "HACBcetciF14iiDndby8U",
20 | "configId": "common.date",
21 | "hideHighlightsInDocument": false
22 | }
23 | ]
24 | }
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "useDefineForClassFields": true,
5 | "lib": ["DOM", "DOM.Iterable", "ESNext"],
6 | "allowJs": false,
7 | "skipLibCheck": true,
8 | "esModuleInterop": false,
9 | "allowSyntheticDefaultImports": true,
10 | "strict": true,
11 | "forceConsistentCasingInFileNames": true,
12 | "module": "ESNext",
13 | "moduleResolution": "Node",
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "noEmit": true,
17 | "jsx": "react-jsx"
18 | },
19 | "include": ["src"],
20 | "references": [{ "path": "./tsconfig.node.json" }]
21 | }
22 |
--------------------------------------------------------------------------------
/sample-data-old/trip-plan.activity.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "trip-plan.activity",
3 | "name": "activity",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "{markdown.h2}",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "city",
12 | "formula": "PrevOfType($, \"city\")",
13 | "visibility": "HIDDEN"
14 | },
15 | {
16 | "name": "openingHours",
17 | "formula": "NextOfType($, \"openingHours\")",
18 | "visibility": "HIDDEN"
19 | },
20 | {
21 | "name": "isOpen",
22 | "formula": "openingHours.data.isOpen",
23 | "visibility": "HIDDEN"
24 | }
25 | ]
26 | }
--------------------------------------------------------------------------------
/sample-data-old/blocks.example-ingredient.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "blocks.example-ingredient",
3 | "name": "exampleIngredient",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "{/eggs|milk|butter|tofu|tomatoes/}",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "aisle",
12 | "formula": "{\n \"milk\" : \"dairy\",\n \"butter\": \"dairy\",\n \"tomatoes\": \"produce\"\n}[$] || \"other\"",
13 | "visibility": "HIDDEN"
14 | },
15 | {
16 | "name": "isVegan",
17 | "formula": "{\n \"milk\" : false,\n \"butter\": false\n}[$] !== false",
18 | "visibility": "HIDDEN"
19 | }
20 | ]
21 | }
--------------------------------------------------------------------------------
/src/refactor/cleanup-highlighters.mjs:
--------------------------------------------------------------------------------
1 | import fs from 'node:fs'
2 | import path from 'node:path'
3 | import { readDocumentSheets } from './utils.mjs'
4 |
5 | const watchPath = process.argv[2]
6 | const fullPath = path.resolve(process.cwd(), watchPath)
7 |
8 | const documentSheets = readDocumentSheets(watchPath)
9 |
10 | fs.readdirSync(fullPath).forEach(file => {
11 | if (
12 | file.startsWith('_') &&
13 | file.endsWith('.highlighter')
14 | ) {
15 | const configId = path.basename(file, '.highlighter')
16 |
17 | if (documentSheets.every((sheet) => sheet.configId !== configId)) {
18 | fs.unlinkSync(path.join(fullPath, file))
19 | }
20 | }
21 | })
--------------------------------------------------------------------------------
/sample-data/plant-watering.1.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "plant-watering.1",
3 | "name": "interval",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "every {number:interval} days",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "intervalInt",
12 | "formula": "ParseInt(interval)",
13 | "visibility": "HIDDEN"
14 | },
15 | {
16 | "name": "daysSince",
17 | "formula": "Round((Date.now() - Date.parse(dates)) / (24 * 60 * 60 * 1000), 1)",
18 | "visibility": "HIDDEN"
19 | },
20 | {
21 | "name": "needsWater",
22 | "formula": "daysSince > intervalInt",
23 | "visibility": "HIDDEN"
24 | }
25 | ]
26 | }
--------------------------------------------------------------------------------
/sample-data-old/plant-watering.1.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "plant-watering.1",
3 | "name": "interval",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "every {number:interval} days",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "intervalInt",
12 | "formula": "ParseInt(interval)",
13 | "visibility": "HIDDEN"
14 | },
15 | {
16 | "name": "daysSince",
17 | "formula": "Round((Date.now() - Date.parse(dates)) / (24 * 60 * 60 * 1000), 1)",
18 | "visibility": "HIDDEN"
19 | },
20 | {
21 | "name": "needsWater",
22 | "formula": "daysSince > intervalInt",
23 | "visibility": "HIDDEN"
24 | }
25 | ]
26 | }
--------------------------------------------------------------------------------
/sample-data/splitwise.1.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "splitwise.1",
3 | "name": "output",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "total [{name:name}]",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "userItems",
12 | "formula": "$.allPrev(\"userMoney\")\n.filter((userMoney) => (\n userMoney.data.name.isEqualTo(name)\n))",
13 | "visibility": "HIDDEN"
14 | },
15 | {
16 | "name": "sum",
17 | "formula": "userItems\n.sumOf(\"data.money.data.amount\")",
18 | "visibility": "HIDDEN"
19 | },
20 | {
21 | "name": "sumText",
22 | "formula": "`$${sum}`",
23 | "visibility": "INLINE"
24 | }
25 | ]
26 | }
--------------------------------------------------------------------------------
/sample-data-old/splitwise.1.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "splitwise.1",
3 | "name": "output",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "total [{name:name}]",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "userItems",
12 | "formula": "$.allPrev(\"userMoney\")\n.filter((userMoney) => (\n userMoney.data.name.isEqualTo(name)\n))",
13 | "visibility": "HIDDEN"
14 | },
15 | {
16 | "name": "sum",
17 | "formula": "userItems\n.sumOf(\"data.money.data.amount\")",
18 | "visibility": "HIDDEN"
19 | },
20 | {
21 | "name": "sumText",
22 | "formula": "`$${sum}`",
23 | "visibility": "INLINE"
24 | }
25 | ]
26 | }
--------------------------------------------------------------------------------
/sample-data/food.quantity.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "food.quantity",
3 | "name": "quantity",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "{number:amount} {/(cup|tablespoon|tbsp|teaspoon|tsp|pound|lb|gram|g|milliliter|ml)s?/:unit}",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "scaleFactor",
12 | "formula": "Find(\"scale\")?.data.sliderValue",
13 | "visibility": "HIDDEN"
14 | },
15 | {
16 | "name": "scaledAmount",
17 | "formula": "(scaleFactor && scaleFactor !== 1 && amount) ? `${scaleFactor * amount.data.value} ${unit}${amount.data.value === 1 ? 's' : ''}` : undefined",
18 | "visibility": "REPLACE"
19 | }
20 | ]
21 | }
--------------------------------------------------------------------------------
/sample-data-old/food.quantity.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "food.quantity",
3 | "name": "quantity",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "{number:amount} {/(cup|tablespoon|tbsp|teaspoon|tsp|pound|lb|gram|g|milliliter|ml)s?/:unit}",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "scaleFactor",
12 | "formula": "Find(\"scale\")?.data.sliderValue",
13 | "visibility": "HIDDEN"
14 | },
15 | {
16 | "name": "scaledAmount",
17 | "formula": "(scaleFactor && scaleFactor !== 1 && amount) ? `${scaleFactor * amount.data.value} ${unit}${amount.data.value === 1 ? 's' : ''}` : undefined",
18 | "visibility": "REPLACE"
19 | }
20 | ]
21 | }
--------------------------------------------------------------------------------
/sample-data-old/trip-plan.openingHours.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "trip-plan.openingHours",
3 | "name": "openingHours",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "Open:",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "openingRange",
12 | "formula": "Filter(NextUntil($, HasType(\"markdown\")), HasType(\"openingRange\"))",
13 | "visibility": "HIDDEN"
14 | },
15 | {
16 | "name": "isOpen",
17 | "formula": "openingRange.some(r => r.data.isActive)",
18 | "visibility": "HIDDEN"
19 | },
20 | {
21 | "name": "color",
22 | "formula": "isOpen ? \"green\" : \"red\"",
23 | "visibility": "STYLE"
24 | }
25 | ]
26 | }
--------------------------------------------------------------------------------
/sample-data/bar.sales.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "bar.sales",
3 | "name": "sales",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "{/^/}{emoji+:items}{/$/}",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "prices",
12 | "formula": "items.map(item => (\n item\n .allPrev(\"price\")\n .find(price => (\n price.data.item.isEqualTo(item)\n ))\n))",
13 | "visibility": "HIDDEN"
14 | },
15 | {
16 | "name": "sum",
17 | "formula": "prices.sumOf(\"data.price\")",
18 | "visibility": "HIDDEN"
19 | },
20 | {
21 | "name": "sumText",
22 | "formula": "`= $ ${sum}`",
23 | "visibility": "INLINE"
24 | }
25 | ]
26 | }
--------------------------------------------------------------------------------
/sample-data/_p8Ot2ukI8aaITNa5bY5JU.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "_p8Ot2ukI8aaITNa5bY5JU",
3 | "name": "search2",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "Total:",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "durations",
12 | "formula": "Filter(\n PrevUntil($, HasType(\"markdown\")),\n HasType(\"duration2\")\n)\n",
13 | "visibility": "HIDDEN"
14 | },
15 | {
16 | "name": "total",
17 | "formula": "durations.reduce((sum, duration) => (\n sum + duration.data.hours\n), 0)",
18 | "visibility": "HIDDEN"
19 | },
20 | {
21 | "name": "col3",
22 | "formula": "`${Round(total, 2)} hours`",
23 | "visibility": "INLINE"
24 | }
25 | ]
26 | }
--------------------------------------------------------------------------------
/sample-data/workout.txt:
--------------------------------------------------------------------------------
1 | Workout
2 | ℹ️ Click on the search icon in the top right corner of this window to see a calendar view of these workouts
3 |
4 | Gym 7/20/22
5 |
6 | Bench 30 Squat 40, Maintain next time.
7 |
8 | Gym 7/18/22
9 |
10 | Bench 30kg 10x3
11 | Squat 35kg 10x3 (easy, could increase weights next)
12 |
13 | Gym 7/16/22
14 |
15 | Squat 30kg 10x3
16 | Bench 35kg 10x3
17 |
18 | Try to focus on form more
19 |
20 | Gym 7/13/22
21 |
22 | Squat 30 10x3
23 | Bench 35 10x3
24 |
25 | Gym 7/12/22
26 |
27 | run 10 km
28 |
29 | Squat 30 10x3
30 | Bench 35 10x3
31 |
32 | 7/7/22 gym: elliptical + bench
33 |
34 | 7/6/22 gym: squat 30, bench 30
35 |
36 | 7/4/22 gym: run + squat
37 |
38 | 7/1/22 gym:
39 |
40 | Squat 30 10x3
41 | Bench 35 10x3, felt a bit sore
--------------------------------------------------------------------------------
/src/embedded-main.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from "react-dom/client";
3 | import * as Tooltip from "@radix-ui/react-tooltip";
4 | import * as Toast from "@radix-ui/react-toast";
5 | import {EmbeddedDocument} from "./EmbeddedDocument";
6 |
7 | const params = new URLSearchParams(location.search)
8 | const documentUrl = params.get('document')
9 |
10 | if (documentUrl) {
11 | ReactDOM.createRoot(document.getElementById("root")!).render(
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | );
20 |
21 | window.top?.postMessage({ type: "demoLoaded", name: documentUrl})
22 | }
--------------------------------------------------------------------------------
/src/refactor/utils.mjs:
--------------------------------------------------------------------------------
1 | import fs from 'node:fs'
2 | import path from 'node:path'
3 |
4 | export function readDocumentSheets (basePath) {
5 | return JSON.parse(fs.readFileSync(path.join(basePath, "_documentsheets"), { encoding: 'utf8', flag: 'r' }))
6 | }
7 |
8 | export function writeDocumentSheets (basePath, documentsSheets) {
9 | fs.writeFileSync(path.join(basePath, "_documentsheets"), JSON.stringify(documentsSheets, null, 2))
10 | }
11 |
12 | export function readHighlighter (basePath, id) {
13 | return JSON.parse(fs.readFileSync(path.join(basePath, `${id}.highlighter`)))
14 | }
15 |
16 | export function writeHighlighter (basePath, id, highlighter) {
17 | fs.writeFileSync(path.join(basePath, `${id}.highlighter`), JSON.stringify(highlighter, null, 2))
18 | }
19 |
20 |
21 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
7 |
8 |
9 |
10 |
11 |
12 | Potluck
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/sample-data-old/blocks.collapse.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "blocks.collapse",
3 | "name": "collapseBlock",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "{/➡️|⬇️/}",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "block",
12 | "formula": "$.prev(\"block\")",
13 | "visibility": "HIDDEN"
14 | },
15 | {
16 | "name": "col14",
17 | "formula": "$ == \"⬇️\" ? \"...\" : undefined",
18 | "visibility": "REPLACE"
19 | },
20 | {
21 | "name": "opposite",
22 | "formula": "$ == \"⬇️\" ? \"➡️\" : \"⬇️\"",
23 | "visibility": "HIDDEN"
24 | },
25 | {
26 | "name": "button",
27 | "formula": "TemplateButton($, $, opposite, \"replace\")",
28 | "visibility": "INLINE"
29 | }
30 | ]
31 | }
--------------------------------------------------------------------------------
/src/main.tsx:
--------------------------------------------------------------------------------
1 | const openDocumentId = new URLSearchParams(location.search).get("openDocument")
2 |
3 |
4 | import React from "react";
5 | import ReactDOM from "react-dom/client";
6 | import App from "./App";
7 | import * as Tooltip from "@radix-ui/react-tooltip";
8 | import * as Toast from "@radix-ui/react-toast";
9 | import "./index.css";
10 | import { selectedTextDocumentIdBox, textDocumentsMobx } from "./primitives";
11 |
12 | if (openDocumentId && textDocumentsMobx.get(openDocumentId)) {
13 | selectedTextDocumentIdBox.set(openDocumentId)
14 | }
15 |
16 | ReactDOM.createRoot(document.getElementById("root")!).render(
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | );
25 |
--------------------------------------------------------------------------------
/sample-data-old/ice-cream.txt:
--------------------------------------------------------------------------------
1 | ice cream
2 | Ingredients
3 | 1¾ cups heavy cream
4 | 2¼ cup whole milk
5 | 3¾ cup sugar
6 | 4⅛ teaspoon fine sea salt
7 | 5 tablespoon vanilla extract
8 |
9 | Instructions
10 | Pour 1 cup of the cream into a saucepan and add the sugar, salt. Scrape the seeds of the vanilla bean into the pot and then add the vanilla pod to the pot. Warm the mixture over medium heat, just until the sugar dissolves. Remove from the heat and add the remaining cream, milk, and vanilla extract (if using extract). Stir to combine and chill in the refrigerator.
11 | When ready to churn, remove the vanilla pod, whisk mixture again and pour into ice cream maker. Churn according to the manufacturer’s instructions. Transfer the finished ice cream to an airtight container and place in the freezer until ready to serve. Enjoy!
--------------------------------------------------------------------------------
/sample-data-old/time-tracking.metadata:
--------------------------------------------------------------------------------
1 | {
2 | "sheets": [
3 | {
4 | "id": "_Wsf3s8IyOld9knDg1rdnC",
5 | "configId": "common.number",
6 | "hideHighlightsInDocument": false
7 | },
8 | {
9 | "id": "_1Co3scDP2mNoUzFoYltQ5",
10 | "configId": "markdown",
11 | "hideHighlightsInDocument": false
12 | },
13 | {
14 | "id": "_H7EWwGZc6qecFzGl0UTWc",
15 | "configId": "time-tracking.project",
16 | "hideHighlightsInDocument": false
17 | },
18 | {
19 | "id": "_mMM1kgsMOjd4Jtwadj75F",
20 | "configId": "time-tracking.time",
21 | "hideHighlightsInDocument": false
22 | },
23 | {
24 | "id": "_2gPgKJTGW7qDrcqP7ofMb",
25 | "configId": "time-tracking.open-time",
26 | "hideHighlightsInDocument": false
27 | }
28 | ]
29 | }
--------------------------------------------------------------------------------
/sample-data/markdown.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "markdown",
3 | "name": "markdown",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "=Markdown()",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "type",
12 | "formula": "$.data.type",
13 | "visibility": "HIDDEN"
14 | },
15 | {
16 | "name": "font-weight",
17 | "formula": "type.startsWith(\"h\") || type === \"bold\" ? \"bold\" : \"normal\"",
18 | "visibility": "STYLE"
19 | },
20 | {
21 | "name": "font-style",
22 | "formula": "type === \"italic\" ? \"italic\" : \"normal\"",
23 | "visibility": "STYLE"
24 | },
25 | {
26 | "name": "font-size",
27 | "formula": "({\"h1\": \"1.3rem\",\n \"h2\": \"1rem\"}[type]) || \"normal\"",
28 | "visibility": "STYLE"
29 | }
30 | ]
31 | }
--------------------------------------------------------------------------------
/src/assets/slider-track.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/sample-data-old/markdown.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "markdown",
3 | "name": "markdown",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "=Markdown()",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "type",
12 | "formula": "$.data.type",
13 | "visibility": "HIDDEN"
14 | },
15 | {
16 | "name": "font-weight",
17 | "formula": "type.startsWith(\"h\") || type === \"bold\" ? \"bold\" : \"normal\"",
18 | "visibility": "STYLE"
19 | },
20 | {
21 | "name": "font-style",
22 | "formula": "type === \"italic\" ? \"italic\" : \"normal\"",
23 | "visibility": "STYLE"
24 | },
25 | {
26 | "name": "font-size",
27 | "formula": "({\"h1\": \"1.3rem\",\n \"h2\": \"1rem\"}[type]) || \"normal\"",
28 | "visibility": "STYLE"
29 | }
30 | ]
31 | }
--------------------------------------------------------------------------------
/src/assets/asterisk-icon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/sample-data-old/gochujang-pork.metadata:
--------------------------------------------------------------------------------
1 | {
2 | "sheets": [
3 | {
4 | "id": "zUe6BSpguigbb8wqVJ8Jo",
5 | "configId": "food.ingredient",
6 | "highlightSearchRange": [
7 | 662,
8 | 1430
9 | ],
10 | "hideHighlightsInDocument": false
11 | },
12 | {
13 | "id": "69ETypGNV9MGLGuYDJVMT",
14 | "configId": "common.number",
15 | "hideHighlightsInDocument": false
16 | },
17 | {
18 | "id": "rtmeGoGDzu10C1mxqo8rG",
19 | "configId": "food.quantity",
20 | "hideHighlightsInDocument": false
21 | },
22 | {
23 | "id": "RAknoIAG0p2vDdGvW6Dvi",
24 | "configId": "food.scale",
25 | "hideHighlightsInDocument": false
26 | },
27 | {
28 | "id": "DNQtQEvwr9jkN8cHFWgeE",
29 | "configId": "common.duration",
30 | "hideHighlightsInDocument": false
31 | }
32 | ]
33 | }
--------------------------------------------------------------------------------
/sample-data/gochujang-pork.metadata:
--------------------------------------------------------------------------------
1 | {
2 | "sheets": [
3 | {
4 | "id": "zUe6BSpguigbb8wqVJ8Jo",
5 | "configId": "food.ingredient",
6 | "highlightSearchRange": [
7 | 662,
8 | 1430
9 | ],
10 | "hideHighlightsInDocument": false
11 | },
12 | {
13 | "id": "69ETypGNV9MGLGuYDJVMT",
14 | "configId": "common.number",
15 | "hideHighlightsInDocument": false
16 | },
17 | {
18 | "id": "rtmeGoGDzu10C1mxqo8rG",
19 | "configId": "food.quantity",
20 | "hideHighlightsInDocument": false
21 | },
22 | {
23 | "id": "RAknoIAG0p2vDdGvW6Dvi",
24 | "configId": "food.scale",
25 | "hideHighlightsInDocument": false
26 | },
27 | {
28 | "id": "DNQtQEvwr9jkN8cHFWgeE",
29 | "configId": "common.duration",
30 | "hideHighlightsInDocument": false
31 | }
32 | ]
33 | }
--------------------------------------------------------------------------------
/sample-data/time-tracking.project.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "time-tracking.project",
3 | "name": "project",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "{markdown.h1:project}",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "time",
12 | "formula": "DateTime.now().toLocaleString(DateTime.TIME_24_SIMPLE)",
13 | "visibility": "HIDDEN"
14 | },
15 | {
16 | "name": "unfinishedTime",
17 | "formula": "NextOfType($, \"openTime\", 10)",
18 | "visibility": "HIDDEN"
19 | },
20 | {
21 | "name": "inProgress",
22 | "formula": "unfinishedTime !== undefined",
23 | "visibility": "HIDDEN"
24 | },
25 | {
26 | "name": "startButton",
27 | "formula": "!inProgress ? TemplateButton($, \"start\", `\\n${time} -`) : undefined",
28 | "visibility": "INLINE"
29 | }
30 | ]
31 | }
--------------------------------------------------------------------------------
/sample-data-old/time-tracking.project.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "time-tracking.project",
3 | "name": "project",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "{markdown.h1:project}",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "time",
12 | "formula": "DateTime.now().toLocaleString(DateTime.TIME_24_SIMPLE)",
13 | "visibility": "HIDDEN"
14 | },
15 | {
16 | "name": "unfinishedTime",
17 | "formula": "NextOfType($, \"openTime\", 10)",
18 | "visibility": "HIDDEN"
19 | },
20 | {
21 | "name": "inProgress",
22 | "formula": "unfinishedTime !== undefined",
23 | "visibility": "HIDDEN"
24 | },
25 | {
26 | "name": "startButton",
27 | "formula": "!inProgress ? TemplateButton($, \"start\", `\\n${time} -`) : undefined",
28 | "visibility": "INLINE"
29 | }
30 | ]
31 | }
--------------------------------------------------------------------------------
/sample-data-old/trip-plan.openingRange.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "trip-plan.openingRange",
3 | "name": "openingRange",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "{day:start} - {day:end}",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "days",
12 | "formula": "const days = [\"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\", \"Sun\"]\n\nreturn days.slice(\n days.indexOf(start.valueOf()),\n days.indexOf(end.valueOf()) + 1\n)",
13 | "visibility": "HIDDEN"
14 | },
15 | {
16 | "name": "timeRanges",
17 | "formula": "Filter(FindAll(\"timeSpan\"), SameLine($))",
18 | "visibility": "HIDDEN"
19 | },
20 | {
21 | "name": "isActive",
22 | "formula": "days.includes(DateTime.now().weekdayShort) &&\ntimeRanges.some(r => r.data.isActive)",
23 | "visibility": "HIDDEN"
24 | }
25 | ]
26 | }
--------------------------------------------------------------------------------
/sample-data-old/_EPSPtlS2leXEhRmSEoQZh.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "_EPSPtlS2leXEhRmSEoQZh",
3 | "name": "teammate",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "{/@shen|@sonnentag|@max|@geoffrey|@pvh/}",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "font-weight",
12 | "formula": "\"normal\"",
13 | "visibility": "STYLE"
14 | },
15 | {
16 | "name": "background",
17 | "formula": "\"#eeeeee\"",
18 | "visibility": "STYLE"
19 | },
20 | {
21 | "name": "padding",
22 | "formula": "\"2px\"",
23 | "visibility": "STYLE"
24 | },
25 | {
26 | "name": "border-radius",
27 | "formula": "\"5px\"",
28 | "visibility": "STYLE"
29 | },
30 | {
31 | "name": "highlighted",
32 | "formula": "FindAll(\"highlightedTeammate\")",
33 | "visibility": "HIDDEN"
34 | }
35 | ]
36 | }
--------------------------------------------------------------------------------
/sample-data-old/blocks.shuffle.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "blocks.shuffle",
3 | "name": "shuffleBlock",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "🔀",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "block",
12 | "formula": "$.prev(\"block\")",
13 | "visibility": "HIDDEN"
14 | },
15 | {
16 | "name": "lines",
17 | "formula": "SplitLines().filter(line => (\n line.span[0] > block.span[0] &&\n line.span[1] < block.span[1]\n))",
18 | "visibility": "HIDDEN"
19 | },
20 | {
21 | "name": "shuffledLines",
22 | "formula": "lines.sortBy(line => Math.random())",
23 | "visibility": "HIDDEN"
24 | },
25 | {
26 | "name": "button",
27 | "formula": "TemplateButton(block, \"shuffle\", `[\\n${shuffledLines.join(\"\\n\")}\\n]`, \"replace\")",
28 | "visibility": "INLINE"
29 | }
30 | ]
31 | }
--------------------------------------------------------------------------------
/sample-data/_EPSPtlS2leXEhRmSEoQZh.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "_EPSPtlS2leXEhRmSEoQZh",
3 | "name": "teammate",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "{/@shen|@sonnentag|@max|@geoffrey|@pvh/}",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "font-weight",
12 | "formula": "\"normal\"",
13 | "visibility": "STYLE"
14 | },
15 | {
16 | "name": "background",
17 | "formula": "\"#eeeeee\"",
18 | "visibility": "STYLE"
19 | },
20 | {
21 | "name": "padding",
22 | "formula": "\"2px\"",
23 | "visibility": "STYLE"
24 | },
25 | {
26 | "name": "border-radius",
27 | "formula": "\"5px\"",
28 | "visibility": "STYLE"
29 | },
30 | {
31 | "name": "highlighted",
32 | "formula": "FindAll(\"highlightedTeammate\")",
33 | "visibility": "HIDDEN"
34 | }
35 | ]
36 | }
--------------------------------------------------------------------------------
/sample-data/bar.metadata:
--------------------------------------------------------------------------------
1 | {
2 | "sheets": [
3 | {
4 | "id": "_2TRyK9cYdyhE5k2QGQNrX",
5 | "configId": "markdown",
6 | "hideHighlightsInDocument": false
7 | },
8 | {
9 | "id": "_csZJCgJ3S78PObHxaQCt6",
10 | "configId": "common.emoji",
11 | "hideHighlightsInDocument": false
12 | },
13 | {
14 | "id": "_M4kQAPANPacHMzSlMrxGj",
15 | "configId": "common.number",
16 | "hideHighlightsInDocument": false
17 | },
18 | {
19 | "id": "_XnudOhOYOAwZ0O41RC71M",
20 | "configId": "bar.price",
21 | "hideHighlightsInDocument": false
22 | },
23 | {
24 | "id": "_FByk6n1gPfsxtVnlmbw9g",
25 | "configId": "bar.sales",
26 | "hideHighlightsInDocument": false
27 | },
28 | {
29 | "id": "_76ua3Ul5NMlnigIHzspSm",
30 | "configId": "bar.total",
31 | "hideHighlightsInDocument": false
32 | }
33 | ]
34 | }
--------------------------------------------------------------------------------
/sample-data-old/bar.metadata:
--------------------------------------------------------------------------------
1 | {
2 | "sheets": [
3 | {
4 | "id": "_2TRyK9cYdyhE5k2QGQNrX",
5 | "configId": "markdown",
6 | "hideHighlightsInDocument": false
7 | },
8 | {
9 | "id": "_csZJCgJ3S78PObHxaQCt6",
10 | "configId": "common.emoji",
11 | "hideHighlightsInDocument": false
12 | },
13 | {
14 | "id": "_M4kQAPANPacHMzSlMrxGj",
15 | "configId": "common.number",
16 | "hideHighlightsInDocument": false
17 | },
18 | {
19 | "id": "_XnudOhOYOAwZ0O41RC71M",
20 | "configId": "bar.price",
21 | "hideHighlightsInDocument": false
22 | },
23 | {
24 | "id": "_FByk6n1gPfsxtVnlmbw9g",
25 | "configId": "bar.sales",
26 | "hideHighlightsInDocument": false
27 | },
28 | {
29 | "id": "_76ua3Ul5NMlnigIHzspSm",
30 | "configId": "bar.total",
31 | "hideHighlightsInDocument": false
32 | }
33 | ]
34 | }
--------------------------------------------------------------------------------
/sample-data-old/plant-watering.2.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "plant-watering.2",
3 | "name": "schedule",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "{dates:lastWatered}",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "daysSince",
12 | "formula": "Round((Date.now() - Date.parse(lastWatered)) / (24 * 60 * 60 * 1000), 1)",
13 | "visibility": "HIDDEN"
14 | },
15 | {
16 | "name": "needsWater",
17 | "formula": "daysSince > PrevOfType($, \"interval\").data.intervalInt",
18 | "visibility": "HIDDEN"
19 | },
20 | {
21 | "name": "color",
22 | "formula": "needsWater ? \"red\": \"green\"",
23 | "visibility": "STYLE"
24 | },
25 | {
26 | "name": "button",
27 | "formula": "TemplateButton($, \"🚿\", DateTime.now().toLocaleString(), \"replace\")",
28 | "visibility": "INLINE"
29 | }
30 | ]
31 | }
--------------------------------------------------------------------------------
/sample-data/aeropress.metadata:
--------------------------------------------------------------------------------
1 | {
2 | "sheets": [
3 | {
4 | "id": "eiIXzGv8Zq1zybgMZdd1z",
5 | "configId": "common.number",
6 | "hideHighlightsInDocument": false
7 | },
8 | {
9 | "id": "X8bBiHSTseJNK4lyNkl2D",
10 | "configId": "food.quantity",
11 | "hideHighlightsInDocument": false
12 | },
13 | {
14 | "id": "JdoUlaXUw0gg15KLpYHp6",
15 | "configId": "common.duration",
16 | "hideHighlightsInDocument": false
17 | },
18 | {
19 | "id": "fUpyOI1wmdDbCnuq6dNfU",
20 | "configId": "food.scale",
21 | "hideHighlightsInDocument": false
22 | },
23 | {
24 | "id": "PL4sKTsqJcM18ppBJIviZ",
25 | "configId": "common.date",
26 | "hideHighlightsInDocument": false
27 | },
28 | {
29 | "id": "Htzihyo06gMLB1lCrEl24",
30 | "configId": "markdown",
31 | "hideHighlightsInDocument": false
32 | }
33 | ]
34 | }
--------------------------------------------------------------------------------
/sample-data/workout.exercise.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "workout.exercise",
3 | "name": "exercise",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "{/bench|squat|run|elliptical/}",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "date",
12 | "formula": "PrevOfType($, \"dates\")",
13 | "visibility": "HIDDEN"
14 | },
15 | {
16 | "name": "numbers",
17 | "formula": "Filter(NextUntil($, HasType(\"exercise\")), SameLine($))",
18 | "visibility": "HIDDEN"
19 | },
20 | {
21 | "name": "weight",
22 | "formula": "First(numbers)",
23 | "visibility": "HIDDEN"
24 | },
25 | {
26 | "name": "sets",
27 | "formula": "Second(numbers)",
28 | "visibility": "HIDDEN"
29 | },
30 | {
31 | "name": "reps",
32 | "formula": "Third(numbers)",
33 | "visibility": "HIDDEN"
34 | }
35 | ]
36 | }
--------------------------------------------------------------------------------
/sample-data-old/aeropress.metadata:
--------------------------------------------------------------------------------
1 | {
2 | "sheets": [
3 | {
4 | "id": "eiIXzGv8Zq1zybgMZdd1z",
5 | "configId": "common.number",
6 | "hideHighlightsInDocument": false
7 | },
8 | {
9 | "id": "X8bBiHSTseJNK4lyNkl2D",
10 | "configId": "food.quantity",
11 | "hideHighlightsInDocument": false
12 | },
13 | {
14 | "id": "JdoUlaXUw0gg15KLpYHp6",
15 | "configId": "common.duration",
16 | "hideHighlightsInDocument": false
17 | },
18 | {
19 | "id": "fUpyOI1wmdDbCnuq6dNfU",
20 | "configId": "food.scale",
21 | "hideHighlightsInDocument": false
22 | },
23 | {
24 | "id": "PL4sKTsqJcM18ppBJIviZ",
25 | "configId": "common.date",
26 | "hideHighlightsInDocument": false
27 | },
28 | {
29 | "id": "Htzihyo06gMLB1lCrEl24",
30 | "configId": "markdown",
31 | "hideHighlightsInDocument": false
32 | }
33 | ]
34 | }
--------------------------------------------------------------------------------
/sample-data-old/blocks.sort.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "blocks.sort",
3 | "name": "sortBlock",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "🔤",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "block",
12 | "formula": "$.prev(\"block\")",
13 | "visibility": "HIDDEN"
14 | },
15 | {
16 | "name": "lines",
17 | "formula": "SplitLines().filter(line => (\n line.span[0] > block.span[0] &&\n line.span[1] < block.span[1]\n))",
18 | "visibility": "HIDDEN"
19 | },
20 | {
21 | "name": "sortedLines",
22 | "formula": "lines.sortBy(line => line.text())",
23 | "visibility": "HIDDEN"
24 | },
25 | {
26 | "name": "button",
27 | "formula": "const text = `[\\n${sortedLines.join(\"\\n\")}\\n]`\n\nreturn TemplateButton(\n block, \"sort\", text, \"replace\"\n)",
28 | "visibility": "INLINE"
29 | }
30 | ]
31 | }
--------------------------------------------------------------------------------
/sample-data-old/workout.exercise.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "workout.exercise",
3 | "name": "exercise",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "{/bench|squat|run|elliptical/}",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "date",
12 | "formula": "PrevOfType($, \"dates\")",
13 | "visibility": "HIDDEN"
14 | },
15 | {
16 | "name": "numbers",
17 | "formula": "Filter(NextUntil($, HasType(\"exercise\")), SameLine($))",
18 | "visibility": "HIDDEN"
19 | },
20 | {
21 | "name": "weight",
22 | "formula": "First(numbers)",
23 | "visibility": "HIDDEN"
24 | },
25 | {
26 | "name": "sets",
27 | "formula": "Second(numbers)",
28 | "visibility": "HIDDEN"
29 | },
30 | {
31 | "name": "reps",
32 | "formula": "Third(numbers)",
33 | "visibility": "HIDDEN"
34 | }
35 | ]
36 | }
--------------------------------------------------------------------------------
/sample-data/plant-watering.2.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "plant-watering.2",
3 | "name": "schedule",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "{dates:lastWatered}",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "daysSince",
12 | "formula": "Round((Date.now() - Date.parse(lastWatered)) / (24 * 60 * 60 * 1000), 1)",
13 | "visibility": "HIDDEN"
14 | },
15 | {
16 | "name": "needsWater",
17 | "formula": "daysSince > PrevOfType($, \"interval\").data.intervalInt",
18 | "visibility": "HIDDEN"
19 | },
20 | {
21 | "name": "color",
22 | "formula": "needsWater ? \"red\": \"green\"",
23 | "visibility": "STYLE"
24 | },
25 | {
26 | "name": "button",
27 | "formula": "TemplateButton($, \"🚿\", DateTime.now().setLocale('en-US').toLocaleString(), \"replace\")",
28 | "visibility": "INLINE"
29 | }
30 | ]
31 | }
--------------------------------------------------------------------------------
/sample-data/default-searches.metadata:
--------------------------------------------------------------------------------
1 | {
2 | "sheets": [
3 | {
4 | "id": "_MqeMnBOqDM4Pdej2Xtrpf",
5 | "configId": "_wqlqE65AnWhAK9dX8pm14",
6 | "hideHighlightsInDocument": false
7 | },
8 | {
9 | "id": "_S9hDqoAlsHf0KIdGkXcsq",
10 | "configId": "youtube",
11 | "hideHighlightsInDocument": false
12 | },
13 | {
14 | "id": "_WX9HAV8CpFFbJdYW3JijD",
15 | "configId": "common.date",
16 | "hideHighlightsInDocument": false
17 | },
18 | {
19 | "id": "_HH6NzuEldBXPsvRUWJx7b",
20 | "configId": "markdown",
21 | "hideHighlightsInDocument": false
22 | },
23 | {
24 | "id": "_gz2GcSLR4lTZudJAKquYN",
25 | "configId": "common.duration",
26 | "hideHighlightsInDocument": false
27 | },
28 | {
29 | "id": "_sMSYj7m9SZQPciwkYsK40",
30 | "configId": "common.number",
31 | "hideHighlightsInDocument": false
32 | }
33 | ]
34 | }
--------------------------------------------------------------------------------
/sample-data-old/blocks.bla.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "blocks.bla",
3 | "name": "remove",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "⬆️",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "block",
12 | "formula": "$.prev(\"block\")",
13 | "visibility": "HIDDEN"
14 | },
15 | {
16 | "name": "lines",
17 | "formula": "SplitLines().filter(line => (\n line.span[0] > block.span[0] &&\n line.span[1] < block.span[1]\n))",
18 | "visibility": "HIDDEN"
19 | },
20 | {
21 | "name": "first",
22 | "formula": "lines[0]",
23 | "visibility": "HIDDEN"
24 | },
25 | {
26 | "name": "rest",
27 | "formula": "lines.splice(1)",
28 | "visibility": "HIDDEN"
29 | },
30 | {
31 | "name": "button",
32 | "formula": "TemplateButton(block, \"remove first\", `${first}\\n[\\n${rest.join(\"\\n\")}\\n]`, \"replace\")",
33 | "visibility": "INLINE"
34 | }
35 | ]
36 | }
--------------------------------------------------------------------------------
/sample-data-old/blocks.remove.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "blocks.remove",
3 | "name": "removeFirstBlock",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "⬆️",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "block",
12 | "formula": "$.prev(\"block\")",
13 | "visibility": "HIDDEN"
14 | },
15 | {
16 | "name": "lines",
17 | "formula": "SplitLines().filter(line => (\n line.span[0] > block.span[0] &&\n line.span[1] < block.span[1]\n))",
18 | "visibility": "HIDDEN"
19 | },
20 | {
21 | "name": "first",
22 | "formula": "lines[0]",
23 | "visibility": "HIDDEN"
24 | },
25 | {
26 | "name": "rest",
27 | "formula": "lines.splice(1)",
28 | "visibility": "HIDDEN"
29 | },
30 | {
31 | "name": "button",
32 | "formula": "TemplateButton(block, \"remove first\", `${first}\\n[\\n${rest.join(\"\\n\")}\\n]`, \"replace\")",
33 | "visibility": "INLINE"
34 | }
35 | ]
36 | }
--------------------------------------------------------------------------------
/src/data/officialFoods.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore
2 | // import officialFoods from "./officialFoodIdsFiltered.csv";
3 | // @ts-ignore
4 | // import nutrients from "./nutrient.csv";
5 | // @ts-ignore
6 | // import measureUnits from "./measure_unit.csv";
7 | // @ts-ignore
8 | import foodNutrients from "./food_nutrient_filtered.csv";
9 |
10 | export type OfficialFood = {
11 | fdc_id: string;
12 | data_type: string;
13 | description: string;
14 | food_category_id: string;
15 | publication_date: string;
16 | };
17 |
18 | export type FoodNutrient = {
19 | id: string;
20 | fdc_id: string;
21 | nutrient_id: string;
22 | amount: string;
23 | data_points: string;
24 | derivation_id: string;
25 | };
26 |
27 | export const OFFICIAL_FOODS = [] /* officialFoods */ as OfficialFood[];
28 | export const NUTRIENTS = [] /* nutrients*/ as { [key: string]: string }[];
29 | export const MEASURE_UNITS = [] /* measureUnits */ as { [key: string]: string }[];
30 | export const FOOD_NUTRIENTS = [] /* foodNutrients */ as FoodNutrient[];
31 |
--------------------------------------------------------------------------------
/sample-data/stonks-portfolio.2.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "stonks-portfolio.2",
3 | "name": "holdings",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "${/\\\\w+/:ticker} {number:shares}@${/[\\\\d\\.]+/:cost}",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "currentPrice",
12 | "formula": "TextOfHighlight(ticker) === \"GME\" ? 40 : 20",
13 | "visibility": "HIDDEN"
14 | },
15 | {
16 | "name": "total",
17 | "formula": "currentPrice * ParseInt(shares)",
18 | "visibility": "HIDDEN"
19 | },
20 | {
21 | "name": "change",
22 | "formula": "Round((currentPrice - cost) / cost, 2)",
23 | "visibility": "HIDDEN"
24 | },
25 | {
26 | "name": "changePercent",
27 | "formula": "change >= 0 ? `+${change * 100}%`\n: `${change * 100}%`",
28 | "visibility": "HIDDEN"
29 | },
30 | {
31 | "name": "col3",
32 | "formula": "`-> $${currentPrice} (${changePercent}) = ${total}`",
33 | "visibility": "INLINE"
34 | }
35 | ]
36 | }
--------------------------------------------------------------------------------
/sample-data-old/pizza-dough.water.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "pizza-dough.water",
3 | "name": "water",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "Water:",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "numberOfBalls",
12 | "formula": "FindAll(\"dough inputs\").find(h => TextOfHighlight(h.data.label).includes(\"dough balls\")).data.value",
13 | "visibility": "HIDDEN"
14 | },
15 | {
16 | "name": "water",
17 | "formula": "FindAll(\"dough inputs\").find(h => TextOfHighlight(h.data.label).includes(\"water %\")).data.value / 100",
18 | "visibility": "HIDDEN"
19 | },
20 | {
21 | "name": "gramsPerBall",
22 | "formula": "450",
23 | "visibility": "HIDDEN"
24 | },
25 | {
26 | "name": "total",
27 | "formula": "numberOfBalls * gramsPerBall * water",
28 | "visibility": "HIDDEN"
29 | },
30 | {
31 | "name": "display",
32 | "formula": "`${Math.round(total)} grams`",
33 | "visibility": "INLINE"
34 | }
35 | ]
36 | }
--------------------------------------------------------------------------------
/sample-data/pizza-dough.water.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "pizza-dough.water",
3 | "name": "water",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "Water:",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "numberOfBalls",
12 | "formula": "FindAll(\"dough inputs\").find(h => TextOfHighlight(h.data.label).includes(\"dough balls\")).data.value",
13 | "visibility": "HIDDEN"
14 | },
15 | {
16 | "name": "water",
17 | "formula": "FindAll(\"dough inputs\").find(h => TextOfHighlight(h.data.label).includes(\"water %\")).data.value / 100",
18 | "visibility": "HIDDEN"
19 | },
20 | {
21 | "name": "gramsPerBall",
22 | "formula": "450",
23 | "visibility": "HIDDEN"
24 | },
25 | {
26 | "name": "total",
27 | "formula": "numberOfBalls * gramsPerBall * water",
28 | "visibility": "HIDDEN"
29 | },
30 | {
31 | "name": "display",
32 | "formula": "`${Math.round(total)} grams`",
33 | "visibility": "INLINE"
34 | }
35 | ]
36 | }
--------------------------------------------------------------------------------
/sample-data-old/default-searches.metadata:
--------------------------------------------------------------------------------
1 | {
2 | "sheets": [
3 | {
4 | "id": "_S9hDqoAlsHf0KIdGkXcsq",
5 | "configId": "youtube",
6 | "hideHighlightsInDocument": false
7 | },
8 | {
9 | "id": "_WX9HAV8CpFFbJdYW3JijD",
10 | "configId": "common.date",
11 | "hideHighlightsInDocument": false
12 | },
13 | {
14 | "id": "_mEAGvWWAof2XNyFwmgUe0",
15 | "configId": "crm.1",
16 | "hideHighlightsInDocument": false
17 | },
18 | {
19 | "id": "_HH6NzuEldBXPsvRUWJx7b",
20 | "configId": "markdown",
21 | "hideHighlightsInDocument": false
22 | },
23 | {
24 | "id": "_r97dmT32ybw8jF0kk7bRw",
25 | "configId": "goodreads.1",
26 | "hideHighlightsInDocument": false
27 | },
28 | {
29 | "id": "_gz2GcSLR4lTZudJAKquYN",
30 | "configId": "common.duration",
31 | "hideHighlightsInDocument": false
32 | },
33 | {
34 | "id": "_sMSYj7m9SZQPciwkYsK40",
35 | "configId": "common.number",
36 | "hideHighlightsInDocument": false
37 | }
38 | ]
39 | }
--------------------------------------------------------------------------------
/sample-data-old/blocks.txt:
--------------------------------------------------------------------------------
1 | block actions
2 | # Readme
3 |
4 | You can mark a section with "[" and "]" to turn them into a block. Then you can use different emojis to add actions to that block
5 |
6 | [
7 | - E
8 | - C
9 | - B
10 | - D
11 | - A
12 | ]
13 | ➡️ collapse the block
14 | 🔤 sort the lines in the block alphabetically
15 | 🔀 shuffles the lines in the block
16 | ⬆️ remove the first item from the block
17 | 🎩 of "type" by "attribute"
18 | - sorting hat will group highlights of *type* by *attribute*
19 |
20 | # Examples
21 |
22 | ## Pick a winner for a raffle
23 |
24 | Collapse the block, then shuffle it and remove the first item to pick a random winner
25 |
26 | [
27 | - Alex
28 | - Sandra
29 | - Bob
30 | ]
31 | ➡️ 🔀 ⬆️
32 |
33 | ## Group a shopping list by category
34 |
35 | - tofu
36 | - tomatoes
37 | - 10 eggs
38 | - milk
39 | - butter
40 |
41 | [
42 | - **other**
43 | - 10 eggs
44 | - tofu
45 | - **produce**
46 | - tomatoes
47 | - **dairy**
48 | - milk
49 | - butter
50 | ]
51 | 🎩 of "exampleIngredient" by "aisle"
52 | 🎩 of "exampleIngredient" by "isVegan"
53 |
--------------------------------------------------------------------------------
/sample-data/pizza-dough.number-of-balls.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "pizza-dough.number-of-balls",
3 | "name": "flour",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "Flour:",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "numberOfBalls",
12 | "formula": "FindAll(\"dough inputs\").find(h => TextOfHighlight(h.data.label).includes(\"dough balls\")).data.value",
13 | "visibility": "HIDDEN"
14 | },
15 | {
16 | "name": "water",
17 | "formula": "FindAll(\"dough inputs\").find(h => TextOfHighlight(h.data.label).includes(\"water %\")).data.value / 100",
18 | "visibility": "HIDDEN"
19 | },
20 | {
21 | "name": "gramsPerBall",
22 | "formula": "450",
23 | "visibility": "HIDDEN"
24 | },
25 | {
26 | "name": "total",
27 | "formula": "numberOfBalls * gramsPerBall * (1 - water)",
28 | "visibility": "HIDDEN"
29 | },
30 | {
31 | "name": "display",
32 | "formula": "`${Math.round(total)} grams`",
33 | "visibility": "INLINE"
34 | }
35 | ]
36 | }
--------------------------------------------------------------------------------
/sample-data-old/pizza-dough.number-of-balls.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "pizza-dough.number-of-balls",
3 | "name": "flour",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "Flour:",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "numberOfBalls",
12 | "formula": "FindAll(\"dough inputs\").find(h => TextOfHighlight(h.data.label).includes(\"dough balls\")).data.value",
13 | "visibility": "HIDDEN"
14 | },
15 | {
16 | "name": "water",
17 | "formula": "FindAll(\"dough inputs\").find(h => TextOfHighlight(h.data.label).includes(\"water %\")).data.value / 100",
18 | "visibility": "HIDDEN"
19 | },
20 | {
21 | "name": "gramsPerBall",
22 | "formula": "450",
23 | "visibility": "HIDDEN"
24 | },
25 | {
26 | "name": "total",
27 | "formula": "numberOfBalls * gramsPerBall * (1 - water)",
28 | "visibility": "HIDDEN"
29 | },
30 | {
31 | "name": "display",
32 | "formula": "`${Math.round(total)} grams`",
33 | "visibility": "INLINE"
34 | }
35 | ]
36 | }
--------------------------------------------------------------------------------
/sample-data/meeting.metadata:
--------------------------------------------------------------------------------
1 | {
2 | "sheets": [
3 | {
4 | "id": "_YcmEbl94YLaKvCBmx4EhF",
5 | "configId": "common.duration",
6 | "hideHighlightsInDocument": false
7 | },
8 | {
9 | "id": "_9t8ilvJ7XPrDn5LM9rcou",
10 | "configId": "time-tracking.time",
11 | "hideHighlightsInDocument": false
12 | },
13 | {
14 | "id": "_JIXZaoTWFccSUrT7JkXCx",
15 | "configId": "todos.1",
16 | "hideHighlightsInDocument": false
17 | },
18 | {
19 | "id": "_vAlraHxh3rPGMefQn1BNn",
20 | "configId": "todos.2",
21 | "hideHighlightsInDocument": false
22 | },
23 | {
24 | "id": "_Pi6GccwquTRbC1uqyfAw5",
25 | "configId": "_1F0eRt4v6Onwr8fDfaXQU",
26 | "hideHighlightsInDocument": false
27 | },
28 | {
29 | "id": "_9guUeM0PS7Q7Uj5Rmtltt",
30 | "configId": "markdown",
31 | "hideHighlightsInDocument": false
32 | },
33 | {
34 | "id": "_vr6DjALacbU2J62wnuczP",
35 | "configId": "_EPSPtlS2leXEhRmSEoQZh",
36 | "hideHighlightsInDocument": false
37 | }
38 | ]
39 | }
--------------------------------------------------------------------------------
/sample-data-old/_AJ5yvielohvnk3xvY1ea5.metadata:
--------------------------------------------------------------------------------
1 | {
2 | "sheets": [
3 | {
4 | "id": "_VU6isfrUenQk3Wdg05qGc",
5 | "configId": "common.number",
6 | "hideHighlightsInDocument": false
7 | },
8 | {
9 | "id": "_YDKITIckz6nDms4vHUM8x",
10 | "configId": "common.duration",
11 | "hideHighlightsInDocument": false
12 | },
13 | {
14 | "id": "_fDC1BebE8R09VEltAN6ld",
15 | "configId": "goodreads.1",
16 | "hideHighlightsInDocument": false
17 | },
18 | {
19 | "id": "_wRX26NlxkoH38ioBxXncv",
20 | "configId": "markdown",
21 | "hideHighlightsInDocument": false
22 | },
23 | {
24 | "id": "_RwIa8qsVEEBI1yN9N9JV2",
25 | "configId": "crm.1",
26 | "hideHighlightsInDocument": false
27 | },
28 | {
29 | "id": "_eTtQylRi3bH4xBKfuZvXk",
30 | "configId": "common.date",
31 | "hideHighlightsInDocument": false
32 | },
33 | {
34 | "id": "_VZNZA4WGboyKJQgWTXZAI",
35 | "configId": "_EPSPtlS2leXEhRmSEoQZh",
36 | "hideHighlightsInDocument": false
37 | }
38 | ]
39 | }
--------------------------------------------------------------------------------
/sample-data/food.ingredient.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "food.ingredient",
3 | "name": "ingredients",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "=MatchHighlight(DataFromDoc(\"all ingredients\", \"allIngredients\", \"$\"))",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "matched",
12 | "formula": "$.data.matchedHighlight",
13 | "visibility": "HIDDEN"
14 | },
15 | {
16 | "name": "USDA Name",
17 | "formula": "USDAFoodName($)",
18 | "visibility": "HIDDEN"
19 | },
20 | {
21 | "name": "quantity",
22 | "formula": "PrevOfType($, [\"quantity\", \"number\"], 20)",
23 | "visibility": "HIDDEN"
24 | },
25 | {
26 | "name": "scaleFactor",
27 | "formula": "Find(\"scale\")?.data.sliderValue",
28 | "visibility": "HIDDEN"
29 | },
30 | {
31 | "name": "scaledQuantity",
32 | "formula": "(scaleFactor && scaleFactor !== 1 && IsNumber(quantity.valueOf())) ? `${quantity * Round(scaleFactor, 2)} ${$}` : undefined",
33 | "visibility": "REPLACE"
34 | }
35 | ]
36 | }
--------------------------------------------------------------------------------
/sample-data-old/_oHItjl0ecKP4fV4fh15L6.metadata:
--------------------------------------------------------------------------------
1 | {
2 | "sheets": [
3 | {
4 | "id": "_YcmEbl94YLaKvCBmx4EhF",
5 | "configId": "common.duration",
6 | "hideHighlightsInDocument": false
7 | },
8 | {
9 | "id": "_9t8ilvJ7XPrDn5LM9rcou",
10 | "configId": "time-tracking.time",
11 | "hideHighlightsInDocument": false
12 | },
13 | {
14 | "id": "_JIXZaoTWFccSUrT7JkXCx",
15 | "configId": "todos.1",
16 | "hideHighlightsInDocument": false
17 | },
18 | {
19 | "id": "_vAlraHxh3rPGMefQn1BNn",
20 | "configId": "todos.2",
21 | "hideHighlightsInDocument": false
22 | },
23 | {
24 | "id": "_Pi6GccwquTRbC1uqyfAw5",
25 | "configId": "_1F0eRt4v6Onwr8fDfaXQU",
26 | "hideHighlightsInDocument": false
27 | },
28 | {
29 | "id": "_9guUeM0PS7Q7Uj5Rmtltt",
30 | "configId": "markdown",
31 | "hideHighlightsInDocument": false
32 | },
33 | {
34 | "id": "_vr6DjALacbU2J62wnuczP",
35 | "configId": "_EPSPtlS2leXEhRmSEoQZh",
36 | "hideHighlightsInDocument": false
37 | }
38 | ]
39 | }
--------------------------------------------------------------------------------
/sample-data-old/food.ingredient.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "food.ingredient",
3 | "name": "ingredients",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "=MatchHighlight(DataFromDoc(\"all ingredients\", \"allIngredients\", \"$\"))",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "matched",
12 | "formula": "$.data.matchedHighlight",
13 | "visibility": "HIDDEN"
14 | },
15 | {
16 | "name": "USDA Name",
17 | "formula": "USDAFoodName($)",
18 | "visibility": "HIDDEN"
19 | },
20 | {
21 | "name": "quantity",
22 | "formula": "PrevOfType($, [\"quantity\", \"number\"], 20)",
23 | "visibility": "HIDDEN"
24 | },
25 | {
26 | "name": "scaleFactor",
27 | "formula": "Find(\"scale\")?.data.sliderValue",
28 | "visibility": "HIDDEN"
29 | },
30 | {
31 | "name": "scaledQuantity",
32 | "formula": "(scaleFactor && scaleFactor !== 1 && IsNumber(quantity.valueOf())) ? `${quantity * Round(scaleFactor, 2)} ${$}` : undefined",
33 | "visibility": "REPLACE"
34 | }
35 | ]
36 | }
--------------------------------------------------------------------------------
/sample-data/time-tracking.metadata:
--------------------------------------------------------------------------------
1 | {
2 | "sheets": [
3 | {
4 | "id": "_EB3s0twrdgiD4cKCOxxfI",
5 | "configId": "_p8Ot2ukI8aaITNa5bY5JU",
6 | "hideHighlightsInDocument": false
7 | },
8 | {
9 | "id": "_B9uvfnlj7FdGpKkSVm1P9",
10 | "configId": "_nzNLA8UWxTsnQ01510U1e",
11 | "hideHighlightsInDocument": false
12 | },
13 | {
14 | "id": "_Wsf3s8IyOld9knDg1rdnC",
15 | "configId": "common.number",
16 | "hideHighlightsInDocument": false
17 | },
18 | {
19 | "id": "_1Co3scDP2mNoUzFoYltQ5",
20 | "configId": "markdown",
21 | "hideHighlightsInDocument": false
22 | },
23 | {
24 | "id": "_H7EWwGZc6qecFzGl0UTWc",
25 | "configId": "time-tracking.project",
26 | "hideHighlightsInDocument": false
27 | },
28 | {
29 | "id": "_mMM1kgsMOjd4Jtwadj75F",
30 | "configId": "time-tracking.time",
31 | "hideHighlightsInDocument": false
32 | },
33 | {
34 | "id": "_2gPgKJTGW7qDrcqP7ofMb",
35 | "configId": "time-tracking.open-time",
36 | "hideHighlightsInDocument": false
37 | }
38 | ]
39 | }
--------------------------------------------------------------------------------
/sample-data-old/bar.sales.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "bar.sales",
3 | "name": "sales",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "{/^/}{emoji+:items}{/$/}",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "prices",
12 | "formula": "items.map(item => (\n item\n .allPrev(\"price\")\n .find(price => (\n price.data.item.isEqualTo(item)\n ))\n))",
13 | "visibility": "HIDDEN"
14 | },
15 | {
16 | "name": "sum",
17 | "formula": "prices.sumOf(\"data.price\")",
18 | "visibility": "HIDDEN"
19 | },
20 | {
21 | "name": "sumText",
22 | "formula": "`= ${sum} Euro`",
23 | "visibility": "INLINE"
24 | },
25 | {
26 | "name": "change",
27 | "formula": "let result = []\n\nif (sum < 5) {\n result.push(` 5 → ${5 - sum}`)\n}\n\nif (sum < 10) {\n result.push(` 10 → ${10 - sum}`)\n}\n\nreturn result.join(', ')",
28 | "visibility": "HIDDEN"
29 | },
30 | {
31 | "name": "col5",
32 | "formula": "HasCursorFocus($) ? change : undefined",
33 | "visibility": "HIDDEN"
34 | }
35 | ]
36 | }
--------------------------------------------------------------------------------
/sample-data/workout.workout.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "workout.workout",
3 | "name": "workouts",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "{/squat|bench|rowing|triceps|elliptical/}",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "number",
12 | "formula": "Filter(NextUntil($, HasType(\"workouts\")), SameLine($))",
13 | "visibility": "HIDDEN"
14 | },
15 | {
16 | "name": "weight",
17 | "formula": "First(number)",
18 | "visibility": "HIDDEN"
19 | },
20 | {
21 | "name": "reps",
22 | "formula": "Second(number)",
23 | "visibility": "HIDDEN"
24 | },
25 | {
26 | "name": "sets",
27 | "formula": "Third(number)",
28 | "visibility": "HIDDEN"
29 | },
30 | {
31 | "name": "date",
32 | "formula": "PrevOfType($, \"dates\")",
33 | "visibility": "HIDDEN"
34 | },
35 | {
36 | "name": "total",
37 | "formula": "reps * sets",
38 | "visibility": "HIDDEN"
39 | },
40 | {
41 | "name": "nextWeight",
42 | "formula": "weight + 5",
43 | "visibility": "HIDDEN"
44 | }
45 | ]
46 | }
--------------------------------------------------------------------------------
/sample-data-old/workout.workout.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "workout.workout",
3 | "name": "workouts",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "{/squat|bench|rowing|triceps|elliptical/}",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "number",
12 | "formula": "Filter(NextUntil($, HasType(\"workouts\")), SameLine($))",
13 | "visibility": "HIDDEN"
14 | },
15 | {
16 | "name": "weight",
17 | "formula": "First(number)",
18 | "visibility": "HIDDEN"
19 | },
20 | {
21 | "name": "reps",
22 | "formula": "Second(number)",
23 | "visibility": "HIDDEN"
24 | },
25 | {
26 | "name": "sets",
27 | "formula": "Third(number)",
28 | "visibility": "HIDDEN"
29 | },
30 | {
31 | "name": "date",
32 | "formula": "PrevOfType($, \"dates\")",
33 | "visibility": "HIDDEN"
34 | },
35 | {
36 | "name": "total",
37 | "formula": "reps * sets",
38 | "visibility": "HIDDEN"
39 | },
40 | {
41 | "name": "nextWeight",
42 | "formula": "weight + 5",
43 | "visibility": "HIDDEN"
44 | }
45 | ]
46 | }
--------------------------------------------------------------------------------
/sample-data/math-pack.formula.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "math-pack.formula",
3 | "name": "formula",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "=SplitLines()",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "numbers",
12 | "formula": "Filter(FindAll(\"number\"), SameLine($))",
13 | "visibility": "HIDDEN"
14 | },
15 | {
16 | "name": "operators",
17 | "formula": "Filter(FindAll(\"operator\"), SameLine($))",
18 | "visibility": "HIDDEN"
19 | },
20 | {
21 | "name": "formula",
22 | "formula": "numbers.concat(operators)\n .sortBy(h => h.span[0])\n .map(h => h.text())\n .join(\" \")",
23 | "visibility": "HIDDEN"
24 | },
25 | {
26 | "name": "result",
27 | "formula": "eval(formula)",
28 | "visibility": "HIDDEN"
29 | },
30 | {
31 | "name": "showResult",
32 | "formula": "operators.length >= 1 && numbers.length >= 2 &&\nresult !== undefined",
33 | "visibility": "HIDDEN"
34 | },
35 | {
36 | "name": "sticker",
37 | "formula": "showResult ? `= ${result}` : \"\"",
38 | "visibility": "INLINE"
39 | }
40 | ]
41 | }
--------------------------------------------------------------------------------
/sample-data-old/math-pack.formula.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "math-pack.formula",
3 | "name": "formula",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "=SplitLines()",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "numbers",
12 | "formula": "Filter(FindAll(\"number\"), SameLine($))",
13 | "visibility": "HIDDEN"
14 | },
15 | {
16 | "name": "operators",
17 | "formula": "Filter(FindAll(\"operator\"), SameLine($))",
18 | "visibility": "HIDDEN"
19 | },
20 | {
21 | "name": "formula",
22 | "formula": "numbers.concat(operators)\n .sortBy(h => h.span[0])\n .map(h => h.text())\n .join(\" \")",
23 | "visibility": "HIDDEN"
24 | },
25 | {
26 | "name": "result",
27 | "formula": "eval(formula)",
28 | "visibility": "HIDDEN"
29 | },
30 | {
31 | "name": "showResult",
32 | "formula": "operators.length >= 1 && numbers.length >= 2 &&\nresult !== undefined",
33 | "visibility": "HIDDEN"
34 | },
35 | {
36 | "name": "sticker",
37 | "formula": "showResult ? `= ${result}` : \"\"",
38 | "visibility": "INLINE"
39 | }
40 | ]
41 | }
--------------------------------------------------------------------------------
/sample-data/chilli.txt:
--------------------------------------------------------------------------------
1 | Todd's Chili
2 | ## Ingredients
3 |
4 | ## Recipe
5 | Bring 2 pounds low fat (~90/10) ground chuck beef to room temperature, and season with 1 Tbsp onion powder, 2 tsp salt, and 3/8 tsp garlic powder.
6 |
7 | Warm bacon fat or cooking oil in large pot over high heat, and add seasoned meat. Break meat into small pieces, and stir until meat is browned and liquid becomes gravy-like.
8 |
9 | Add 12oz ipa beer or hop water, 8oz can tomato sauce/puree, 3 Tbl ground ancho chili powder, 1 tsp ground cumin, 1 tsp paprika, 1 tsp unsweetened cocoa powder, 1/4 tsp dried oregano, 1/4 tsp ground cayenne pepper, and 1/8 tsp ground cinnamon to meat mixture, and simmer over low heat for 2-3 hours, stirring regularly.
10 |
11 | Add 1/8 Cup diced poblano peppers to mixture, and continue to simmer for 2 hours, stirring regularly.
12 |
13 | Optionally rinse 1 can red kidney beans and 1 can black beans with water and drain. Gently stir beans into mixture, keeping the beans intact.
14 |
15 | Simmer on low until liquid as evaporated. Chili is ready once flavors are blended and texture is to your liking.
16 |
17 | Serve in bowl and garnish to taste with grated cheddar, avocado, sour cream, jalapeño, salsa, tortilla chips, Fritos, or corn bread.
--------------------------------------------------------------------------------
/sample-data-old/blocks.metadata:
--------------------------------------------------------------------------------
1 | {
2 | "sheets": [
3 | {
4 | "id": "_YR7856hxyyG2NKZsJAzws",
5 | "configId": "blocks.remove",
6 | "hideHighlightsInDocument": false
7 | },
8 | {
9 | "id": "_saVunZsu1B5sKwfqSIkGb",
10 | "configId": "blocks.block",
11 | "hideHighlightsInDocument": false
12 | },
13 | {
14 | "id": "_I9PHjLNehuiyq4vYgEYdm",
15 | "configId": "blocks.collapse",
16 | "hideHighlightsInDocument": false
17 | },
18 | {
19 | "id": "_AP3bdNza4OSrHgfB8V2wz",
20 | "configId": "blocks.shuffle",
21 | "hideHighlightsInDocument": false
22 | },
23 | {
24 | "id": "_oqy9W8cxf0i1NQD6xhUdb",
25 | "configId": "blocks.sort",
26 | "hideHighlightsInDocument": false
27 | },
28 | {
29 | "id": "_ZJYg11klR2KoVhlWmeL2W",
30 | "configId": "blocks.sorting-hat",
31 | "hideHighlightsInDocument": false
32 | },
33 | {
34 | "id": "_CPg91EyQ0EVBMmTCFAGt0",
35 | "configId": "blocks.example-ingredient",
36 | "hideHighlightsInDocument": false
37 | },
38 | {
39 | "id": "_O2LJ8Q1oZ5OV271idEx3m",
40 | "configId": "markdown",
41 | "hideHighlightsInDocument": false
42 | }
43 | ]
44 | }
--------------------------------------------------------------------------------
/sample-data-old/_iiy0UQcJ4SleIwm7XOkYg.txt:
--------------------------------------------------------------------------------
1 | Todd's Chili
2 | ## Ingredients
3 |
4 | ## Recipe
5 | Bring 2 pounds low fat (~90/10) ground chuck beef to room temperature, and season with 1 Tbsp onion powder, 2 tsp salt, and 3/8 tsp garlic powder.
6 |
7 | Warm bacon fat or cooking oil in large pot over high heat, and add seasoned meat. Break meat into small pieces, and stir until meat is browned and liquid becomes gravy-like.
8 |
9 | Add 12oz ipa beer or hop water, 8oz can tomato sauce/puree, 3 Tbl ground ancho chili powder, 1 tsp ground cumin, 1 tsp paprika, 1 tsp unsweetened cocoa powder, 1/4 tsp dried oregano, 1/4 tsp ground cayenne pepper, and 1/8 tsp ground cinnamon to meat mixture, and simmer over low heat for 2-3 hours, stirring regularly.
10 |
11 | Add 1/8 Cup diced poblano peppers to mixture, and continue to simmer for 2 hours, stirring regularly.
12 |
13 | Optionally rinse 1 can red kidney beans and 1 can black beans with water and drain. Gently stir beans into mixture, keeping the beans intact.
14 |
15 | Simmer on low until liquid as evaporated. Chili is ready once flavors are blended and texture is to your liking.
16 |
17 | Serve in bowl and garnish to taste with grated cheddar, avocado, sour cream, jalapeño, salsa, tortilla chips, Fritos, or corn bread.
--------------------------------------------------------------------------------
/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig, ViteDevServer } from "vite";
2 | import react from "@vitejs/plugin-react";
3 | import dsv from "@rollup/plugin-dsv";
4 | import { resolve } from 'path';
5 | import analyze from 'rollup-plugin-analyzer'
6 |
7 | // The Vite Chokidar overrides don't actually work unless you write a plugin.
8 | // This plugin overwrites the `ignored` directories for live-reloading. We do
9 | // this to avoid reloads on changes to `sample-data/`.
10 | //
11 | // https://github.com/vitejs/vite/issues/8341
12 | const ignored = () => {
13 | return {
14 | name: "ignored-overrides",
15 | configureServer: (server: ViteDevServer): void => {
16 | server.watcher.options = {
17 | ...server.watcher.options,
18 | ignored: ["**/.git/**", "**/node_modules/**", "**/sample-data/**"],
19 | };
20 | },
21 | };
22 | };
23 |
24 | // https://vitejs.dev/config/
25 | export default defineConfig({
26 | plugins: [react(), dsv(), ignored(), {...analyze(), apply: "build"}],
27 | base: '',
28 | server: {
29 | host: true
30 | },
31 | build: {
32 | rollupOptions: {
33 | input: {
34 | main: resolve(__dirname, "index.html"),
35 | embedded: resolve(__dirname, "embedded.html")
36 | }
37 | }
38 | }
39 | });
40 |
--------------------------------------------------------------------------------
/sample-data-old/blocks.sorting-hat.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "blocks.sorting-hat",
3 | "name": "sortingHat",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "🎩 of {/\"[^\"]*\"/:a} by {/\"[^\"]*\"/:b}",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "type",
12 | "formula": "a.text().slice(1, -1)",
13 | "visibility": "HIDDEN"
14 | },
15 | {
16 | "name": "attr",
17 | "formula": "b.text().slice(1, -1)",
18 | "visibility": "HIDDEN"
19 | },
20 | {
21 | "name": "block",
22 | "formula": "$.prev(\"block\")",
23 | "visibility": "HIDDEN"
24 | },
25 | {
26 | "name": "groupedItems",
27 | "formula": "return FindAll(type).filter(h => (\n h.span[0] > block.span[0] &&\n h.span[1] < block.span[1]\n ))\n .groupBy(h => h.data[attr])\n .map(({group, items}) => {\n const heading = `- **${group}**`\n\n return(\n `${heading}\\n${items.map((item) => (\n ` ${item.wholeLine().text().trimStart()}`\n )).join('\\n')}`\n )\n })\n.join(\"\\n\")\n",
28 | "visibility": "HIDDEN"
29 | },
30 | {
31 | "name": "button",
32 | "formula": "TemplateButton(block, \"group\", `[\\n${groupedItems}\\n]`, \"replace\")",
33 | "visibility": "INLINE"
34 | }
35 | ]
36 | }
--------------------------------------------------------------------------------
/sample-data/meeting.txt:
--------------------------------------------------------------------------------
1 | Potluck Weekly Meeting 8/15
2 | ## agenda
3 | Start at SOC 2:00
4 |
5 | 15 minutes chit chat
6 | 60 minutes minutes goals for the week
7 | - Start writing!
8 | - Get more usage: more user tests + larger group session
9 | - Polish + bugfixes
10 | - [ ] @max make the formula + pattern bars look more different
11 |
12 | other topics:
13 | - Related Work
14 | - Potluck + AI
15 |
16 | 15 minutes sync
17 |
18 | ## coffee demo
19 | - it has computed properties + widgets, doesn't have formatting / buttons
20 | - it's pretty involved. make a simpler first step? unit conversion?
21 | - maybe hardcoded scaling is simple enough.
22 |
23 | ## javascript??
24 | - is it just incidental? or do we lean into the familiarity?
25 |
26 | ## next steps
27 |
28 | - intro: @shen
29 | - design principles: @geoffrey
30 | - demo @max
31 | - findings @sonnentag: 4-6 findings, one sentence per
32 | - related work: @geoffrey (maybe @shen helps)
33 |
34 | Goal: legible artifact by Friday
35 | Non-goal: uniformity of prose
36 |
37 | ## other notes
38 |
39 | - Shen is out Thurs afternoon ~ this week
40 | - chatting w/ Nathan on Tues
41 |
42 | ## instructions for using this doc
43 | Team member names get highlighted: @shen @sonnentag @max @geoffrey
44 | Put your own name here to extra-highlight just your name specifically: @geoffrey (doesn't work, some bug in formulas)
--------------------------------------------------------------------------------
/sample-data-old/_oHItjl0ecKP4fV4fh15L6.txt:
--------------------------------------------------------------------------------
1 | Potluck Weekly Meeting 8/15
2 | ## agenda
3 | Start at SOC 2:00
4 |
5 | 15 minutes chit chat
6 | 60 minutes minutes goals for the week
7 | - Start writing!
8 | - Get more usage: more user tests + larger group session
9 | - Polish + bugfixes
10 | - [ ] @max make the formula + pattern bars look more different
11 |
12 | other topics:
13 | - Related Work
14 | - Potluck + AI
15 |
16 | 15 minutes sync
17 |
18 | ## coffee demo
19 | - it has computed properties + widgets, doesn't have formatting / buttons
20 | - it's pretty involved. make a simpler first step? unit conversion?
21 | - maybe hardcoded scaling is simple enough.
22 |
23 | ## javascript??
24 | - is it just incidental? or do we lean into the familiarity?
25 |
26 | ## next steps
27 |
28 | - intro: @shen
29 | - design principles: @geoffrey
30 | - demo @max
31 | - findings @sonnentag: 4-6 findings, one sentence per
32 | - related work: @geoffrey (maybe @shen helps)
33 |
34 | Goal: legible artifact by Friday
35 | Non-goal: uniformity of prose
36 |
37 | ## other notes
38 |
39 | - Shen is out Thurs afternoon ~ this week
40 | - chatting w/ Nathan on Tues
41 |
42 | ## instructions for using this doc
43 | Team member names get highlighted: @shen @sonnentag @max @geoffrey
44 | Put your own name here to extra-highlight just your name specifically: @geoffrey (doesn't work, some bug in formulas)
--------------------------------------------------------------------------------
/sample-data/chilli.metadata:
--------------------------------------------------------------------------------
1 | {
2 | "sheets": [
3 | {
4 | "id": "_rBNrhouCsIKScVW4rzoUP",
5 | "configId": "_qvqxfvCRfMf4jMbieqxcv",
6 | "hideHighlightsInDocument": false
7 | },
8 | {
9 | "id": "_ei1bdWIXkCgYHQLqEU8Pp",
10 | "configId": "food.ingredient",
11 | "hideHighlightsInDocument": false
12 | },
13 | {
14 | "id": "_OMjCFGo4XlMo8EgXzJbqQ",
15 | "configId": "common.duration",
16 | "hideHighlightsInDocument": false
17 | },
18 | {
19 | "id": "_8l4M46VNQLL2fS424kG6E",
20 | "configId": "food.quantity",
21 | "hideHighlightsInDocument": false
22 | },
23 | {
24 | "id": "_QeVX2HaMkKRUOIpwLEER5",
25 | "configId": "food.scale",
26 | "hideHighlightsInDocument": false
27 | },
28 | {
29 | "id": "_9ZHb9D3b2xcrg4ZPoZ7xX",
30 | "configId": "common.number",
31 | "hideHighlightsInDocument": false
32 | },
33 | {
34 | "id": "_9yOMzkh3EZ2E1dHYo3yg8",
35 | "configId": "common.duration",
36 | "hideHighlightsInDocument": false
37 | },
38 | {
39 | "id": "_KHJTAm55FCUQOnhGIpcHz",
40 | "configId": "markdown",
41 | "hideHighlightsInDocument": false
42 | },
43 | {
44 | "id": "_e7JegGTX8hQPnwfM8HeAU",
45 | "configId": "common.date",
46 | "hideHighlightsInDocument": false
47 | }
48 | ]
49 | }
--------------------------------------------------------------------------------
/sample-data-old/tasktxt.4.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "tasktxt.4",
3 | "name": "task",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "[ ]",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "duration",
12 | "formula": "First(Filter(FindAll(\"duration\"), SameLine))",
13 | "visibility": "HIDDEN"
14 | },
15 | {
16 | "name": "startTime",
17 | "formula": "duration?.data.startTime",
18 | "visibility": "HIDDEN"
19 | },
20 | {
21 | "name": "secondsElapsed",
22 | "formula": "startTime !== undefined\n ? DateTime.fromJSDate(NowDate()).diff(DateTime.fromISO(TextOfHighlight(startTime).substring(1, TextOfHighlight(startTime).length - 1))).as(\"seconds\")\n : undefined",
23 | "visibility": "HIDDEN"
24 | },
25 | {
26 | "name": "totalSeconds",
27 | "formula": "duration !== undefined ? ParseInt(duration.data.number) : undefined",
28 | "visibility": "HIDDEN"
29 | },
30 | {
31 | "name": "col9",
32 | "formula": "secondsElapsed !== undefined ? `${Round(totalSeconds - secondsElapsed)}s left` : undefined",
33 | "visibility": "SUPERSCRIPT"
34 | },
35 | {
36 | "name": "col12",
37 | "formula": "startTime !== undefined ? TemplateButton($, \"done\", \"[x]\", \"replace\") : undefined",
38 | "visibility": "INLINE"
39 | }
40 | ]
41 | }
--------------------------------------------------------------------------------
/src/EmbeddedDocument.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import {useState, useEffect} from "react";
3 | import {observer} from "mobx-react-lite";
4 | import "./index.css";
5 | import classNames from "classnames";
6 | import {ToastViewport} from "@radix-ui/react-toast";
7 | import {selectedTextDocumentIdBox} from "./primitives";
8 | import {Editor} from "./Editor"
9 | import {loadDocumentExport} from "./App";
10 |
11 | export const EmbeddedDocument = observer(({url}: { url: string }) => {
12 | const [isLoading, setIsLoading] = useState(true)
13 |
14 | useEffect(() => {
15 | setIsLoading(true)
16 |
17 | fetch(url)
18 | .then((res) => res.text())
19 | .then((documentExport) => {
20 | loadDocumentExport(eval(`(() => { return ${documentExport} })()`), true)
21 | setIsLoading(false)
22 | })
23 |
24 | }, [url])
25 |
26 | if (isLoading) {
27 | return
28 | }
29 |
30 | const documentId = selectedTextDocumentIdBox.get()
31 |
32 | return (
33 | <>
34 |
40 |
41 |
42 |
43 | >
44 | )
45 | })
46 |
--------------------------------------------------------------------------------
/sample-data-old/trip-plan.metadata:
--------------------------------------------------------------------------------
1 | {
2 | "sheets": [
3 | {
4 | "id": "_InLcuS3AkMLP7bsKI5v6N",
5 | "configId": "trip-plan.activity",
6 | "hideHighlightsInDocument": false
7 | },
8 | {
9 | "id": "_QgP1hr72eFZyXOKb6GBwM",
10 | "configId": "trip-plan.city",
11 | "hideHighlightsInDocument": false
12 | },
13 | {
14 | "id": "_rHwaFAMcGPKOYFHMzGvaJ",
15 | "configId": "common.number",
16 | "hideHighlightsInDocument": false
17 | },
18 | {
19 | "id": "_nAjrSj3fnSHKYYvocIVGd",
20 | "configId": "markdown",
21 | "hideHighlightsInDocument": false
22 | },
23 | {
24 | "id": "_VDr76rWqTSaAMmFNLpru5",
25 | "configId": "trip-plan.day",
26 | "hideHighlightsInDocument": false
27 | },
28 | {
29 | "id": "_NKhtuqG94fi030KZVPz1J",
30 | "configId": "trip-plan.openingRange",
31 | "hideHighlightsInDocument": false
32 | },
33 | {
34 | "id": "_Y179IOu73uTbYWrdxv9bt",
35 | "configId": "trip-plan.time",
36 | "hideHighlightsInDocument": false
37 | },
38 | {
39 | "id": "_yqGX28lejuaacUHpNFDBh",
40 | "configId": "trip-plan.timeSpan",
41 | "hideHighlightsInDocument": false
42 | },
43 | {
44 | "id": "_WqCbdCTLROphJfThUc0r9",
45 | "configId": "trip-plan.openingHours",
46 | "hideHighlightsInDocument": false
47 | }
48 | ]
49 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Potluck: Dynamic Documents as Personal Software
2 |
3 | 
4 |
5 | Today, personal computing is organized around apps: large prefabricated units of software developed by professionals for the masses, with few opportunities for customization. How might we reorient computing so that people can deeply tailor software to meet their unique needs?
6 |
7 | We think a promising workflow is gradual enrichment from docs to apps: starting with regular text documents and incrementally evolving them into interactive software. Potluck is a research prototype that supports this workflow. Users can create live searches that extract structured information from freeform text, write formulas that compute with that information, and then display the results as dynamic annotations in the original document.
8 |
9 | We’ve found that Potluck is versatile enough to build personal tools for managing recipes, workouts, household chores, and more. We share some findings from building these tools, and envision a computational environment where these primitives help people grow documents into personal tools.
10 |
11 | - [**Live demo**](https://www.inkandswitch.com/potluck/demo/)
12 | - [**Research essay**](https://www.inkandswitch.com/potluck/)
13 |
14 | This repo has the code for the Potluck prototype. It's quite usable for a prototype but it's not a polished product or actively maintained.
15 |
--------------------------------------------------------------------------------
/sample-data-old/workshop.duration.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "workshop.duration",
3 | "name": "agendaDuration",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "{number:minutes} minutes",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "prevDurations",
12 | "formula": "AllPrevOfType($, \"agendaDuration\")",
13 | "visibility": "HIDDEN"
14 | },
15 | {
16 | "name": "prevDateTime",
17 | "formula": "DateTime.fromObject({\n hours: ParseInt($.prev(\"time\").data.hours),\n minutes: ParseInt($.prev(\"time\").data.minutes)\n})",
18 | "visibility": "HIDDEN"
19 | },
20 | {
21 | "name": "startTime",
22 | "formula": "prevDurations\n .reduce((dateTime, duration) => (\n dateTime.plus({\n minutes: ParseInt(duration.data.minutes)\n })\n ),\n prevDateTime)",
23 | "visibility": "HIDDEN"
24 | },
25 | {
26 | "name": "endTime",
27 | "formula": "startTime.plus({ \n minutes: ParseInt(minutes)\n})",
28 | "visibility": "HIDDEN"
29 | },
30 | {
31 | "name": "startTimeText",
32 | "formula": "startTime.toLocaleString(DateTime.TIME_SIMPLE)",
33 | "visibility": "HIDDEN"
34 | },
35 | {
36 | "name": "endTimeText",
37 | "formula": "endTime.toLocaleString(DateTime.TIME_SIMPLE)",
38 | "visibility": "HIDDEN"
39 | },
40 | {
41 | "name": "durationText",
42 | "formula": "`${startTimeText} - ${endTimeText}`",
43 | "visibility": "REPLACE"
44 | }
45 | ]
46 | }
--------------------------------------------------------------------------------
/sample-data/workshop.duration.highlighter:
--------------------------------------------------------------------------------
1 | {
2 | "id": "workshop.duration",
3 | "name": "agendaDuration",
4 | "properties": [
5 | {
6 | "name": "$",
7 | "formula": "{number:minutes} minutes",
8 | "visibility": "HIDDEN"
9 | },
10 | {
11 | "name": "prevDurations",
12 | "formula": "AllPrevOfType($, \"agendaDuration\")",
13 | "visibility": "HIDDEN"
14 | },
15 | {
16 | "name": "prevDateTime",
17 | "formula": "DateTime.fromObject({\n hours: ParseInt($.prev(\"time\").data.hours),\n minutes: ParseInt($.prev(\"time\").data.minutes)\n})",
18 | "visibility": "HIDDEN"
19 | },
20 | {
21 | "name": "startTime",
22 | "formula": "prevDurations\n .reduce((dateTime, duration) => (\n dateTime.plus({\n minutes: ParseInt(duration.data.minutes)\n })\n ),\n prevDateTime)",
23 | "visibility": "HIDDEN"
24 | },
25 | {
26 | "name": "endTime",
27 | "formula": "startTime.plus({ \n minutes: ParseInt(minutes)\n})",
28 | "visibility": "HIDDEN"
29 | },
30 | {
31 | "name": "startTimeText",
32 | "formula": "startTime.toLocaleString(DateTime.TIME_SIMPLE)",
33 | "visibility": "HIDDEN"
34 | },
35 | {
36 | "name": "endTimeText",
37 | "formula": "endTime.toLocaleString(DateTime.TIME_SIMPLE)",
38 | "visibility": "HIDDEN"
39 | },
40 | {
41 | "name": "durationText",
42 | "formula": "`${startTimeText} - ${endTimeText}`",
43 | "visibility": "REPLACE"
44 | }
45 | ]
46 | }
--------------------------------------------------------------------------------
/embedded-test.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
8 |
9 |
10 |
11 |
12 |
13 | Potluck
14 |
15 |
16 |
17 |
18 |
19 |
21 |
22 |
23 |
25 |
26 |
27 |
29 |
30 |
31 |
32 |
34 |
35 |
36 |
37 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/sample-data-old/_iiy0UQcJ4SleIwm7XOkYg.metadata:
--------------------------------------------------------------------------------
1 | {
2 | "sheets": [
3 | {
4 | "id": "_rBNrhouCsIKScVW4rzoUP",
5 | "configId": "_qvqxfvCRfMf4jMbieqxcv",
6 | "hideHighlightsInDocument": false
7 | },
8 | {
9 | "id": "_ei1bdWIXkCgYHQLqEU8Pp",
10 | "configId": "food.ingredient",
11 | "hideHighlightsInDocument": false
12 | },
13 | {
14 | "id": "_OMjCFGo4XlMo8EgXzJbqQ",
15 | "configId": "common.duration",
16 | "hideHighlightsInDocument": false
17 | },
18 | {
19 | "id": "_8l4M46VNQLL2fS424kG6E",
20 | "configId": "food.quantity",
21 | "hideHighlightsInDocument": false
22 | },
23 | {
24 | "id": "_QeVX2HaMkKRUOIpwLEER5",
25 | "configId": "food.scale",
26 | "hideHighlightsInDocument": false
27 | },
28 | {
29 | "id": "_9ZHb9D3b2xcrg4ZPoZ7xX",
30 | "configId": "common.number",
31 | "hideHighlightsInDocument": false
32 | },
33 | {
34 | "id": "_9yOMzkh3EZ2E1dHYo3yg8",
35 | "configId": "common.duration",
36 | "hideHighlightsInDocument": false
37 | },
38 | {
39 | "id": "_9I4ok3wsBr37d5mtoyLwZ",
40 | "configId": "goodreads.1",
41 | "hideHighlightsInDocument": false
42 | },
43 | {
44 | "id": "_KHJTAm55FCUQOnhGIpcHz",
45 | "configId": "markdown",
46 | "hideHighlightsInDocument": false
47 | },
48 | {
49 | "id": "_vguDUU4gpZbEVkeyoGDsN",
50 | "configId": "crm.1",
51 | "hideHighlightsInDocument": false
52 | },
53 | {
54 | "id": "_e7JegGTX8hQPnwfM8HeAU",
55 | "configId": "common.date",
56 | "hideHighlightsInDocument": false
57 | }
58 | ]
59 | }
--------------------------------------------------------------------------------
/src/assets/whiteout.svg:
--------------------------------------------------------------------------------
1 |
25 |
--------------------------------------------------------------------------------
/documents/todos.json:
--------------------------------------------------------------------------------
1 | {
2 | "textDocument": {
3 | "id": "todos",
4 | "name": "natto todos",
5 | "text": [
6 | "simple todo list with conditional formatting",
7 | "",
8 | "[ ] make a gallery view",
9 | "[ ] announce multiplayer 08/15/22",
10 | "[ ] add keyboard shortcuts",
11 | "[x] add zoom ability"
12 | ],
13 | "sheets": [
14 | {
15 | "id": "_Do0kWwRB2norSG0x4paId",
16 | "configId": "todos.1",
17 | "hideHighlightsInDocument": false
18 | },
19 | {
20 | "id": "_LyD5a1lA4LFoMpVDuEz2g",
21 | "configId": "todos.2",
22 | "hideHighlightsInDocument": false
23 | }
24 | ]
25 | },
26 | "sheetConfigs": [
27 | {
28 | "id": "todos.1",
29 | "name": "completed",
30 | "properties": [
31 | {
32 | "name": "$",
33 | "formula": "[x]",
34 | "visibility": "HIDDEN"
35 | },
36 | {
37 | "name": "task",
38 | "formula": "TextAfter($)",
39 | "visibility": "HIDDEN"
40 | },
41 | {
42 | "name": "text-decoration",
43 | "formula": "\"line-through\"",
44 | "visibility": "STYLE"
45 | }
46 | ]
47 | },
48 | {
49 | "id": "todos.2",
50 | "name": "incomplete",
51 | "properties": [
52 | {
53 | "name": "$",
54 | "formula": "[ ]",
55 | "visibility": "HIDDEN"
56 | },
57 | {
58 | "name": "task",
59 | "formula": "TextAfter($)",
60 | "visibility": "HIDDEN"
61 | },
62 | {
63 | "name": "duedate",
64 | "formula": "Filter(FindAll(\"dates\"), a => SameLine($, a))",
65 | "visibility": "HIDDEN"
66 | }
67 | ]
68 | }
69 | ]
70 | }
--------------------------------------------------------------------------------
/src/NumberSliderComponent.tsx:
--------------------------------------------------------------------------------
1 | import { action, computed, makeObservable, observable } from "mobx";
2 | import { observer } from "mobx-react-lite";
3 | import { generateNanoid } from "./utils";
4 |
5 | type NumberSliderState = {
6 | value: number;
7 | };
8 |
9 | const NumberSlider = observer(
10 | ({
11 | state,
12 | range,
13 | }: {
14 | state: NumberSliderState;
15 | range: [from: number, to: number];
16 | }) => {
17 | return (
18 |
19 | {
25 | state.value = parseFloat(e.target.value);
26 | })}
27 | className="h-1"
28 | />
29 |
30 | );
31 | }
32 | );
33 |
34 | class NumberSliderComponentData {
35 | constructor(readonly state: NumberSliderState) {
36 | makeObservable(this, {
37 | value: computed,
38 | });
39 | }
40 |
41 | get value() {
42 | return this.state.value;
43 | }
44 | }
45 |
46 | export class NumberSliderComponent {
47 | id = generateNanoid();
48 | state: NumberSliderState;
49 | data: NumberSliderComponentData;
50 |
51 | constructor(initialValue: number, readonly range: [number, number]) {
52 | this.state = observable({
53 | value: initialValue,
54 | });
55 | this.data = new NumberSliderComponentData(this.state);
56 | }
57 |
58 | render() {
59 | return ;
60 | }
61 |
62 | destroy() {}
63 | }
64 |
65 | export function createNumberSliderComponent(
66 | initialValue: number
67 | ): NumberSliderComponent {
68 | return new NumberSliderComponent(initialValue, [0, 10]);
69 | }
70 |
--------------------------------------------------------------------------------
/documents/stonks.json:
--------------------------------------------------------------------------------
1 | {
2 | "textDocument": {
3 | "id": "stonks-portfolio",
4 | "name": "stonks portfolio",
5 | "text": [
6 | "would like to import outside data",
7 | "doing row-wise computation is pretty straightforward in potluck",
8 | "",
9 | "$GME 250@$5.00",
10 | "$AMC 100@$1.00",
11 | "",
12 | "portfolio value"
13 | ],
14 | "sheets": [
15 | {
16 | "id": "_vYbP2uJkzwO2zAXyV9ZDY",
17 | "configId": "stonks-portfolio.1",
18 | "hideHighlightsInDocument": false
19 | },
20 | {
21 | "id": "_ns9sZr9wgelKJDzzVezCe",
22 | "configId": "stonks-portfolio.2",
23 | "hideHighlightsInDocument": false
24 | }
25 | ]
26 | },
27 | "sheetConfigs": [
28 | {
29 | "id": "stonks-portfolio.1",
30 | "name": "portfolio value",
31 | "properties": [
32 | {
33 | "name": "$",
34 | "formula": "portfolio value",
35 | "visibility": "HIDDEN"
36 | },
37 | {
38 | "name": "col8",
39 | "formula": "FindAll(\"holdings\").map(h => h.data.currentPrice * TextOfHighlight(h.data.shares)).reduce((a, b) => a + b, 0)",
40 | "visibility": "INLINE"
41 | }
42 | ]
43 | },
44 | {
45 | "id": "stonks-portfolio.2",
46 | "name": "holdings",
47 | "properties": [
48 | {
49 | "name": "$",
50 | "formula": "${/\\\\w+/:ticker} {number:shares}@${/[\\\\d\\.]+/:cost}",
51 | "visibility": "HIDDEN"
52 | },
53 | {
54 | "name": "currentPrice",
55 | "formula": "TextOfHighlight(ticker) === \"GME\" ? 40 : 20",
56 | "visibility": "SUPERSCRIPT"
57 | },
58 | {
59 | "name": "col10",
60 | "formula": "`$${currentPrice * ParseInt(shares)}`",
61 | "visibility": "INLINE"
62 | }
63 | ]
64 | }
65 | ]
66 | }
--------------------------------------------------------------------------------
/src/markdown.ts:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line
2 | // @ts-ignore
3 | Prism.languages.markdown = Prism.languages.extend("markup", {}), Prism.languages.insertBefore("markdown", "prolog", {
4 | blockquote: {pattern: /^>(?:[\t ]*>)*/m, alias: "punctuation"},
5 | code: [{pattern: /^(?: {4}|\t).+/m, alias: "keyword"}, {pattern: /``.+?``|`[^`\n]+`/, alias: "keyword"}],
6 | title: [{
7 | pattern: /\w+.*(?:\r?\n|\r)(?:==+|--+)/,
8 | alias: "important",
9 | inside: {punctuation: /==+$|--+$/}
10 | }, {pattern: /(^\s*)#+.+/m, lookbehind: !0, alias: "important", inside: {punctuation: /^#+|#+$/}}],
11 | hr: {pattern: /(^\s*)([*-])([\t ]*\2){2,}(?=\s*$)/m, lookbehind: !0, alias: "punctuation"},
12 | list: {pattern: /(^\s*)(?:[*+-]|\d+\.)(?=[\t ].)/m, lookbehind: !0, alias: "punctuation"},
13 | "url-reference": {
14 | pattern: /!?\[[^\]]+\]:[\t ]+(?:\S+|<(?:\\.|[^>\\])+>)(?:[\t ]+(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\)))?/,
15 | inside: {
16 | variable: {pattern: /^(!?\[)[^\]]+/, lookbehind: !0},
17 | string: /(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\))$/,
18 | punctuation: /^[\[\]!:]|[<>]/
19 | },
20 | alias: "url"
21 | },
22 | bold: {
23 | pattern: /(^|[^\\])(\*\*|__)(?:(?:\r?\n|\r)(?!\r?\n|\r)|.)+?\2/,
24 | lookbehind: !0,
25 | inside: {punctuation: /^\*\*|^__|\*\*$|__$/}
26 | },
27 | italic: {
28 | pattern: /(^|[^\\])([*_])(?:(?:\r?\n|\r)(?!\r?\n|\r)|.)+?\2/,
29 | lookbehind: !0,
30 | inside: {punctuation: /^[*_]|[*_]$/}
31 | },
32 | url: {
33 | pattern: /!?\[[^\]]+\](?:\([^\s)]+(?:[\t ]+"(?:\\.|[^"\\])*")?\)| ?\[[^\]\n]*\])/,
34 | inside: {variable: {pattern: /(!?\[)[^\]]+(?=\]$)/, lookbehind: !0}, string: {pattern: /"(?:\\.|[^"\\])*"(?=\)$)/}}
35 | }
36 | //@ts-ignore
37 | }), Prism.languages.markdown.bold.inside.url = Prism.util.clone(Prism.languages.markdown.url), Prism.languages.markdown.italic.inside.url = Prism.util.clone(Prism.languages.markdown.url), Prism.languages.markdown.bold.inside.italic = Prism.util.clone(Prism.languages.markdown.italic), Prism.languages.markdown.italic.inside.bold = Prism.util.clone(Prism.languages.markdown.bold); // prettier-ignore
38 |
--------------------------------------------------------------------------------
/sample-data-old/pizza.txt:
--------------------------------------------------------------------------------
1 | sheet pan corn kimchi pizza
2 | Sheet Pan Corn Pizza With Kimchi and Hot Dogs
3 |
4 | 5 Tbsp. extra-virgin olive oil, divided
5 | 1 lb. store-bought pizza dough, room temperature
6 | 1 14.5-oz. can crushed tomatoes
7 | 2 tsp. sugar
8 | 1 cup coarsely chopped drained kimchi, plus juice from jar (optional)
9 | Kosher salt
10 | 8 oz. low-moisture mozzarella, grated
11 | 1 medium green bell pepper, cut into ¼" pieces
12 | 4 all-beef or other hot dogs, sliced into ½" coins
13 | 2 cups corn (from about 2 ears)
14 | 3 scallions, thinly sliced
15 |
16 | Step 1
17 | Coat a large rimmed baking sheet with 4 Tbsp. extra-virgin olive oil. Place 1 lb. store-bought pizza dough, room temperature, in center of baking sheet; using your fingers, gradually stretch dough outward from center until it reaches to edges and into corners of baking sheet. (If dough is too stiff or springs back, cover with an inverted baking sheet or plastic wrap and let rest 10 minutes before trying again. You may need to let dough rest 2 or 3 times.) Cover and let rise in a warm spot until slightly puffy, about 30 minutes.
18 |
19 | Step 2
20 | While the dough is rising, place a rack in lowest position of oven; preheat to 475°. Combine one 14.5-oz. can crushed tomatoes, 2 tsp. sugar, remaining 1 Tbsp. extra-virgin olive oil, and up to ¼ cup kimchi juice (if using) in a small saucepan. Bring to a simmer over medium heat and cook, stirring occasionally, until sauce is slightly reduced, 7–10 minutes. Remove from heat; season with salt.
21 |
22 | Step 3
23 | Uncover dough and scatter 8 oz. low-moisture mozzarella, grated, over, going all the way to the edges. Dollop sauce over (do not spread), then evenly top with 1 medium green bell pepper, cut into ¼" pieces, 4 all-beef or other hot dogs, sliced into ½" coins, 2 cups corn kernels (from about 2 ears), and 1 cup coarsely chopped drained kimchi.
24 |
25 | Step 4
26 | Bake pizza until cheese is melted and crust is golden brown on bottom and sides (lift an edge with a heatproof spatula to check), 22–28 minutes. If crust feels soft or bendy in center, loosely cover pizza with foil and continue to bake 8–10 minutes longer.
27 |
28 | Step 5
29 | To serve, top pizza with 3 scallions, thinly sliced; cut into squares.
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "potluck-parser",
3 | "private": true,
4 | "version": "0.0.0",
5 | "scripts": {
6 | "dev": "vite",
7 | "build": "tsc && vite build",
8 | "preview": "vite preview",
9 | "refactor-watcher": "node src/refactor/watcher.mjs sample-data",
10 | "refactor-cleanup": "node src/refactor/cleanup-highlighters.mjs sample-data"
11 | },
12 | "dependencies": {
13 | "@codemirror/autocomplete": "^6.1.0",
14 | "@codemirror/commands": "^6.0.1",
15 | "@codemirror/lang-javascript": "^6.0.2",
16 | "@codemirror/language": "^6.2.1",
17 | "@codemirror/state": "^6.1.0",
18 | "@codemirror/view": "^6.0.3",
19 | "@radix-ui/react-hover-card": "^0.1.5",
20 | "@radix-ui/react-icons": "^1.1.1",
21 | "@radix-ui/react-popover": "^1.0.0",
22 | "@radix-ui/react-toast": "^1.0.0",
23 | "@radix-ui/react-tooltip": "^1.0.0",
24 | "@rollup/plugin-dsv": "^2.0.3",
25 | "@types/memoizee": "^0.4.8",
26 | "@types/nanoid-dictionary": "^4.2.0",
27 | "browser-fs-access": "^0.31.0",
28 | "classnames": "^2.3.1",
29 | "codemirror": "^6.0.1",
30 | "date-fns": "^2.28.0",
31 | "eventemitter3": "^4.0.7",
32 | "file-dialog": "^0.0.8",
33 | "fuzzyset": "^1.0.7",
34 | "idb-keyval": "^6.2.0",
35 | "lodash": "^4.17.21",
36 | "luxon": "^3.0.1",
37 | "memoizee": "^0.4.15",
38 | "mobx": "^6.6.1",
39 | "mobx-react-lite": "^3.4.0",
40 | "nanoid": "^4.0.0",
41 | "nanoid-dictionary": "^4.3.0",
42 | "ohm-js": "^16.4.0",
43 | "prismjs": "^1.28.0",
44 | "react": "^18.0.0",
45 | "react-big-calendar": "^1.5.0",
46 | "react-dom": "^18.0.0"
47 | },
48 | "devDependencies": {
49 | "@types/lodash": "^4.14.182",
50 | "@types/luxon": "^3.0.0",
51 | "@types/prismjs": "^1.26.0",
52 | "@types/react": "^18.0.0",
53 | "@types/react-big-calendar": "^0.38.1",
54 | "@types/react-dom": "^18.0.0",
55 | "@vitejs/plugin-react": "^1.3.0",
56 | "autoprefixer": "^10.4.7",
57 | "outdent": "^0.8.0",
58 | "postcss": "^8.4.14",
59 | "prettier": "^2.7.1",
60 | "rollup-plugin-analyzer": "^4.0.0",
61 | "tailwindcss": "^3.1.6",
62 | "typescript": "^4.6.3",
63 | "vite": "^2.9.9",
64 | "watcher": "^1.2.0"
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | .icon {
6 | display: inline-block;
7 | mask-position: center;
8 | mask-size: contain;
9 | mask-repeat: no-repeat;
10 | width: 1em;
11 | height: 1em;
12 |
13 | -webkit-mask-position: center;
14 | -webkit-mask-size: contain;
15 | -webkit-mask-repeat: no-repeat;
16 | }
17 |
18 | .icon-plus {
19 | mask-image: url("assets/plus-icon.svg");
20 | -webkit-mask-image: url("assets/plus-icon.svg");
21 | }
22 |
23 | .icon-asterisk {
24 | mask-image: url('assets/asterisk-icon.svg');
25 | -webkit-mask-image: url('assets/asterisk-icon.svg');
26 | }
27 |
28 | .icon-expandable {
29 | transition: transform 100ms ease-in-out;
30 | mask-image: url('assets/chevron-forward.svg');
31 | -webkit-mask-image: url('assets/chevron-forward.svg');
32 | }
33 |
34 | .icon-expandable.is-expanded {
35 | transform: rotate(90deg);
36 | }
37 |
38 | .button {
39 | @apply px-3 py-1 bg-blue-500 text-white rounded;
40 | }
41 |
42 | .cm-focused .cm-highlight-selection,
43 | .cm-highlight-hover {
44 | @apply bg-yellow-200
45 | }
46 |
47 | .cm-highlight-replace {
48 | @apply hidden;
49 | }
50 |
51 | .rbc-event:hover {
52 | @apply bg-blue-500;
53 | }
54 |
55 | .cm-highlight {
56 | padding-bottom: 2px;
57 | background-image: url(./assets/splat-underline-2-default.svg);
58 | background-size: 100% 4px;
59 | background-position: bottom;
60 | background-repeat: no-repeat;
61 | }
62 |
63 | input[type="range"] {
64 | @apply h-1 w-14;
65 | background-image: url(./assets/slider-track.svg);
66 | background-repeat: no-repeat;
67 | background-size: contain;
68 | background-position: center center;
69 | -webkit-appearance: none;
70 | }
71 |
72 | input[type="range"]::-webkit-slider-thumb {
73 | @apply h-1.5 w-1.5 rounded-full cursor-ew-resize;
74 | background-image: url(./assets/slider-handle.svg);
75 | background-repeat: no-repeat;
76 | background-size: contain;
77 | -webkit-appearance: none;
78 | }
79 |
80 | .annotation-token {
81 | @apply font-mono text-sm;
82 | color: #1355ff;
83 | background-image: url(./assets/whiteout.svg);
84 | background-size: auto 100%;
85 | background-repeat: repeat-x;
86 | font-family: "Schoolbell", sans-serif;
87 | font-size: 16px;
88 | }
89 |
90 | /* z-index hack */
91 |
92 | div[data-radix-popper-content-wrapper] {
93 | z-index: 9999 !important;
94 | }
--------------------------------------------------------------------------------
/src/assets/splat-underline-2-default.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/utils.ts:
--------------------------------------------------------------------------------
1 | import { customAlphabet } from "nanoid";
2 | import {
3 | HighlightComponent,
4 | Span,
5 | textDocumentsMobx,
6 | } from "./primitives";
7 | import { alphanumeric } from "nanoid-dictionary";
8 | import { getComputedSheetValue } from "./compute";
9 | import { Highlight } from "./highlight";
10 |
11 | export function doSpansOverlap(a: Span, b: Span) {
12 | return a[0] <= b[1] && b[0] <= a[1];
13 | }
14 |
15 | export function doesSpanContainOtherSpan(parent: Span, child: Span) {
16 | return parent[0] <= child[0] && parent[1] >= child[1];
17 | }
18 |
19 | export function doesSpanContainsPosition(span: Span, position: number) {
20 | return span[0] <= position && position < span[1];
21 | }
22 |
23 | export function getTextForHighlight(highlight: Highlight) {
24 | const textDocument = textDocumentsMobx.get(highlight.documentId);
25 | return textDocument?.text.sliceString(highlight.span[0], highlight.span[1]);
26 | }
27 |
28 | export function getIntValue(value: any) {
29 | if (isValueRowHighlight(value)) {
30 | value = getTextForHighlight(value);
31 | }
32 |
33 | return parseInt(value, 10);
34 | }
35 |
36 | export function isValueRowHighlight(valueRow: any): valueRow is Highlight {
37 | return (
38 | typeof valueRow === "object" &&
39 | "span" in valueRow &&
40 | valueRow.span !== undefined
41 | );
42 | }
43 |
44 | export function isHighlightComponent(value: any): value is HighlightComponent {
45 | return (
46 | value !== undefined &&
47 | value !== null &&
48 | typeof value === "object" &&
49 | typeof value.render === "function"
50 | );
51 | }
52 |
53 | export function isNumericish(value: any): boolean {
54 | return (
55 | typeof value === "number" ||
56 | (typeof value === "string" && /^([\d\.])+$/.test(value))
57 | );
58 | }
59 |
60 | export function coerceValueToNumber (value: any) : number | typeof NaN {
61 | if (isValueRowHighlight(value)) {
62 | return coerceValueToNumber(getTextForHighlight(value))
63 | }
64 |
65 | if (typeof value === "number") {
66 | return value
67 | }
68 |
69 | if (isNumericish(value)) {
70 | return parseFloat(value)
71 | }
72 |
73 | return NaN
74 | }
75 |
76 | const _generateNanoid = customAlphabet(alphanumeric)
77 |
78 | export const generateNanoid = () => `_${_generateNanoid()}`;
79 |
80 | export function transformColumnFormula(
81 | formula: string,
82 | isFirstColumn: boolean
83 | ) {
84 | return isFirstColumn
85 | ? formula.startsWith("=")
86 | ? formula.substring(1)
87 | : `MatchPattern("${formula.replaceAll(/"/g, '\\"')}")`
88 | : formula;
89 | }
90 |
--------------------------------------------------------------------------------
/src/data/measure_unit.csv:
--------------------------------------------------------------------------------
1 | "id","name"
2 | "1000","cup"
3 | "1001","tablespoon"
4 | "1002","teaspoon"
5 | "1003","liter"
6 | "1004","milliliter"
7 | "1005","cubic inch"
8 | "1006","cubic centimeter"
9 | "1007","gallon"
10 | "1008","pint"
11 | "1009","fl oz"
12 | "1010","paired cooked w"
13 | "1011","paired raw w"
14 | "1012","dripping w"
15 | "1013","bar"
16 | "1014","bird"
17 | "1015","biscuit"
18 | "1016","bottle"
19 | "1017","box"
20 | "1018","breast"
21 | "1019","can"
22 | "1020","chicken"
23 | "1021","chop"
24 | "1022","cookie"
25 | "1023","container"
26 | "1024","cracker"
27 | "1025","drink"
28 | "1026","drumstick"
29 | "1027","fillet"
30 | "1028","fruit"
31 | "1029","large"
32 | "1030","lb"
33 | "1031","leaf"
34 | "1032","leg"
35 | "1033","link"
36 | "1034","links"
37 | "1035","loaf"
38 | "1036","medium"
39 | "1037","muffin"
40 | "1038","oz"
41 | "1039","package"
42 | "1040","packet"
43 | "1041","patty"
44 | "1042","patties"
45 | "1043","piece"
46 | "1044","pieces"
47 | "1045","quart"
48 | "1046","roast"
49 | "1047","sausage"
50 | "1048","scoop"
51 | "1049","serving"
52 | "1050","slice"
53 | "1051","slices"
54 | "1052","small"
55 | "1053","stalk"
56 | "1054","steak"
57 | "1055","stick"
58 | "1056","strip"
59 | "1057","tablet"
60 | "1058","thigh"
61 | "1059","unit"
62 | "1060","wedge"
63 | "1061","orig ckd g"
64 | "1062","orig rw g"
65 | "1063","medallion"
66 | "1064","pie"
67 | "1065","wing"
68 | "1066","back"
69 | "1067","olive"
70 | "1068","pocket"
71 | "1069","order"
72 | "1070","shrimp"
73 | "1071","each"
74 | "1072","filet"
75 | "1073","plantain"
76 | "1074","nugget"
77 | "1075","pretzel"
78 | "1076","corndog"
79 | "1077","spear"
80 | "1078","sandwich"
81 | "1079","tortilla"
82 | "1080","burrito"
83 | "1081","taco"
84 | "1082","tomatoes"
85 | "1083","chips"
86 | "1084","shell"
87 | "1085","bun"
88 | "1086","crust"
89 | "1087","sheet"
90 | "1088","bag"
91 | "1089","bagel"
92 | "1090","bowl"
93 | "1091","breadstick"
94 | "1092","bulb"
95 | "1093","cake"
96 | "1094","carton"
97 | "1095","chunk"
98 | "1096","contents"
99 | "1097","cutlet"
100 | "1098","doughnut"
101 | "1099","egg"
102 | "1100","fish"
103 | "1101","foreshank"
104 | "1102","frankfurter"
105 | "1103","fries"
106 | "1104","head"
107 | "1105","jar"
108 | "1106","loin"
109 | "1107","pancake"
110 | "1108","pizza"
111 | "1109","rack"
112 | "1110","ribs"
113 | "1111","roll"
114 | "1112","shank"
115 | "1113","shoulder"
116 | "1114","skin"
117 | "1115","wafers"
118 | "1116","wrap"
119 | "1117","bunch"
120 | "1118","Tablespoons"
121 | "1119","Banana"
122 | "1120","Onion"
123 | "9999","undetermined"
124 |
--------------------------------------------------------------------------------
/sample-data/gochujang-pork.txt:
--------------------------------------------------------------------------------
1 | Gochujang pork
2 | Grilled Gochujang Pork With Fresh Sesame Kimchi
3 |
4 | Pork shoulder is often prepared as a large roast, requiring hours of cooking until it’s tender. But if you slice it thinly and pound it, the meat quickly absorbs this savory gochujang marinade and cooks up in no time. The spicy pork is balanced by a cool and crisp sesame kimchi, eaten fresh like a salad rather than fermented like traditional preparations. Baby bok choy stands in for the usual napa cabbage, and it’s coated in a vibrant sauce of garlic, ginger, gochugaru, fish sauce and nutty sesame oil. Tuck any leftover pork and kimchi into sandwiches the next day, garnished with tomatoes and mayonnaise.
5 |
6 | scale by
7 |
8 | 2 tablespoons gochugaru
9 | 2 tablespoons distilled white vinegar
10 | 2 tablespoons toasted sesame oil
11 | 3 teaspoons grated garlic
12 | 2 teaspoons grated peeled ginger
13 | 1 teaspoon kosher salt (such as Diamond Crystal), plus more for seasoning
14 | ½ teaspoon fish sauce
15 | 1 tablespoon plus ½ teaspoon granulated sugar
16 | 1½ pounds baby bok choy, quartered lengthwise
17 | 3 scallions, halved lengthwise and thinly sliced on the diagonal
18 | 2 tablespoons gochujang (Korean chile paste)
19 | 2 tablespoons neutral oil, such as safflower or canola
20 | 1 tablespoon low-sodium soy sauce
21 | 1 teaspoon ground black pepper, plus more for seasoning
22 | 2 pounds pork shoulder, thinly sliced crosswise and pounded ⅛-inch-thick (see Tip)
23 | 1 large white onion, peeled and sliced into ¼-inch-thick rings
24 | Steamed rice, for serving
25 |
26 | Preparation
27 | Step 1
28 | In a large bowl, combine the gochugaru, vinegar, sesame oil, 1 teaspoon of the garlic, 1 teaspoon of the ginger, 1 teaspoon salt, the fish sauce and ½ teaspoon of the sugar; mix well. Add bok choy and scallions, and toss with your hands, working the sauce in between and all over the leaves.
29 |
30 | Step 2
31 | Heat a grill to medium-high or heat a stovetop griddle pan over medium-high. In a large bowl, combine the gochujang, neutral oil, soy sauce, 1 teaspoon black pepper and the remaining 2 teaspoons garlic, 1 teaspoon ginger and 1 tablespoon sugar; mix well. Very lightly season the pork with salt and pepper. Add pork and onion to the marinade and toss, gently massaging the marinade all over the meat (The meat does not need to rest in the marinade before it is grilled, but it can be marinated for up to 3 hours.)
32 |
33 | Step 3
34 | Grill the pork and onion, in batches if necessary, until nicely charred and caramelized around the edges, and the pork is cooked through, about 3 minutes per side. Transfer to a serving platter.
35 |
36 | Step 4
37 | Serve the grilled pork and onions with the fresh sesame kimchi and rice on the side.
--------------------------------------------------------------------------------
/sample-data-old/gochujang-pork.txt:
--------------------------------------------------------------------------------
1 | gochujang pork
2 | Grilled Gochujang Pork With Fresh Sesame Kimchi
3 |
4 | Pork shoulder is often prepared as a large roast, requiring hours of cooking until it’s tender. But if you slice it thinly and pound it, the meat quickly absorbs this savory gochujang marinade and cooks up in no time. The spicy pork is balanced by a cool and crisp sesame kimchi, eaten fresh like a salad rather than fermented like traditional preparations. Baby bok choy stands in for the usual napa cabbage, and it’s coated in a vibrant sauce of garlic, ginger, gochugaru, fish sauce and nutty sesame oil. Tuck any leftover pork and kimchi into sandwiches the next day, garnished with tomatoes and mayonnaise.
5 |
6 | scale by
7 |
8 | 2 tablespoons gochugaru
9 | 2 tablespoons distilled white vinegar
10 | 2 tablespoons toasted sesame oil
11 | 3 teaspoons grated garlic
12 | 2 teaspoons grated peeled ginger
13 | 1 teaspoon kosher salt (such as Diamond Crystal), plus more for seasoning
14 | ½ teaspoon fish sauce
15 | 1 tablespoon plus ½ teaspoon granulated sugar
16 | 1½ pounds baby bok choy, quartered lengthwise
17 | 3 scallions, halved lengthwise and thinly sliced on the diagonal
18 | 2 tablespoons gochujang (Korean chile paste)
19 | 2 tablespoons neutral oil, such as safflower or canola
20 | 1 tablespoon low-sodium soy sauce
21 | 1 teaspoon ground black pepper, plus more for seasoning
22 | 2 pounds pork shoulder, thinly sliced crosswise and pounded ⅛-inch-thick (see Tip)
23 | 1 large white onion, peeled and sliced into ¼-inch-thick rings
24 | Steamed rice, for serving
25 |
26 | Preparation
27 | Step 1
28 | In a large bowl, combine the gochugaru, vinegar, sesame oil, 1 teaspoon of the garlic, 1 teaspoon of the ginger, 1 teaspoon salt, the fish sauce and ½ teaspoon of the sugar; mix well. Add bok choy and scallions, and toss with your hands, working the sauce in between and all over the leaves.
29 |
30 | Step 2
31 | Heat a grill to medium-high or heat a stovetop griddle pan over medium-high. In a large bowl, combine the gochujang, neutral oil, soy sauce, 1 teaspoon black pepper and the remaining 2 teaspoons garlic, 1 teaspoon ginger and 1 tablespoon sugar; mix well. Very lightly season the pork with salt and pepper. Add pork and onion to the marinade and toss, gently massaging the marinade all over the meat (The meat does not need to rest in the marinade before it is grilled, but it can be marinated for up to 3 hours.)
32 |
33 | Step 3
34 | Grill the pork and onion, in batches if necessary, until nicely charred and caramelized around the edges, and the pork is cooked through, about 3 minutes per side. Transfer to a serving platter.
35 |
36 | Step 4
37 | Serve the grilled pork and onions with the fresh sesame kimchi and rice on the side.
--------------------------------------------------------------------------------
/src/HighlightHoverCard.tsx:
--------------------------------------------------------------------------------
1 | import { observer } from "mobx-react-lite";
2 | import { useState } from "react";
3 | import { textDocumentsMobx } from "./primitives";
4 | import { getTextForHighlight } from "./utils";
5 | import * as HoverCardPrimitive from "@radix-ui/react-hover-card";
6 | import { Highlight } from "./highlight";
7 |
8 | export const HighlightHoverCardContent = observer(
9 | ({ highlight }: { highlight: Highlight }) => {
10 | const [extraLinesToShow, setExtraLinesToShow] = useState(3);
11 | const textDocument = textDocumentsMobx.get(highlight.documentId);
12 | if (textDocument === undefined) {
13 | return null;
14 | }
15 | const highlightLineStart = textDocument.text.lineAt(
16 | highlight.span[0]
17 | ).number;
18 | const startPosOfHighlightLine =
19 | textDocument.text.line(highlightLineStart).from;
20 | const highlightLineEnd = textDocument.text.lineAt(highlight.span[1]).number;
21 | const extraContextText = textDocument.text.sliceString(
22 | highlight.span[1],
23 | textDocument.text.line(
24 | Math.min(highlightLineEnd + extraLinesToShow, textDocument.text.lines)
25 | ).to
26 | );
27 |
28 | const startLine = textDocument.text.lineAt(highlight.span[0]).number;
29 | const endLine = textDocument.text.lineAt(highlight.span[1]).number;
30 |
31 | return (
32 | <>
33 |
34 | {textDocument.name} (Line{" "}
35 | {startLine === endLine ? startLine : `${startLine} - ${endLine}`})
36 |
37 |
38 | {highlight.span[0] > startPosOfHighlightLine ? (
39 |
40 | {textDocument.text.sliceString(
41 | startPosOfHighlightLine,
42 | highlight.span[0]
43 | )}
44 |
45 | ) : null}
46 |
47 | {getTextForHighlight(highlight)}
48 |
49 | {extraContextText.length > 0 ? {extraContextText} : null}
50 |
51 |
52 | {highlightLineEnd + extraLinesToShow < textDocument.text.lines ? (
53 |
61 | ) : null}
62 |
63 | >
64 | );
65 | }
66 | );
67 |
68 | export function HighlightHoverCard({
69 | children,
70 | highlight,
71 | }: {
72 | children: React.ReactNode;
73 | highlight: Highlight;
74 | }) {
75 | return (
76 |
77 | {children}
78 |
83 |
84 |
85 |
86 |
87 | );
88 | }
89 |
--------------------------------------------------------------------------------
/src/refactor/watcher.mjs:
--------------------------------------------------------------------------------
1 | import path from 'node:path'
2 | import fs from "node:fs"
3 | import Watcher from 'watcher'
4 | import { readDocumentSheets, readHighlighter, writeDocumentSheets, writeHighlighter } from './utils.mjs'
5 | import outdent from 'outdent'
6 | import { fileURLToPath } from 'url';
7 | const __filename = fileURLToPath(import.meta.url);
8 | const __dirname = path.dirname(__filename);
9 |
10 | const watchPath = process.argv[2]
11 | const fullPath = path.resolve(process.cwd(), watchPath)
12 |
13 | const watcher = new Watcher(fullPath, { renameDetection: true })
14 |
15 | watcher.on('rename', (filePath, filePathNext) => {
16 | if (filePath.endsWith('.txt') && filePathNext.endsWith('.txt')) {
17 | onRenameDocument(filePath, filePathNext)
18 | } else if (filePath.endsWith('.highlighter') && filePathNext.endsWith('.highlighter')) {
19 | onRenameHighlighter(filePath, filePathNext)
20 | }
21 | })
22 |
23 | generateDefaultStateFile()
24 |
25 | function onRenameHighlighter (oldName, newName) {
26 | const oldConfigId = path.basename(oldName, '.highlighter')
27 | const newConfigId = path.basename(newName, '.highlighter')
28 |
29 | console.log(`rename highlighter ${oldConfigId} => ${newConfigId}`)
30 |
31 | const documentsSheets = readDocumentSheets(fullPath)
32 | const highlighter = readHighlighter(fullPath, newConfigId)
33 |
34 | let changeCount = 0
35 |
36 | const refactoredHighlighter = { ...highlighter, id: newConfigId }
37 | const refactoredDocumentsSheet = documentsSheets.map((documentSheet) => {
38 | if (documentSheet.configId !== oldConfigId) {
39 | return documentSheet
40 | }
41 |
42 | changeCount += 1
43 |
44 | return { ...documentSheet, configId: newConfigId }
45 | })
46 |
47 | console.log(`changed ${changeCount} sheets`)
48 |
49 | writeHighlighter(fullPath, newConfigId, refactoredHighlighter)
50 | writeDocumentSheets(fullPath, refactoredDocumentsSheet)
51 | generateDefaultStateFile()
52 | }
53 |
54 | function onRenameDocument (oldName, newName) {
55 | const oldTextDocumentId = path.basename(oldName, '.txt')
56 | const newTextDocumentId = path.basename(newName, '.txt')
57 |
58 | console.log(`rename document ${oldTextDocumentId} => ${newTextDocumentId}`)
59 |
60 | const documentsSheets = readDocumentSheets(fullPath)
61 |
62 | let changeCount = 0
63 |
64 | const refactoredDocumentsSheet = documentsSheets.map((documentSheet) => {
65 | if (documentSheet.textDocumentId !== oldTextDocumentId) {
66 | return documentSheet
67 | }
68 |
69 | changeCount += 1
70 |
71 | return { ...documentSheet, textDocumentId: newTextDocumentId }
72 | })
73 |
74 | console.log(`changed ${changeCount} sheets`)
75 |
76 | writeDocumentSheets(fullPath, refactoredDocumentsSheet)
77 | generateDefaultStateFile()
78 | }
79 |
80 | function generateDefaultStateFile () {
81 | console.log('generate DefaultState.ts')
82 |
83 | const imports = []
84 | const exportEntries = []
85 |
86 | fs.readdirSync(fullPath).forEach((file, index )=> {
87 | const importName = `file_${index}`
88 |
89 | imports.push(`import ${importName} from "../sample-data/${file}?raw"`)
90 | exportEntries.push(` "/${file}" : ${importName}`)
91 |
92 | })
93 |
94 | const content = outdent`
95 | // auto generated, do not edit
96 | ${imports.join("\n")}
97 |
98 | export const DefaultFiles = {
99 | ${exportEntries.join(',\n')}
100 | }
101 | `
102 |
103 | fs.writeFileSync(path.join(__dirname, "../DefaultState.ts"), content)
104 | }
--------------------------------------------------------------------------------
/src/SheetCalendar.tsx:
--------------------------------------------------------------------------------
1 | import { Calendar, dateFnsLocalizer, Views } from "react-big-calendar";
2 | import format from "date-fns/format";
3 | import parse from "date-fns/parse";
4 | import startOfWeek from "date-fns/startOfWeek";
5 | import getDay from "date-fns/getDay";
6 | import enUS from "date-fns/locale/en-US";
7 | import "react-big-calendar/lib/css/react-big-calendar.css";
8 | import { observer } from "mobx-react-lite";
9 | import {
10 | hoverHighlightsMobx,
11 | SheetConfig,
12 | SheetValueRow,
13 | TextDocument,
14 | PropertyDefinition,
15 | } from "./primitives";
16 | import { useMemo, useState } from "react";
17 | import { getIntValue, getTextForHighlight, isValueRowHighlight } from "./utils";
18 | import addDays from "date-fns/addDays";
19 | import { action } from "mobx";
20 | import { HighlightHoverCard } from "./HighlightHoverCard";
21 | import { SheetViewProps } from "./SheetComponent";
22 | import { Highlight } from "./highlight";
23 |
24 | function getDateForRow(row: SheetValueRow) {
25 | const { day, month, year } =
26 | typeof row.data.year === "number" ? row.data : row.data.date.data;
27 | return new Date(2000 + getIntValue(year), getIntValue(month) - 1, getIntValue(day));
28 | }
29 |
30 | type CalendarEvent = {
31 | id: string;
32 | title: string;
33 | start: Date;
34 | end: Date;
35 | highlight: Highlight;
36 | };
37 |
38 | function CalendarMonthEvent({
39 | event,
40 | title,
41 | }: {
42 | event: CalendarEvent;
43 | title: string;
44 | }) {
45 | return (
46 |
47 | {
49 | const childrenHighlights = Object.values(
50 | event.highlight.data
51 | ).flatMap((columnData) =>
52 | isValueRowHighlight(columnData) ? [columnData] : []
53 | );
54 | hoverHighlightsMobx.replace(
55 | childrenHighlights.length > 0
56 | ? childrenHighlights
57 | : [event.highlight]
58 | );
59 | })}
60 | onMouseLeave={action(() => {
61 | hoverHighlightsMobx.clear();
62 | })}
63 | >
64 | {title}
65 |
66 |
67 | );
68 | }
69 |
70 | export const SheetCalendar = observer(
71 | ({ textDocument, sheetConfig, columns, rows }: SheetViewProps) => {
72 | const localizer = dateFnsLocalizer({
73 | format,
74 | parse,
75 | startOfWeek,
76 | getDay,
77 | locales: {
78 | "en-US": enUS,
79 | },
80 | });
81 | const titleColumnName = sheetConfig.properties[0].name;
82 | const events = useMemo(
83 | () =>
84 | rows.map((row, i): CalendarEvent => {
85 | const date = getDateForRow(row);
86 | return {
87 | id: `${i}`,
88 | title: `${getTextForHighlight(row.data[titleColumnName])}`,
89 | start: date,
90 | end: addDays(date, 1),
91 | highlight: row as Highlight,
92 | };
93 | }),
94 | [rows]
95 | );
96 | const [defaultDate] = useState(() => getDateForRow(rows[rows.length - 1]));
97 | return (
98 |
99 |
110 |
111 | );
112 | }
113 | );
114 |
--------------------------------------------------------------------------------
/src/compute.ts:
--------------------------------------------------------------------------------
1 | import { comparer, computed, IComputedValue } from "mobx";
2 | import { evaluateSheet } from "./formulas";
3 | import {
4 | getSheetConfigsOfTextDocument,
5 | selectedTextDocumentIdBox,
6 | SheetConfig,
7 | sheetConfigsMobx, SheetValueRow,
8 | TextDocument,
9 | TextDocumentSheet,
10 | textDocumentsMobx,
11 | } from "./primitives";
12 | import {Highlight} from "./highlight";
13 | import { isValueRowHighlight } from "./utils";
14 |
15 | const getDocumentSheetKey = (
16 | textDocumentId: string,
17 | sheetConfigId: string,
18 | firstColumnOnly: boolean = false
19 | ) => `${textDocumentId}:${sheetConfigId}${firstColumnOnly ? ":first" : ""}`;
20 | const documentSheetCache: Record> = {};
21 |
22 | export function getComputedSheetValue(
23 | textDocumentId: string,
24 | sheetConfigId: string,
25 | firstColumnOnly: boolean = false
26 | ): IComputedValue {
27 | const key = getDocumentSheetKey(
28 | textDocumentId,
29 | sheetConfigId,
30 | firstColumnOnly
31 | );
32 | if (documentSheetCache[key] === undefined) {
33 | documentSheetCache[key] = computed(() => {
34 | const textDocument = textDocumentsMobx.get(textDocumentId);
35 | const sheetConfig = sheetConfigsMobx.get(sheetConfigId);
36 | if (textDocument === undefined || sheetConfig === undefined) {
37 | return [];
38 | }
39 | return evaluateSheet(textDocument, sheetConfig, firstColumnOnly);
40 | });
41 | }
42 | return documentSheetCache[key];
43 | }
44 |
45 | const documentValueCache: Record<
46 | string,
47 | IComputedValue<{ [sheetConfigId: string]: SheetValueRow[] }>
48 | > = {};
49 | export function getComputedDocumentValues(
50 | textDocumentId: string
51 | ): IComputedValue<{ [sheetConfigId: string]: SheetValueRow[] }> {
52 | if (documentValueCache[textDocumentId] === undefined) {
53 | documentValueCache[textDocumentId] = computed(() => {
54 | const textDocument = textDocumentsMobx.get(textDocumentId);
55 | if (textDocument === undefined) {
56 | return {};
57 | }
58 | const sheetConfigs = getSheetConfigsOfTextDocument(textDocument);
59 | return Object.fromEntries(
60 | sheetConfigs.map((sheetConfig) => [
61 | sheetConfig.id,
62 | getComputedSheetValue(textDocument.id, sheetConfig.id).get(),
63 | ])
64 | );
65 | });
66 | }
67 | return documentValueCache[textDocumentId];
68 | }
69 |
70 | export const editorSelectionHighlightsComputed = computed(
71 | () => {
72 | const textDocumentId = selectedTextDocumentIdBox.get();
73 | const textDocument = textDocumentsMobx.get(textDocumentId);
74 | if (textDocument === undefined) {
75 | return [];
76 | }
77 | const documentValueRows = getComputedDocumentValues(textDocumentId).get();
78 | return Object.entries(documentValueRows)
79 | .map(([sheetConfigId, sheetValueRows]) =>
80 | sheetValueRows.filter((r): r is Highlight => {
81 | return isValueRowHighlight(r);
82 | })
83 | )
84 | .flat();
85 | },
86 | { equals: comparer.structural }
87 | );
88 |
89 | // we cheat a little here, for the sheetConfigId we only eval the first column to avoid
90 | // circular dependencies, this is necessary for the formula like NextValuesUntil(activity, HasType("workouts"))
91 | // here we reference workouts in the workout table
92 | export function getComputedHighlightsForDocumentAvoidingCircular(
93 | textDocument: TextDocument,
94 | sheetConfigIdToGetFirstColumnOnly: string
95 | ): IComputedValue {
96 | return computed(() => {
97 | return textDocument.sheets.flatMap((sheet) =>
98 | getComputedSheetValue(
99 | textDocument.id,
100 | sheet.configId,
101 | sheet.configId === sheetConfigIdToGetFirstColumnOnly
102 | )
103 | .get()
104 | .filter((r): r is Highlight => isValueRowHighlight(r))
105 | );
106 | });
107 | }
108 |
--------------------------------------------------------------------------------
/documents/plant-tracker.js:
--------------------------------------------------------------------------------
1 | (() => {
2 | const day = 24 * 60 * 60 * 1000
3 |
4 | const date1 = new Date(Date.now() - (7 * day))
5 | const date2 = new Date(Date.now() - (4 * day))
6 | const date3 = new Date(Date.now() - (4 * day))
7 | const date4 = new Date(Date.now() - (4 * day))
8 |
9 | function dateToString (date) {
10 | return `${(date.getMonth() + 1)}/${date.getDate()}/${date.getFullYear()}`
11 | }
12 |
13 | return {
14 | "textDocument": {
15 | "id": "plant-watering",
16 | "name": "Plant Watering Tracker",
17 | "text": [
18 | "## Plants",
19 | "",
20 | "🌱 Fiddle leaf: every 6 days, last watered on " + dateToString(date1),
21 | "🌱 Montsera: every 4 days, last watered on " + dateToString(date2),
22 | "🌱 Yuzu tree: every 5 days, last watered on " + dateToString(date3),
23 | "- The Yuzu tree needs some holes in the pot so that water can drain.",
24 | "🌱 Pine Bonsai: every 4 days, last watered on " + dateToString(date4)
25 | ],
26 | "sheets": [
27 | {
28 | "id": "_j3k9Ygb6rLwbSyd5vinuv",
29 | "configId": "plant-watering.2",
30 | "hideHighlightsInDocument": false
31 | },
32 | {
33 | "id": "_a8dvDDZ1nZurQsdKLnzjR",
34 | "configId": "plant-watering.1",
35 | "hideHighlightsInDocument": false
36 | },
37 | {
38 | "id": "_FcKOW7acW3QAUd18PMGAt",
39 | "configId": "markdown",
40 | "hideHighlightsInDocument": true
41 | }
42 | ]
43 | },
44 | "sheetConfigs": [
45 | {
46 | "id": "plant-watering.2",
47 | "name": "schedule",
48 | "properties": [
49 | {
50 | "name": "$",
51 | "formula": "{dates:lastWatered}",
52 | "visibility": "HIDDEN"
53 | },
54 | {
55 | "name": "daysSince",
56 | "formula": "Round((Date.now() - Date.parse(lastWatered)) / (24 * 60 * 60 * 1000), 1)",
57 | "visibility": "HIDDEN"
58 | },
59 | {
60 | "name": "needsWater",
61 | "formula": "daysSince > PrevOfType($, \"interval\").data.intervalInt",
62 | "visibility": "HIDDEN"
63 | },
64 | {
65 | "name": "color",
66 | "formula": "needsWater ? \"red\": \"green\"",
67 | "visibility": "STYLE"
68 | },
69 | {
70 | "name": "button",
71 | "formula": "TemplateButton($, \"🚿\", DateTime.now().toLocaleString(), \"replace\")",
72 | "visibility": "INLINE"
73 | }
74 | ]
75 | },
76 | {
77 | "id": "plant-watering.1",
78 | "name": "interval",
79 | "properties": [
80 | {
81 | "name": "$",
82 | "formula": "every {number:interval} days",
83 | "visibility": "HIDDEN"
84 | },
85 | {
86 | "name": "intervalInt",
87 | "formula": "ParseInt(interval)",
88 | "visibility": "HIDDEN"
89 | },
90 | {
91 | "name": "daysSince",
92 | "formula": "Round((Date.now() - Date.parse(dates)) / (24 * 60 * 60 * 1000), 1)",
93 | "visibility": "HIDDEN"
94 | },
95 | {
96 | "name": "needsWater",
97 | "formula": "daysSince > intervalInt",
98 | "visibility": "HIDDEN"
99 | }
100 | ]
101 | },
102 | {
103 | "id": "markdown",
104 | "name": "markdown",
105 | "properties": [
106 | {
107 | "name": "$",
108 | "formula": "=Markdown()",
109 | "visibility": "HIDDEN"
110 | },
111 | {
112 | "name": "type",
113 | "formula": "$.data.type",
114 | "visibility": "HIDDEN"
115 | },
116 | {
117 | "name": "font-weight",
118 | "formula": "type.startsWith(\"h\") || type === \"bold\" ? \"bold\" : \"normal\"",
119 | "visibility": "STYLE"
120 | },
121 | {
122 | "name": "font-style",
123 | "formula": "type === \"italic\" ? \"italic\" : \"normal\"",
124 | "visibility": "STYLE"
125 | },
126 | {
127 | "name": "font-size",
128 | "formula": "({\"h1\": \"1.3rem\",\n \"h2\": \"1rem\"}[type]) || \"normal\"",
129 | "visibility": "STYLE"
130 | }
131 | ]
132 | }
133 | ]
134 | }
135 | })()
--------------------------------------------------------------------------------
/src/highlight.ts:
--------------------------------------------------------------------------------
1 | import { sheetConfigsMobx, SheetValueRow, Span, textDocumentsMobx } from "./primitives";
2 | import { coerceValueToNumber, getTextForHighlight, isNumericish, isValueRowHighlight } from "./utils";
3 | import { isNaN, isString, orderBy, get, pick, sortBy, isEqual, groupBy} from "lodash";
4 | import { getComputedSheetValue } from "./compute";
5 |
6 | export class Highlight {
7 | constructor(
8 | readonly documentId: string,
9 | readonly sheetConfigId: string, // sheet config id should be optional to represent sub highlights in patterns, for now just set the sheetConfigId to empty string to represent undefined configId
10 | readonly span: Span,
11 | readonly data: { [columnName: string]: any },
12 | ) {
13 | }
14 |
15 | allPrev(type: string | string[]) {
16 | const typeSheetConfigs = Array.from(sheetConfigsMobx.values()).filter(
17 | (sheetConfig) =>
18 | isString(type)
19 | ? sheetConfig.name === type
20 | : type.includes(sheetConfig.name)
21 | );
22 |
23 | return orderBy(
24 | typeSheetConfigs
25 | .flatMap((typeSheetConfig) => {
26 | const firstColumnOnly =
27 | typeSheetConfig.id === this.sheetConfigId;
28 |
29 | return getComputedSheetValue(this.documentId, typeSheetConfig.id, firstColumnOnly).get()
30 | })
31 | .filter((row) => (
32 | "span" in row &&
33 | row.span[1] < this.span[0]
34 | )) as Highlight[],
35 |
36 | [({ span }) => span[0]],
37 | ['desc']
38 | );
39 | }
40 |
41 | prev(type: string | string[]) {
42 | return this.allPrev(type)[0]
43 | }
44 |
45 | allNext(type: string | string[]) {
46 | const typeSheetConfigs = Array.from(sheetConfigsMobx.values()).filter(
47 | (sheetConfig) =>
48 | isString(type)
49 | ? sheetConfig.name === type
50 | : type.includes(sheetConfig.name)
51 | );
52 |
53 | return orderBy(
54 | typeSheetConfigs
55 | .flatMap((typeSheetConfig) => {
56 | const firstColumnOnly =
57 | typeSheetConfig.id === this.sheetConfigId;
58 |
59 | return getComputedSheetValue(this.documentId, typeSheetConfig.id, firstColumnOnly).get()
60 | })
61 | .filter((row) => (
62 | "span" in row &&
63 | row.span[0] > this.span[1]
64 | )) as Highlight[],
65 |
66 | [({ span }) => span[0]],
67 | ['asc']
68 | );
69 | }
70 |
71 | next(type: string | string[]) {
72 | return this.allNext(type)[0]
73 | }
74 |
75 | valueOf() {
76 | const spanText = this.text()
77 |
78 | if (!spanText) {
79 | return
80 | }
81 |
82 | if (isNumericish(spanText)) {
83 | return parseFloat(spanText)
84 | }
85 |
86 | return spanText
87 | }
88 |
89 | toString() {
90 | return this.text()
91 | }
92 |
93 | text() {
94 | return getTextForHighlight(this)
95 | }
96 |
97 | isEqualTo(value: any) {
98 | if (isValueRowHighlight(value)) {
99 | return this.text() === value.text()
100 | }
101 |
102 | return this.text() === value
103 | }
104 |
105 | wholeLine () {
106 | const textDocument = textDocumentsMobx.get(this.documentId)
107 |
108 | if (!textDocument) {
109 | return
110 | }
111 |
112 | const fromLine = textDocument.text.lineAt(this.span[0])
113 | const toLine = textDocument.text.lineAt(this.span[0])
114 |
115 | return Highlight.from({
116 | ...this,
117 | span: [fromLine.from, toLine.to]
118 | })
119 | }
120 |
121 | static from(highlight: {
122 | documentId: string,
123 | sheetConfigId: string,
124 | span: Span,
125 | data: { [columnName: string]: any },
126 | }) {
127 | return new Highlight(highlight.documentId, highlight.sheetConfigId, highlight.span, highlight.data)
128 | }
129 | }
130 |
131 |
132 | Object.defineProperty(Array.prototype, 'sumOf', {
133 | value: function (path?: string) {
134 | let sum = 0;
135 |
136 | this.forEach((item: any) => {
137 | const number = coerceValueToNumber(path ? get(item, path) : item)
138 |
139 | console.log(item, number, path)
140 |
141 | if (!isNaN(number)) {
142 | sum += number
143 | }
144 | })
145 |
146 | return sum;
147 | }
148 | });
149 |
150 | Object.defineProperty(Array.prototype, "sortBy", {
151 | value: function (fn: (item: any) => any) {
152 | return sortBy(this, fn);
153 | }
154 | })
155 |
156 | Object.defineProperty(Array.prototype, "isEqual", {
157 | value: function (value: any) {
158 | return isEqual(this, value)
159 | }
160 | })
161 |
162 | Object.defineProperty(Array.prototype, "groupBy", {
163 | value: function (fn: (item: any) => any) {
164 | return Object.entries(groupBy(this, fn)).map(([group, items]) => ({group, items }))
165 | }
166 | })
--------------------------------------------------------------------------------
/documents/coffee.json:
--------------------------------------------------------------------------------
1 | {
2 | "textDocument": {
3 | "id": "aeropress",
4 | "name": "☕️ James Hoffmann Aeropress",
5 | "text": [
6 | "## Recipe",
7 | "Grind 11 g coffee, medium-fine. ",
8 | "Add 200 g water, brew 2 minutes, plunge!",
9 | "",
10 | "scale by",
11 | "",
12 | "## Notes",
13 | "6/22/22: Pretty good, but forgot to swirl.",
14 | "6/23/22: Felt weak and under-extracted. Grind finer?"
15 | ],
16 | "sheets": [
17 | {
18 | "id": "eiIXzGv8Zq1zybgMZdd1z",
19 | "configId": "common.number",
20 | "hideHighlightsInDocument": false
21 | },
22 | {
23 | "id": "X8bBiHSTseJNK4lyNkl2D",
24 | "configId": "food.quantity",
25 | "hideHighlightsInDocument": false
26 | },
27 | {
28 | "id": "JdoUlaXUw0gg15KLpYHp6",
29 | "configId": "common.duration",
30 | "hideHighlightsInDocument": false
31 | },
32 | {
33 | "id": "fUpyOI1wmdDbCnuq6dNfU",
34 | "configId": "food.scale",
35 | "hideHighlightsInDocument": false
36 | },
37 | {
38 | "id": "PL4sKTsqJcM18ppBJIviZ",
39 | "configId": "common.date",
40 | "hideHighlightsInDocument": false
41 | },
42 | {
43 | "id": "Htzihyo06gMLB1lCrEl24",
44 | "configId": "markdown",
45 | "hideHighlightsInDocument": false
46 | }
47 | ]
48 | },
49 | "sheetConfigs": [
50 | {
51 | "id": "common.number",
52 | "name": "number",
53 | "properties": [
54 | {
55 | "name": "$",
56 | "formula": "{/[0-9][0-9\\.]*/}",
57 | "visibility": "HIDDEN"
58 | },
59 | {
60 | "name": "value",
61 | "formula": "ParseFloat($)",
62 | "visibility": "HIDDEN"
63 | }
64 | ]
65 | },
66 | {
67 | "id": "food.quantity",
68 | "name": "quantity",
69 | "properties": [
70 | {
71 | "name": "$",
72 | "formula": "{number:amount} {/(cup|tablespoon|tbsp|teaspoon|tsp|pound|lb|gram|g|milliliter|ml)s?/:unit}",
73 | "visibility": "HIDDEN"
74 | },
75 | {
76 | "name": "scaleFactor",
77 | "formula": "Find(\"scale\")?.data.sliderValue",
78 | "visibility": "HIDDEN"
79 | },
80 | {
81 | "name": "scaledAmount",
82 | "formula": "(scaleFactor && scaleFactor !== 1 && amount) ? `${scaleFactor * amount.data.value} ${unit}${amount.data.value === 1 ? 's' : ''}` : undefined",
83 | "visibility": "REPLACE"
84 | }
85 | ]
86 | },
87 | {
88 | "id": "common.duration",
89 | "name": "durations",
90 | "properties": [
91 | {
92 | "name": "$",
93 | "formula": "{/((\\\\d+\\\\s+(hours?|minutes?|seconds?))\\\\s*)*(\\\\d+\\\\s+(hours?|minutes?|seconds?))/}",
94 | "visibility": "HIDDEN"
95 | },
96 | {
97 | "name": "timer",
98 | "formula": "Timer($)",
99 | "visibility": "INLINE"
100 | }
101 | ]
102 | },
103 | {
104 | "id": "food.scale",
105 | "name": "scale",
106 | "properties": [
107 | {
108 | "name": "$",
109 | "formula": "scale by",
110 | "visibility": "HIDDEN"
111 | },
112 | {
113 | "name": "slider",
114 | "formula": "Slider($)",
115 | "visibility": "SUPERSCRIPT"
116 | },
117 | {
118 | "name": "sliderValue",
119 | "formula": "slider.data.value",
120 | "visibility": "INLINE"
121 | }
122 | ]
123 | },
124 | {
125 | "id": "common.date",
126 | "name": "dates",
127 | "properties": [
128 | {
129 | "name": "date",
130 | "formula": "{number:month}/{number:day}/{number:year}",
131 | "visibility": "HIDDEN"
132 | }
133 | ]
134 | },
135 | {
136 | "id": "markdown",
137 | "name": "markdown",
138 | "properties": [
139 | {
140 | "name": "$",
141 | "formula": "=Markdown()",
142 | "visibility": "HIDDEN"
143 | },
144 | {
145 | "name": "type",
146 | "formula": "$.data.type",
147 | "visibility": "HIDDEN"
148 | },
149 | {
150 | "name": "font-weight",
151 | "formula": "type.startsWith(\"h\") || type === \"bold\" ? \"bold\" : \"normal\"",
152 | "visibility": "STYLE"
153 | },
154 | {
155 | "name": "font-style",
156 | "formula": "type === \"italic\" ? \"italic\" : \"normal\"",
157 | "visibility": "STYLE"
158 | },
159 | {
160 | "name": "font-size",
161 | "formula": "({\"h1\": \"1.3rem\",\n \"h2\": \"1rem\"}[type]) || \"normal\"",
162 | "visibility": "STYLE"
163 | }
164 | ]
165 | }
166 | ]
167 | }
--------------------------------------------------------------------------------