├── .github └── workflows │ └── main.yml ├── .gitignore ├── .vscode ├── launch.json └── tasks.json ├── CONTRIBUTING.md ├── DEVELOPMENT.md ├── Dockerfile ├── LICENSE ├── README.md ├── SELF_HOSTING.md ├── app ├── assets │ ├── home │ │ ├── JsonHero2.mp4 │ │ ├── JsonHeroSearch.mp4 │ │ ├── JsonHeroShare.mp4 │ │ └── UncoverEdgeCases.mp4 │ ├── images │ │ ├── opengraph.png │ │ ├── td-triangle.png │ │ ├── trigger-dev-logo-dark.png │ │ └── trigger-dev-logo.png │ └── svgs │ │ ├── CopyIcon.svg │ │ ├── EyeIcon.svg │ │ └── TickIcon.svg ├── bindings.d.ts ├── components │ ├── AutoplayVideo.tsx │ ├── BlankColumn.tsx │ ├── CodeEditor.tsx │ ├── CodeViewer.tsx │ ├── Column.tsx │ ├── ColumnItem.tsx │ ├── Columns.tsx │ ├── ContainerInfo.tsx │ ├── CopySelectedNode.tsx │ ├── CopyText.tsx │ ├── CopyTextButton.tsx │ ├── DataTable.tsx │ ├── DocumentTitle.tsx │ ├── DragAndDropForm.tsx │ ├── ExampleDoc.tsx │ ├── ExampleUrl.tsx │ ├── FileSelector │ │ └── FileDropzone.tsx │ ├── Footer.tsx │ ├── Header.tsx │ ├── Home │ │ ├── HomeApiHeroBanner.tsx │ │ ├── HomeApiHeroLaptop.tsx │ │ ├── HomeCollaborateSection.tsx │ │ ├── HomeEdgeCasesSection.tsx │ │ ├── HomeFeatureGridSection.tsx │ │ ├── HomeFooter.tsx │ │ ├── HomeGithubBanner.tsx │ │ ├── HomeGridFeatureItem.tsx │ │ ├── HomeHeader.tsx │ │ ├── HomeHeroSection.tsx │ │ ├── HomeInfoBoxSection.tsx │ │ ├── HomeSearchSection.tsx │ │ ├── HomeSection.tsx │ │ └── HomeSplitSection.tsx │ ├── Icons │ │ ├── ArrayIcon.tsx │ │ ├── ArrowKeysIcon.tsx │ │ ├── ArrowKeysUpDownIcon.tsx │ │ ├── CopyShortcutIcon.tsx │ │ ├── DiscordIcon.tsx │ │ ├── DiscordIconTransparent.tsx │ │ ├── EmailIcon.tsx │ │ ├── EmailIconTransparent.tsx │ │ ├── EscapeKeyIcon.tsx │ │ ├── GithubIcon.tsx │ │ ├── GithubIconSimple.tsx │ │ ├── LoadingIcon.tsx │ │ ├── Logo.tsx │ │ ├── LogoTriggerdotdev.tsx │ │ ├── MoonIcon.tsx │ │ ├── ObjectIcon.tsx │ │ ├── ShortcutIcon.tsx │ │ ├── SquareBracketsIcon.tsx │ │ ├── StringIcon.tsx │ │ ├── SunIcon.tsx │ │ ├── TreeIcon.tsx │ │ └── TwitterIcon.tsx │ ├── IndentPreference.tsx │ ├── InfoHeader.tsx │ ├── InfoPanel.tsx │ ├── JsonColumnView.tsx │ ├── JsonEditor.tsx │ ├── JsonPreview.tsx │ ├── JsonSchemaViewer.tsx │ ├── JsonTreeView.tsx │ ├── JsonView.tsx │ ├── NewDocument.tsx │ ├── NewFile.tsx │ ├── OpenInWindow.tsx │ ├── PathBar.tsx │ ├── PathPreview.tsx │ ├── PreferencesProvider.tsx │ ├── Preview │ │ ├── CalendarMonth.tsx │ │ ├── PreviewBox.tsx │ │ ├── PreviewProperties.tsx │ │ ├── PreviewValue.tsx │ │ └── Types │ │ │ ├── PreviewAudioUri.tsx │ │ │ ├── PreviewDate.tsx │ │ │ ├── PreviewHtml.tsx │ │ │ ├── PreviewIPFSImage.tsx │ │ │ ├── PreviewImage.tsx │ │ │ ├── PreviewImageUri.tsx │ │ │ ├── PreviewJson.tsx │ │ │ ├── PreviewString.tsx │ │ │ ├── PreviewUri.tsx │ │ │ ├── PreviewUriElement.tsx │ │ │ ├── PreviewVideoUri.tsx │ │ │ ├── RetweetIcon.tsx │ │ │ └── preview.types.d.ts │ ├── Primitives │ │ ├── Body.tsx │ │ ├── BodyBold.tsx │ │ ├── ExtraLargeTitle.tsx │ │ ├── LargeMono.tsx │ │ ├── LargeTitle.tsx │ │ ├── Mono.tsx │ │ ├── PageNotFoundTitle.tsx │ │ ├── SmallBody.tsx │ │ ├── SmallSubtitle.tsx │ │ ├── SmallTitle.tsx │ │ └── Title.tsx │ ├── Properties │ │ ├── PropertiesFloat.tsx │ │ ├── PropertiesInt.tsx │ │ ├── PropertiesString.tsx │ │ └── PropertiesValue.tsx │ ├── RelatedValues.tsx │ ├── Resizable.tsx │ ├── SampleUrls.tsx │ ├── SearchBar.tsx │ ├── SearchPalette.tsx │ ├── Share.tsx │ ├── SideBar.tsx │ ├── StarCountProvider.tsx │ ├── ThemeModeToggle.tsx │ ├── ThemeProvider.tsx │ ├── ToolTip.tsx │ ├── UI │ │ ├── Dialog.tsx │ │ ├── GithubStar.tsx │ │ ├── GithubStarSmall.tsx │ │ ├── Popover.tsx │ │ ├── Tabs.tsx │ │ └── ToastPopover.tsx │ ├── UrlForm.tsx │ ├── ValueIcon.tsx │ └── json-schema-map.d.ts ├── entry.client.tsx ├── entry.server.tsx ├── entry.worker.ts ├── graphJSON.server.ts ├── hooks │ ├── useClickOutside.tsx │ ├── useIsMounted.tsx │ ├── useJson.tsx │ ├── useJsonColumnView.tsx │ ├── useJsonDoc.tsx │ ├── useJsonSchema.tsx │ ├── useJsonSearch.tsx │ ├── useJsonTree.tsx │ ├── useLoadWhenOnline.tsx │ ├── useMemoCompare.ts │ ├── useOnScreen.tsx │ ├── useRelatedPaths.ts │ ├── useSelectedInfo.tsx │ └── useVirtualTree.ts ├── jsonDoc.server.ts ├── root.tsx ├── routes │ ├── actions │ │ ├── $id │ │ │ └── update.ts │ │ ├── createFromFile.ts │ │ ├── createFromUrl.ts │ │ ├── getPreview.$url.ts │ │ └── setTheme.ts │ ├── api │ │ └── create[.json].ts │ ├── index.tsx │ ├── j │ │ ├── $id.tsx │ │ ├── $id │ │ │ ├── editor.tsx │ │ │ ├── index.tsx │ │ │ ├── terminal.tsx │ │ │ └── tree.tsx │ │ └── $id[.json].ts │ ├── new.tsx │ └── privacy.mdx ├── services │ ├── apihero.server.ts │ ├── github.server.ts │ ├── toast.server.ts │ └── uriPreview.server.ts ├── theme.server.ts ├── useColumnView │ └── index.ts └── utilities │ ├── animationConstants.ts │ ├── classnames.ts │ ├── codeMirrorSetup.ts │ ├── codeMirrorTheme.ts │ ├── colors.ts │ ├── dataType.ts │ ├── formatStarCount.ts │ ├── formatter.ts │ ├── getRandomUserAgent.ts │ ├── icons.ts │ ├── inferredTemporal.ts │ ├── jsonColumnView.ts │ ├── nullable.ts │ ├── relatedValues.ts │ ├── safeFetch.ts │ ├── search.ts │ ├── stableJson.ts │ └── xml │ ├── __test__ │ ├── convertXmlToJsonString.test.ts │ ├── isXML.test.ts │ └── xml.txt │ ├── convertFromRawXml.ts │ ├── createFromRawXml.ts │ └── isXML.ts ├── examples ├── owenWilsonWows.json ├── pokemon.json └── ronSwansonQuotes.json ├── package-lock.json ├── package.json ├── public ├── favicon.ico └── fonts │ └── MonoLisa │ ├── woff │ ├── MonoLisa-Regular.woff │ └── MonoLisa-RegularItalic.woff │ └── woff2 │ ├── MonoLisa-Regular.woff2 │ └── MonoLisa-RegularItalic.woff2 ├── remix.config.js ├── remix.env.d.ts ├── styles └── tailwind.css ├── tailwind.config.js ├── tests ├── formatStarCount.test.ts ├── jsonColumnView.test.ts ├── relatedValues.test.ts ├── search.test.ts ├── setup.js └── stableJson.test.ts ├── tsconfig.json ├── worker └── index.js ├── wrangler.toml └── wrangler.toml.dev /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | paths: 7 | - ".github/workflows/main.yml" 8 | - "app/**/*.ts" 9 | - "app/**/*.tsx" 10 | - "public/*" 11 | - "styles/*" 12 | - "worker/*" 13 | - "tests/*" 14 | - "package.json" 15 | - "package-lock.json" 16 | - "remix.config.js" 17 | - "tsconfig.json" 18 | - "wrangler.toml" 19 | - "remix.env.d.ts" 20 | - "tailwind.config.js" 21 | 22 | workflow_dispatch: 23 | 24 | jobs: 25 | build: 26 | runs-on: ubuntu-latest 27 | 28 | steps: 29 | - uses: actions/checkout@v4 30 | - uses: actions/setup-node@v4 31 | with: 32 | node-version: "20" 33 | registry-url: "https://registry.npmjs.org" 34 | - run: npm ci 35 | - run: npm test 36 | - run: npm run build 37 | - name: Publish app 38 | uses: cloudflare/wrangler-action@1.3.0 39 | with: 40 | apiToken: ${{ secrets.CF_API_TOKEN }} 41 | environment: "production" 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | 3 | /.cache 4 | /build 5 | /public/build 6 | .env 7 | /app/tailwind.css 8 | /jsonDocs 9 | .DS_Store 10 | /dist 11 | .mf 12 | /meta.json 13 | /stats.html 14 | public/entry.worker.js -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "pwa-chrome", 9 | "request": "launch", 10 | "name": "Launch Chrome against localhost with document", 11 | "url": "http://localhost:8787", 12 | "webRoot": "${workspaceFolder}/app" 13 | }, 14 | { 15 | "name": "Debug Jest All Tests", 16 | "type": "node", 17 | "request": "launch", 18 | "runtimeArgs": [ 19 | "--inspect-brk", 20 | "${workspaceRoot}/node_modules/.bin/jest", 21 | "--runInBand" 22 | ], 23 | "console": "integratedTerminal", 24 | "internalConsoleOptions": "neverOpen" 25 | }, 26 | { 27 | "name": "Debug Jest Test File", 28 | "type": "node", 29 | "request": "launch", 30 | "runtimeArgs": [ 31 | "--inspect-brk", 32 | "${workspaceRoot}/node_modules/.bin/jest", 33 | "--runInBand" 34 | ], 35 | "args": ["${fileBasename}", "--no-cache"], 36 | "console": "integratedTerminal", 37 | "internalConsoleOptions": "neverOpen" 38 | } 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "type": "typescript", 6 | "tsconfig": "tsconfig.json", 7 | "option": "watch", 8 | "problemMatcher": ["$tsc-watch"], 9 | "group": "build", 10 | "label": "tsc: watch - tsconfig.json" 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## ⚡️ JSON Hero Contributing Guide 2 | 3 | First of all, thanks for considering contributing to this project! If you have any questions please don't hesitate to reach out to [eric@jsonhero.io](mailto:eric@jsonhero.io) or join us on [Discord](https://discord.gg/JtBAxBr2m3). 4 | 5 | JSON Hero is a Typescript React application built with [remix.run](https://remix.run), with support for deploying to Cloudflare workers. 6 | 7 | To get started with contributing, please read our [Development guide](https://github.com/triggerdotdev/jsonhero-web/blob/main/DEVELOPMENT.md) first to get JSON Hero running locally. 8 | 9 | ### Running tests 10 | 11 | Although there is less test-coverage for JSON Hero than there should be, tests should still be run to ensure builds have not been broken: 12 | 13 | ```bash 14 | npm test 15 | ``` 16 | 17 | You can also run tests in "watch" mode: 18 | 19 | ```bash 20 | npm run test:watch 21 | ``` 22 | 23 | ### Making changes 24 | 25 | Please make any changes to your forked repository in a branch other than `main`. If you are working on a bug fix, please use the `bug/` prefix for your branch name. If you are working on a feature, please use `features/`. If you are working on a specific issue please name the branch `issue-` 26 | 27 | Make sure to run the `npm lint` command to ensure there are no Typescript compile-time errors. 28 | 29 | ### Pull Requests 30 | 31 | Please open a Pull Request against the `main` branch in the `triggerdotdev/jsonhero-web` repository. We will aim to address all newly opened PRs by the following Friday. If you haven't opened a Pull Request before, please check out GitHub's [Pull Request documentation](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests) 32 | 33 | ### Other JSON Hero projects 34 | 35 | If you'd like to contribute to the [VSCode extension](https://marketplace.visualstudio.com/items?itemName=JSONHero.jsonhero-vscode), please see the [triggerdotdev/vscode-extension](https://github.com/triggerdotdev/vscode-extension) repo. 36 | 37 | For issues related to the JSON Schema inference, please check out [triggerdotdev/schema-infer](https://github.com/triggerdotdev/schema-infer). 38 | 39 | The "Smart Preview" feature is in-part powered by the [@jsonhero/json-infer-types](https://github.com/triggerdotdev/json-infer-types) project. 40 | 41 | If it's related to the Search functionality, please see the [triggerdotdev/fuzzy-json-search](https://github.com/triggerdotdev/fuzzy-json-search) repo. 42 | -------------------------------------------------------------------------------- /DEVELOPMENT.md: -------------------------------------------------------------------------------- 1 | ## 👩🏽‍💻 JSON Hero Local Development Guide 2 | 3 | Welcome to JSON Hero development and thanks for being here! If you'd like to run JSON Hero locally, please use the following guide to get started. If you have any issues with this guide please feel free to email me at [eric@jsonhero.io](mailto:eric@jsonhero.io) or come leave a message in our open [Discord Channel](https://discord.gg/JtBAxBr2m3). 4 | 5 | For more information about contributing to JSON Hero please see the [Contributing doc](https://github.com/triggerdotdev/jsonhero-web/blob/main/CONTRIBUTING.md). 6 | 7 | ### Install dependencies 8 | 9 | Before you can run JSON Hero locally, you will need to install the following dependencies on your machine: 10 | 11 | #### Git 12 | 13 | You most likely already have git installed on your machine, but if not, you can install it from the [Git website](https://git-scm.com). 14 | 15 | #### Node.js 16 16 | 17 | Even though JSON Hero runs on [Cloudflare Workers](https://workers.cloudflare.com), which isn't a Node.js environment, you will still need Node.js 16 to run it locally. The recommended way to install Node.js is to download a pre-built package from the [Node.js website](https://nodejs.org/en/) 18 | 19 | #### NPM 20 | 21 | If you install Node.js through the above link, you should also have NPM automatically installed as well. To make sure, run the following command in your preferred Terminal: 22 | 23 | ```bash 24 | npm ---version 25 | ``` 26 | 27 | ### Fork JSON Hero on GitHub (optional) 28 | 29 | To contribute code to JSON Hero, you should first create a fork of the [jsonhero-web](https://github.com/triggerdotdev/jsonhero-web) repository on GitHub. Follow [these instructions](https://docs.github.com/en/get-started/quickstart/fork-a-repo) on repository forking. 30 | 31 | ### Clone the repo 32 | 33 | In your terminal, issue the following command to clone the repository to your local machine: 34 | 35 | ```bash 36 | git clone https://github.com/triggerdotdev/jsonhero-web.git 37 | ``` 38 | 39 | Or if you've forked the repository: 40 | 41 | ```bash 42 | git clone https://github.com//jsonhero-web.git 43 | ``` 44 | 45 | Then `cd` into the repository: 46 | 47 | ```bash 48 | cd jsonhero-web 49 | ``` 50 | 51 | ### Prepare the repo 52 | 53 | First, install npm dependencies: 54 | 55 | ```bash 56 | npm install 57 | ``` 58 | 59 | Run the following command to create the `.env` file with a new `SESSION_SECRET` environment variable: 60 | 61 | ```bash 62 | echo "SESSION_SECRET=$(openssl rand -hex 32)" > .env 63 | ``` 64 | 65 | Then, run `npm run build` or `npm run dev` to build. 66 | 67 | Start the development server: 68 | 69 | ```bash 70 | npm start 71 | ``` 72 | 73 | You should now be able to access your local JSON Hero server on [localhost:8787](http://localhost:8787) 74 | 75 | > **Note** JSON documents created locally are not persisted across server restarts 76 | 77 | ### Previewing URLs 78 | 79 | We currently use [OpenGraph Ninja](https://opengraph.ninja/) to power some of the Preview URL functionality. 80 | 81 | ### Deploying to Cloudflare 82 | 83 | _Coming Soon_ 84 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Builder 2 | FROM node:16.17.0 as builder 3 | WORKDIR /src 4 | COPY . /src 5 | 6 | # App 7 | RUN cd /src 8 | RUN npm install 9 | RUN echo "SESSION_SECRET=abc123" > .env 10 | RUN npm run build 11 | 12 | CMD npm start 13 | -------------------------------------------------------------------------------- /SELF_HOSTING.md: -------------------------------------------------------------------------------- 1 | ## Deploying to Cloudflare 2 | 3 | ### Install and login to wrangler 4 | ```bash 5 | npm install -g wrangler 6 | wrangler login 7 | ``` 8 | 9 | ### Create service 10 | Go to workers tab from your [cloudflare profile](https://dash.cloudflare.com/profile) and create a new worker. Use HTTP Handler as service type. The name of worker must match the `name` field in `wrangler.toml`. 11 | 12 | ### Setup wrangler.toml 13 | Edit the following variables in `wrangler.toml` and `wrangler.toml.dev`: 14 | - `account_id`: Get account id by using 15 | ```bash 16 | wrangler whoami 17 | ``` 18 | - `kv_namespaces`: Run the following comands to create a new KV namespace. 19 | ```bash 20 | wrangler kv:namespace create DOCUMENTS # gives namespace id 21 | wrangler kv:namespace create DOCUMENTS --preview # gives preview id for namespace 22 | ``` 23 | Replace current entry for `kv_namespaces` as: 24 | ```toml 25 | kv_namespaces = [ 26 | { binding = "DOCUMENTS", id = , preview_id = } 27 | ] 28 | ``` 29 | 30 | ### Configure Environment Variables 31 | Set `SESSION_SECRET` environment for worker. 32 | ```bash 33 | wrangler secret put SESSION_SECRET 34 | ``` 35 | Optionally set other secrets listed at the end of `wrangler.toml`. 36 | 37 | ### Publish worker 38 | ```bash 39 | wrangler publish 40 | ``` 41 | -------------------------------------------------------------------------------- /app/assets/home/JsonHero2.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/triggerdotdev/jsonhero-web/d33e7ccab9f07557fb1462d95419f90b41f0b2d0/app/assets/home/JsonHero2.mp4 -------------------------------------------------------------------------------- /app/assets/home/JsonHeroSearch.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/triggerdotdev/jsonhero-web/d33e7ccab9f07557fb1462d95419f90b41f0b2d0/app/assets/home/JsonHeroSearch.mp4 -------------------------------------------------------------------------------- /app/assets/home/JsonHeroShare.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/triggerdotdev/jsonhero-web/d33e7ccab9f07557fb1462d95419f90b41f0b2d0/app/assets/home/JsonHeroShare.mp4 -------------------------------------------------------------------------------- /app/assets/home/UncoverEdgeCases.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/triggerdotdev/jsonhero-web/d33e7ccab9f07557fb1462d95419f90b41f0b2d0/app/assets/home/UncoverEdgeCases.mp4 -------------------------------------------------------------------------------- /app/assets/images/opengraph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/triggerdotdev/jsonhero-web/d33e7ccab9f07557fb1462d95419f90b41f0b2d0/app/assets/images/opengraph.png -------------------------------------------------------------------------------- /app/assets/images/td-triangle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/triggerdotdev/jsonhero-web/d33e7ccab9f07557fb1462d95419f90b41f0b2d0/app/assets/images/td-triangle.png -------------------------------------------------------------------------------- /app/assets/images/trigger-dev-logo-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/triggerdotdev/jsonhero-web/d33e7ccab9f07557fb1462d95419f90b41f0b2d0/app/assets/images/trigger-dev-logo-dark.png -------------------------------------------------------------------------------- /app/assets/images/trigger-dev-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/triggerdotdev/jsonhero-web/d33e7ccab9f07557fb1462d95419f90b41f0b2d0/app/assets/images/trigger-dev-logo.png -------------------------------------------------------------------------------- /app/assets/svgs/CopyIcon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/assets/svgs/EyeIcon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/assets/svgs/TickIcon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/bindings.d.ts: -------------------------------------------------------------------------------- 1 | export {}; 2 | 3 | declare global { 4 | const DOCUMENTS: KVNamespace; 5 | const SESSION_SECRET: string; 6 | const GRAPH_JSON_API_KEY: string; 7 | const GRAPH_JSON_COLLECTION: string; 8 | const APIHERO_PROJECT_KEY: string; 9 | } 10 | -------------------------------------------------------------------------------- /app/components/AutoplayVideo.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef } from "react"; 2 | import { useOnScreen } from "~/hooks/useOnScreen"; 3 | 4 | export function AutoplayVideo({ src }: { src: string }) { 5 | const elementRef = useRef(null); 6 | const isOnScreen = useOnScreen(elementRef); 7 | 8 | useEffect(() => { 9 | if (elementRef.current == null) return; 10 | 11 | elementRef.current.muted = true; 12 | elementRef.current.playsInline = true; 13 | 14 | if (isOnScreen) { 15 | elementRef.current.play(); 16 | } else { 17 | elementRef.current.pause(); 18 | } 19 | }, [isOnScreen]); 20 | 21 | return ( 22 |