Author SHA1 Message Date
James Ritchie 2a3219989f WIP Reconfiguring the app layout to not use calculated heights 2022-05-13 14:00:50 +01:00
James Ritchie e8e08899dd WIP testing a css grid layout for the main app 2022-05-13 09:55:43 +01:00
Eric Allam d4c23be1b6 Move the examples out of app 2022-05-12 10:08:31 +01:00
James Ritchie 35082d9c43 Small header tweak to fix mobile portrait 2022-05-07 18:33:32 +01:00
D-K-P 48ac6bcdf7 Added Twitter icon to the header / footer of the homepage 2022-05-06 21:13:20 +01:00
James Ritchie 7e46e60d21 Added some example JSON files to help when running locally 2022-05-06 17:12:33 +01:00
Eric Allam 927cc26669 Convert the rest of the samples to ExampleDocs 2022-05-04 21:06:22 +01:00
Eric Allam 401205b57f Convert the Unsplash to the new ExampleDoc 2022-05-04 20:57:00 +01:00
Eric Allam a08fe62e78 Allow url documents to be injested, so it only hits the url once 2022-05-04 20:48:09 +01:00
Eric Allam fdd972b973 Allow creating read-only documents through the /new endpoint with readonly=true (fixes issue #48) 2022-05-03 22:36:27 +01:00
Eric Allam d793753a0a Make sure video element reloads if src changes 2022-05-03 22:05:51 +01:00
Eric Allam 6f548261a1 Adding additional logging to the $id endpoint 2022-05-03 16:39:40 +01:00
Eric Allam 635a97b133 Preview audio & video urls (fixes issue #46) 2022-05-03 15:09:19 +01:00
David Sanchez 3b0a5b80e7 New copy to clipboard shortcut feature and footer icon (#27) 2022-05-03 11:28:30 +01:00
Eric Allam cabd9e3379 Fixed the star count to exactly match github 2022-05-02 21:42:32 +01:00
Eric Allam 436f01e459 Add ttl option to the /new endpoint, allows setting a min 60s timeout 2022-05-02 21:30:19 +01:00
31 changed files with 1865 additions and 198 deletions
+9
View File
@@ -0,0 +1,9 @@
import { Body } from "./Primitives/Body";
export function Banner() {
return (
<div className="flex justify-center items-center w-screen bg-yellow-300 min-h-[30px]">
<Body>Test Banner</Body>
</div>
);
}
+1 -4
View File
@@ -94,10 +94,7 @@ export function CodeEditor(opts: CodeEditorProps) {
return (
<div>
<div
className="h-jsonViewerHeight overflow-y-auto no-scrollbar"
ref={editor}
/>
<div className="h-full overflow-y-auto no-scrollbar" ref={editor} />
</div>
);
}
+6 -59
View File
@@ -1,86 +1,33 @@
import { Title } from "./Primitives/Title";
import { colorForItemAtPath } from "~/utilities/colors";
import { ColumnViewNode, IconComponent } from "~/useColumnView";
import { IconComponent } from "~/useColumnView";
import { useJson } from "../hooks/useJson";
import { memo, useCallback, useMemo, useRef } from "react";
import { ColumnItem } from "./ColumnItem";
import { useJsonColumnViewAPI } from "~/hooks/useJsonColumnView";
import { useVirtual } from "react-virtual";
import { memo, useMemo } from "react";
export type ColumnProps = {
id: string;
title: string;
icon?: IconComponent;
hasHighlightedElement: boolean;
items: ColumnViewNode[];
selectedPath: string[];
highlightedPath: string[];
children: React.ReactNode;
};
function ColumnElement(column: ColumnProps) {
const { id, title, items, selectedPath, highlightedPath } = column;
const { id, title, children } = column;
const [json] = useJson();
const iconColor = useMemo(() => colorForItemAtPath(id, json), [id, json]);
const { goToNodeId } = useJsonColumnViewAPI();
const containerRef = useRef<HTMLDivElement>(null);
const rowVirtualizer = useVirtual({
size: items.length,
parentRef: containerRef,
estimateSize: useCallback(() => 36, []),
overscan: 5,
});
return (
<div
className={
"column flex-none border-r-[1px] border-slate-300 w-80 transition dark:border-slate-600"
"column flex-none overflow-x-auto no-scrollbar border-r-[1px] border-slate-300 w-80 transition dark:border-slate-600"
}
>
<div className="flex items-center text-slate-800 bg-slate-50 mb-[3px] p-2 pb-0 transition dark:bg-slate-900 dark:text-slate-300">
{column.icon && <column.icon className="h-6 w-6 mr-1" />}
<Title className="">{title}</Title>
</div>
<div
className="overflow-y-auto h-viewerHeight no-scrollbar"
ref={containerRef}
>
<div
style={{
height: `${rowVirtualizer.totalSize}px`,
width: "100%",
position: "relative",
}}
>
{rowVirtualizer.virtualItems.map((virtualRow) => {
const item = items[virtualRow.index];
return (
<div
key={virtualRow.index}
style={{
position: "absolute",
top: 0,
left: 0,
width: "100%",
height: `${virtualRow.size}px`,
transform: `translateY(${virtualRow.start}px)`,
}}
>
<ColumnItem
item={item}
json={json}
isSelected={selectedPath.includes(item.id)}
isHighlighted={
highlightedPath[highlightedPath.length - 1] === item.id
}
onClick={(id) => goToNodeId(id, "columnView")}
/>
</div>
);
})}
</div>
</div>
<div className="h-full">{children}</div>
</div>
);
}
+26 -6
View File
@@ -10,10 +10,17 @@ import { BlankColumn } from "./BlankColumn";
import { Column } from "./Column";
import { ColumnItem } from "./ColumnItem";
function ColumnsElement({ columns }: { columns: ColumnDefinition[] }) {
function ColumnsElement({
columns,
props,
}: {
columns: ColumnDefinition[];
props?: any;
}) {
const [json] = useJson();
const { selectedPath, highlightedPath, highlightedNodeId } =
useJsonColumnViewState();
const { goToNodeId } = useJsonColumnViewAPI();
const highlightedItemIsValue = useMemo<boolean>(() => {
if (highlightedNodeId == null) {
return false;
@@ -26,7 +33,10 @@ function ColumnsElement({ columns }: { columns: ColumnDefinition[] }) {
}, [highlightedPath, json]);
return (
<div className="columns flex flex-grow overflow-x-auto no-scrollbar focus:outline-none">
<div
className="all-columns flex h-full overflow-hidden focus:outline-none"
{...props}
>
{columns.map((column) => {
return (
<Column
@@ -37,10 +47,20 @@ function ColumnsElement({ columns }: { columns: ColumnDefinition[] }) {
hasHighlightedElement={
highlightedPath[highlightedPath.length - 2] === column.id
}
selectedPath={selectedPath}
highlightedPath={highlightedPath}
items={column.items}
/>
>
{column.items.map((item) => (
<ColumnItem
key={item.id}
item={item}
json={json}
isSelected={selectedPath.includes(item.id)}
isHighlighted={
highlightedPath[highlightedPath.length - 1] === item.id
}
onClick={(id) => goToNodeId(id, "columnView")}
/>
))}
</Column>
);
})}
{highlightedItemIsValue ? <BlankColumn /> : null}
+20
View File
@@ -0,0 +1,20 @@
import { useHotkeys } from "react-hotkeys-hook";
import { useSelectedInfo } from "../hooks/useSelectedInfo";
export function CopySelectedNodeShortcut() {
const selectedInfo = useSelectedInfo();
useHotkeys(
'shift+c,shift+C',
(e) => {
e.preventDefault();
const selectedJSON = selectedInfo?.name === "string"
? selectedInfo?.value
: JSON.stringify(selectedInfo?.value, null, 2);
navigator.clipboard.writeText(selectedJSON);
},
[selectedInfo]
);
return <></>;
}
+57 -43
View File
@@ -16,52 +16,66 @@ export function DocumentTitle() {
}
}, [updateDoc]);
const startEditing = useCallback(() => {
ref.current?.select();
ref.current?.focus();
}, [ref.current]);
return (
<updateDoc.Form method="post" action={`/actions/${doc.id}/update`}>
if (doc.readOnly) {
return (
<div
className="flex justify-center items-center w-full"
title={doc.title}
>
<label className="relative block group">
<PencilAltIcon className="h-5 w-5 absolute top-1/2 transform -translate-y-1/2 left-3 text-white opacity-0 transition pointer-events-none group-hover:opacity-80 group-focus:opacity-80" />
<input
ref={ref}
className={
"min-w-[15vw] border-none text-ellipsis text-slate-300 px-2 pl-10 py-1 rounded-sm bg-transparent placeholder:text-slate-400 focus:bg-black/30 focus:outline-none focus:border-none hover:bg-black hover:bg-opacity-30 hover:cursor-text transition dark:bg-transparent dark:text-slate-200 dark:placeholder:text-slate-400 dark:focus:bg-black dark:focus:bg-opacity-10 dark:hover:bg-black dark:hover:bg-opacity-10"
}
type="text"
name="title"
spellCheck="false"
placeholder="Name your JSON file"
value={editedTitle}
onChange={(e) => setEditedTitle(e.target.value)}
/>
</label>
{match(editedTitle)
.with(doc.title, () => <p className="ml-2 text-transparent">Save</p>)
.with("", () => (
<button
className="ml-2 text-lime-500 hover:text-lime-600 transition"
onClick={() => setEditedTitle(doc.title)}
>
Reset
</button>
))
.otherwise(() => (
<button
type="submit"
className="ml-2 text-lime-500 hover:text-lime-600 transition"
>
Save
</button>
))}
<span
className={
"min-w-[15vw] border-none text-ellipsis text-slate-300 px-2 pl-10 py-1 rounded-sm bg-transparent placeholder:text-slate-400 focus:bg-black/30 focus:outline-none focus:border-none hover:cursor-text transition dark:bg-transparent dark:text-slate-200 dark:placeholder:text-slate-400 dark:focus:bg-black dark:focus:bg-opacity-10"
}
>
{doc.title}
</span>
</div>
</updateDoc.Form>
);
);
} else {
return (
<updateDoc.Form method="post" action={`/actions/${doc.id}/update`}>
<div
className="flex justify-center items-center w-full"
title={doc.title}
>
<label className="relative block group">
<PencilAltIcon className="h-5 w-5 absolute top-1/2 transform -translate-y-1/2 left-3 text-white opacity-0 transition pointer-events-none group-hover:opacity-80 group-focus:opacity-80" />
<input
ref={ref}
className={
"min-w-[15vw] border-none text-ellipsis text-slate-300 px-2 pl-10 py-1 rounded-sm bg-transparent placeholder:text-slate-400 focus:bg-black/30 focus:outline-none focus:border-none hover:bg-black hover:bg-opacity-30 hover:cursor-text transition dark:bg-transparent dark:text-slate-200 dark:placeholder:text-slate-400 dark:focus:bg-black dark:focus:bg-opacity-10 dark:hover:bg-black dark:hover:bg-opacity-10"
}
type="text"
name="title"
spellCheck="false"
placeholder="Name your JSON file"
value={editedTitle}
onChange={(e) => setEditedTitle(e.target.value)}
/>
</label>
{match(editedTitle)
.with(doc.title, () => (
<p className="ml-2 text-transparent">Save</p>
))
.with("", () => (
<button
className="ml-2 text-lime-500 hover:text-lime-600 transition"
onClick={() => setEditedTitle(doc.title)}
>
Reset
</button>
))
.otherwise(() => (
<button
type="submit"
className="ml-2 text-lime-500 hover:text-lime-600 transition"
>
Save
</button>
))}
</div>
</updateDoc.Form>
);
}
}
+20
View File
@@ -0,0 +1,20 @@
import { Link } from "remix";
export function ExampleDoc({
id,
title,
path,
}: {
id: string;
title: string;
path?: string;
}) {
return (
<Link
to={`/j/${id}${path ? `?path=${path}` : ""}`}
className="bg-slate-900 px-4 py-2 rounded-md whitespace-nowrap text-lime-300 transition hover:text-lime-500"
>
{title}
</Link>
);
}
+8 -1
View File
@@ -1,4 +1,5 @@
import { ArrowKeysIcon } from "./Icons/ArrowKeysIcon";
import { CopyShortcutIcon } from "./Icons/CopyShortcutIcon";
import { EscapeKeyIcon } from "./Icons/EscapeKeyIcon";
import { SquareBracketsIcon } from "./Icons/SquareBracketsIcon";
import { Body } from "./Primitives/Body";
@@ -6,7 +7,7 @@ import { ThemeModeToggler } from "./ThemeModeToggle";
export function Footer() {
return (
<footer className="flex items-center justify-between w-screen h-[30px] bg-slate-200 dark:bg-slate-800 border-t-[1px] border-slate-400 transition dark:border-slate-600">
<footer className="flex items-center justify-between w-screen min-h-[30px] bg-slate-200 dark:bg-slate-800 border-t-[1px] border-slate-400 transition dark:border-slate-600">
<ol className="flex pl-3">
<li className="flex items-center">
<ArrowKeysIcon className="transition text-slate-300 dark:text-slate-500" />
@@ -26,6 +27,12 @@ export function Footer() {
Reset path
</Body>
</li>
<li className="flex items-center">
<CopyShortcutIcon className="transition text-slate-300 dark:text-slate-500" />
<Body className="pl-2 pr-4 text-slate-800 transition dark:text-white">
Copy selected node
</Body>
</li>
</ol>
<ThemeModeToggler />
</footer>
+6
View File
@@ -2,6 +2,7 @@ import { DiscordIcon } from "../Icons/DiscordIcon";
import { EmailIcon } from "../Icons/EmailIcon";
import { GithubIcon } from "../Icons/GithubIcon";
import { Logo } from "../Icons/Logo";
import { TwitterIcon } from "../Icons/TwitterIcon";
export type HomeFooterProps = {
maxWidth?: string;
@@ -36,6 +37,11 @@ export function HomeFooter({ maxWidth = "1150px" }: HomeFooterProps) {
<DiscordIcon />
</a>
</li>
<li className="ml-2 hover:cursor-pointer">
<a href="https://twitter.com/json_hero" target="_blank">
<TwitterIcon />
</a>
</li>
</ol>
</div>
</footer>
+7 -1
View File
@@ -1,5 +1,6 @@
import { DiscordIconTransparent } from "../Icons/DiscordIconTransparent";
import { EmailIconTransparent } from "../Icons/EmailIconTransparent";
import { TwitterIcon } from "../Icons/TwitterIcon";
import { Logo } from "../Icons/Logo";
import { NewDocument } from "../NewDocument";
import { GithubStar } from "../UI/GithubStar";
@@ -13,7 +14,7 @@ import {
export function HomeHeader() {
return (
<header className="fixed z-20 flex items-center justify-between w-screen h-[82px] px-4 bg-indigo-700">
<div className="flex w-44 mr-3">
<div className="flex w-36 sm:w-44 mr-3">
<Logo />
</div>
<ol className="flex items-center gap-2">
@@ -45,6 +46,11 @@ export function HomeHeader() {
<DiscordIconTransparent />
</a>
</li>
<li className="hover:cursor-pointer opacity-90 hover:opacity-100 transition">
<a href="https://twitter.com/json_hero" target="_blank">
<TwitterIcon />
</a>
</li>
</ol>
</header>
);
+7 -1
View File
@@ -142,7 +142,13 @@ function SampleJSONPreview({
}) {
return (
<JsonDocProvider
doc={{ id: "sample", title: "Sample", type: "raw", contents: "" }}
doc={{
id: "sample",
title: "Sample",
type: "raw",
readOnly: false,
contents: "",
}}
path={initialSelection}
>
<JsonProvider initialJson={json}>
+23
View File
@@ -0,0 +1,23 @@
export function CopyShortcutIcon(props: React.SVGProps<SVGSVGElement>) {
return (
<svg
className={props.className}
width="30"
height="14"
viewBox="0 0 30 14"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<rect width="14" height="14" rx="1.53846" fill="currentColor" />
<rect x="16" width="14" height="14" rx="1.53846" fill="currentColor" />
<path
d="M5.64,10.22H8.25V7a.39.39,0,0,1,.38-.39H10l-3-3L4,6.65H5.26A.39.39,0,0,1,5.64,7v3.18Zm3,.78H5.26a.38.38,0,0,1-.39-.39V7.43H3.11a.39.39,0,0,1-.28-.66L6.72,2.86a.39.39,0,0,1,.55,0l3.88,3.91a.38.38,0,0,1-.27.66H9v3.18a.38.38,0,0,1-.39.39Z"
stroke="#0f172a" strokeWidth="0.35px" fill="#0f172a"
/>
<path
d="M23.81,9.52a1.66,1.66,0,0,0,.53-.27,1.57,1.57,0,0,0,.35-.42,1.07,1.07,0,0,0,.13-.53h1.6a2.26,2.26,0,0,1-.25,1,2.87,2.87,0,0,1-.7.85,3.35,3.35,0,0,1-1,.58,3.56,3.56,0,0,1-1.22.21,3.73,3.73,0,0,1-1.55-.31,3.2,3.2,0,0,1-1.11-.84,3.61,3.61,0,0,1-.67-1.22,4.91,4.91,0,0,1-.23-1.5V6.88a4.91,4.91,0,0,1,.23-1.5,3.54,3.54,0,0,1,.67-1.22,3.33,3.33,0,0,1,1.11-.84,3.94,3.94,0,0,1,2.83-.09,3,3,0,0,1,1,.59,2.66,2.66,0,0,1,.67.92,2.94,2.94,0,0,1,.23,1.17h-1.6a1.48,1.48,0,0,0-.45-1.07,1.65,1.65,0,0,0-.52-.33,1.75,1.75,0,0,0-.65-.12,1.59,1.59,0,0,0-1.44.78,2.27,2.27,0,0,0-.31.8,4.53,4.53,0,0,0-.09.91v.24a4.63,4.63,0,0,0,.09.92,2.46,2.46,0,0,0,.3.8,1.67,1.67,0,0,0,.56.56,1.63,1.63,0,0,0,.89.22A2.05,2.05,0,0,0,23.81,9.52Z"
fill="#0f172a"
/>
</svg>
);
}
+1 -1
View File
@@ -24,7 +24,7 @@ export function InfoPanel() {
return (
<>
<div className="h-inspectorHeight p-4 bg-white border-l-[1px] border-slate-300 overflow-y-auto no-scrollbar transition dark:bg-slate-800 dark:border-slate-600">
<div className="h-full p-4 bg-white border-l-[1px] border-slate-300 overflow-y-auto no-scrollbar transition dark:bg-slate-800 dark:border-slate-600">
<InfoHeader relatedPaths={relatedPaths} />
<div className="mb-4">
+7 -4
View File
@@ -4,6 +4,7 @@ import {
} from "../hooks/useJsonColumnView";
import { useHotkeys } from "react-hotkeys-hook";
import { Columns } from "./Columns";
import { CopySelectedNodeShortcut } from "./CopySelectedNode";
export function JsonColumnView() {
const { getColumnViewProps, columns } = useJsonColumnViewState();
@@ -11,9 +12,7 @@ export function JsonColumnView() {
return (
<>
<KeyboardShortcuts />
<div {...getColumnViewProps()}>
<Columns columns={columns} />
</div>
<Columns columns={columns} props={getColumnViewProps()} />
</>
);
}
@@ -67,5 +66,9 @@ function KeyboardShortcuts() {
[api]
);
return <></>;
return (
<>
<CopySelectedNodeShortcut />
</>
);
}
+1 -1
View File
@@ -81,7 +81,7 @@ export function JsonEditor() {
<CodeEditor
language="json"
content={jsonMapped.json}
readOnly={false}
readOnly={true}
onUpdate={onUpdate}
selection={selection}
/>
+26 -22
View File
@@ -6,6 +6,7 @@ import {
} from "~/hooks/useJsonColumnView";
import { JsonTreeViewNode, useJsonTreeViewContext } from "~/hooks/useJsonTree";
import { VirtualNode } from "~/hooks/useVirtualTree";
import { CopySelectedNodeShortcut } from "./CopySelectedNode";
import { Body } from "./Primitives/Body";
import { Mono } from "./Primitives/Mono";
@@ -78,31 +79,34 @@ export function JsonTreeView() {
}, [treeRef.current]);
return (
<div
className="text-white w-full"
ref={parentRef}
style={{
height: `calc(100vh - 106px)`,
overflowY: "auto",
overflowX: "hidden",
}}
>
<>
<CopySelectedNodeShortcut />
<div
className="relative w-full outline-none"
style={{ height: `${tree.totalSize}px` }}
{...tree.getTreeProps()}
ref={treeRef}
className="text-white w-full"
ref={parentRef}
style={{
height: `calc(100vh - 106px)`,
overflowY: "auto",
overflowX: "hidden",
}}
>
{tree.nodes.map((virtualNode) => (
<TreeViewNode
virtualNode={virtualNode}
key={virtualNode.node.id}
onToggle={(node, e) => tree.toggleNode(node.id, e)}
selectedNodeId={selectedNodeId}
/>
))}
<div
className="relative w-full outline-none"
style={{ height: `${tree.totalSize}px` }}
{...tree.getTreeProps()}
ref={treeRef}
>
{tree.nodes.map((virtualNode) => (
<TreeViewNode
virtualNode={virtualNode}
key={virtualNode.node.id}
onToggle={(node, e) => tree.toggleNode(node.id, e)}
selectedNodeId={selectedNodeId}
/>
))}
</div>
</div>
</div>
</>
);
}
+1 -1
View File
@@ -4,7 +4,7 @@ import { SearchBar } from "./SearchBar";
export function JsonView({ children }: { children: React.ReactNode }) {
return (
<div className="path-bar-and-column-wrapper flex flex-col flex-grow overflow-x-hidden border-l-[1px] border-slate-300 transition dark:border-slate-600">
<div className="path-bar-and-columns flex flex-col flex-grow overflow-x-hidden border-l-[1px] border-slate-300 transition dark:border-slate-600">
<div className="flex justify-between p-1 bg-slate-200 border-slate-300 border-b-[1px] transition dark:bg-slate-900 dark:border-slate-600">
<div className="flex-shrink-0 flex-grow-0">
<PathHistoryControls />
@@ -0,0 +1,42 @@
import { useRef } from "react";
import { useHotkeys } from "react-hotkeys-hook";
import { Body } from "~/components/Primitives/Body";
import { PreviewBox } from "../PreviewBox";
export function PreviewAudioUri({
src,
contentType,
}: {
src: string;
contentType: string;
}) {
const mediaRef = useRef<HTMLMediaElement>(null);
useHotkeys(
"space",
(e) => {
e.preventDefault();
if (mediaRef.current) {
if (mediaRef.current.paused) {
mediaRef.current.play();
} else {
mediaRef.current.pause();
}
}
},
[mediaRef]
);
return (
<div>
<PreviewBox>
<Body>
<audio controls src={src} ref={mediaRef}>
Sorry, your browser doesn't support embedded audio.
</audio>
</Body>
</PreviewBox>
</div>
);
}
@@ -6,9 +6,11 @@ import {
import Color from "color";
import { CodeViewer } from "~/components/CodeViewer";
import { PreviewBox } from "../PreviewBox";
import { PreviewAudioUri } from "./PreviewAudioUri";
import { PreviewDate } from "./PreviewDate";
import { PreviewImageUri } from "./PreviewImageUri";
import { PreviewUri } from "./PreviewUri";
import { PreviewVideoUri } from "./PreviewVideoUri";
export function PreviewString({ info }: { info: JSONStringType }) {
if (info.format == null) {
@@ -30,6 +32,28 @@ export function PreviewString({ info }: { info: JSONStringType }) {
contentType={info.format.contentType}
/>
);
} else if (
info.format.contentType === "video/mp4" ||
info.format.contentType === "video/webm" ||
info.format.contentType === "video/ogg"
) {
return (
<PreviewVideoUri
src={info.value}
contentType={info.format.contentType}
/>
);
} else if (
info.format.contentType === "audio/mpeg" ||
info.format.contentType === "audio/ogg" ||
info.format.contentType === "audio/wav"
) {
return (
<PreviewAudioUri
src={info.value}
contentType={info.format.contentType}
/>
);
} else {
return <PreviewUri value={info.value} type={info} />;
}
@@ -0,0 +1,43 @@
import { useRef } from "react";
import { useHotkeys } from "react-hotkeys-hook";
import { Body } from "~/components/Primitives/Body";
import { PreviewBox } from "../PreviewBox";
export function PreviewVideoUri({
src,
contentType,
}: {
src: string;
contentType: string;
}) {
const videoRef = useRef<HTMLVideoElement>(null);
useHotkeys(
"space",
(e) => {
e.preventDefault();
if (videoRef.current) {
if (videoRef.current.paused) {
videoRef.current.play();
} else {
videoRef.current.pause();
}
}
},
[videoRef]
);
return (
<div>
<PreviewBox>
<Body>
<video key={src} controls ref={videoRef}>
<source src={src} type={contentType} />
Sorry, your browser doesn't support embedded videos.
</video>
</Body>
</PreviewBox>
</div>
);
}
+14 -17
View File
@@ -1,31 +1,28 @@
import { ExampleDoc } from "./ExampleDoc";
import { ExampleUrl } from "./ExampleUrl";
export function SampleUrls() {
return (
<div className="flex justify-start flex-wrap gap-2">
<ExampleUrl
url="https://gist.githubusercontent.com/ericallam/332aca65c34dbc0fdb1b3cffbdf3f7a4/raw/4c6e264c3afd3432b608bad628290a7d71035253/unsplash.json"
title="Unsplash API Example"
displayTitle="Unsplash API"
<ExampleDoc
id="xW6aDgcbKgwC"
title="Unsplash API"
path="4.urls.regular"
/>
<ExampleUrl
url="https://gist.githubusercontent.com/ericallam/77e37e93e370b32387ce8d598dd06fc8/raw/9306a2c039fc27ab42bdadb80cbd6dbca49d27ec/tweet.json"
title="Tweet API Example"
displayTitle="Tweet JSON"
<ExampleDoc
id="d9udW60cLOok"
title="Tweet JSON"
path="data.0.entities.urls.0.expanded_url"
/>
<ExampleUrl
url="https://gist.githubusercontent.com/ericallam/f11f4981adf72b0427427c349afb3a09/raw/37eee4234b628f3278c9d4f644fee4a1c4987593/Airtable.json"
title="Airtable Product Catalog"
displayTitle="Airtable API"
<ExampleDoc
id="JZw4Wx9Mgj6r"
title="Airtable API"
path="records.3.createdTime"
/>
<ExampleUrl
url="https://gist.githubusercontent.com/ericallam/51b77c0051c21a34ea33d601bb0b1bef/raw/0b099f7d3bb5fe47f1fd55007d6a5fe9c5fd046b/GitHub.json"
title="List of Github Repos"
displayTitle="Github API"
/>
<ExampleDoc id="PjHo1o5MVeH4" title="Github API" />
</div>
);
}
+30 -4
View File
@@ -1,8 +1,10 @@
import { customRandom } from "nanoid";
import safeFetch from "./utilities/safeFetch";
type BaseJsonDocument = {
id: string;
title: string;
readOnly: boolean;
};
export type RawJsonDocument = BaseJsonDocument & {
@@ -17,6 +19,8 @@ export type UrlJsonDocument = BaseJsonDocument & {
export type CreateJsonOptions = {
ttl?: number;
readOnly?: boolean;
injest?: boolean;
metadata?: any;
};
@@ -37,17 +41,33 @@ export async function createFromUrlOrRawJson(
export async function createFromUrl(
url: URL,
title?: string
title?: string,
options?: CreateJsonOptions
): Promise<JSONDocument> {
if (options?.injest) {
const response = await safeFetch(url.href);
if (!response.ok) {
throw new Error(`Failed to injest ${url.href}`);
}
return createFromRawJson(title || url.href, await response.text(), options);
}
const docId = createId();
const doc = {
const doc: JSONDocument = {
id: docId,
type: <const>"url",
url: url.href,
title: title ?? url.hostname,
readOnly: options?.readOnly ?? false,
};
await DOCUMENTS.put(docId, JSON.stringify(doc));
await DOCUMENTS.put(docId, JSON.stringify(doc), {
expirationTtl: options?.ttl ?? undefined,
metadata: options?.metadata ?? undefined,
});
return doc;
}
@@ -58,7 +78,13 @@ export async function createFromRawJson(
options?: CreateJsonOptions
): Promise<JSONDocument> {
const docId = createId();
const doc = { id: docId, type: <const>"raw", contents, title: filename };
const doc: JSONDocument = {
id: docId,
type: <const>"raw",
contents,
title: filename,
readOnly: options?.readOnly ?? false,
};
await DOCUMENTS.put(docId, JSON.stringify(doc), {
expirationTtl: options?.ttl ?? undefined,
+64
View File
@@ -0,0 +1,64 @@
export default function Index() {
return <Flex />;
}
function Grid() {
return (
<div className="grid overflow-hidden grid-cols-1 grid-rows-app gap-0 h-screen">
<div className="bg-yellow-300 border-yellow-500 border-dashed border">
banner
</div>
<div className="bg-green-300 border-green-500 border-dashed border">
header
</div>
<div className="bg-red-300 border-red-500 border-dashed border">
<div className="flex h-full w-24 bg-slate-500"></div>
</div>
<div className="bg-blue-300 border-blue-500 border-dashed border">
footer
</div>
</div>
);
}
function Flex() {
return (
<div className="flex overflow-hidden flex-col gap-0 h-screen">
<div className="bg-yellow-300 border-yellow-500 border-dashed border min-h-8">
banner
</div>
<div className="bg-green-300 border-green-500 border-dashed border min-h-8">
header
</div>
<div className="bg-red-300 border-red-500 overflow-y-auto border-dashed border flex-grow">
<div className="h-full w-48 overflow-y-auto no-scrollbar bg-slate-500">
<div className="">
<TestItem></TestItem>
<TestItem></TestItem>
<TestItem></TestItem>
<TestItem></TestItem>
<TestItem></TestItem>
<TestItem></TestItem>
<TestItem></TestItem>
<TestItem></TestItem>
<TestItem></TestItem>
<TestItem></TestItem>
<TestItem></TestItem>
<TestItem></TestItem>
</div>
</div>
</div>
<div className="bg-blue-300 border-blue-500 border-dashed border min-h-8">
footer
</div>
</div>
);
}
function TestItem() {
return (
<div className="flex items-center justify-center bg-orange-300 border-b-slate-900 border-b">
<p className="text-2xl text-black">Test Item</p>
</div>
);
}
+9 -1
View File
@@ -28,6 +28,7 @@ import { Body } from "~/components/Primitives/Body";
import { PageNotFoundTitle } from "~/components/Primitives/PageNotFoundTitle";
import { SmallSubtitle } from "~/components/Primitives/SmallSubtitle";
import { Logo } from "~/components/Icons/Logo";
import { Banner } from "~/components/Banner";
export const loader: LoaderFunction = async ({ params, request }) => {
invariant(params.id, "expected params.id");
@@ -48,6 +49,10 @@ export const loader: LoaderFunction = async ({ params, request }) => {
const jsonResponse = await safeFetch(doc.url);
if (!jsonResponse.ok) {
console.log(
`Failed to fetch ${doc.url}: ${jsonResponse.status} (${jsonResponse.statusText})`
);
throw new Response(jsonResponse.statusText, {
status: jsonResponse.status,
});
@@ -55,6 +60,8 @@ export const loader: LoaderFunction = async ({ params, request }) => {
const json = await jsonResponse.json();
console.log(`Fetched ${doc.url} with json, returning...`);
return {
doc,
json,
@@ -125,7 +132,7 @@ export default function JsonDocumentRoute() {
<JsonSearchProvider>
<JsonTreeViewProvider overscan={25}>
<div>
<div className="block sm:hidden fixed bg-black/80 h-screen w-screen z-50 text-white">
<div className="block md:hidden fixed bg-black/80 h-screen w-screen z-50 text-white">
<div className="flex flex-col items-center justify-center h-full text-center">
<LargeTitle>JSON Hero only works on desktop</LargeTitle>
<LargeTitle>👇</LargeTitle>
@@ -139,6 +146,7 @@ export default function JsonDocumentRoute() {
</div>
</div>
<div className="h-screen flex flex-col sm:overflow-hidden">
<Banner />
<Header />
<div className="bg-slate-50 flex-grow transition dark:bg-slate-900">
<div className="main-container flex justify-items-stretch h-full">
+34 -4
View File
@@ -1,23 +1,49 @@
import { LoaderFunction, redirect } from "remix";
import { json, LoaderFunction, redirect } from "remix";
import invariant from "tiny-invariant";
import { sendEvent } from "~/graphJSON.server";
import { createFromRawJson, createFromUrl } from "~/jsonDoc.server";
import {
createFromRawJson,
createFromUrl,
CreateJsonOptions,
} from "~/jsonDoc.server";
export let loader: LoaderFunction = async ({ request, context }) => {
const url = new URL(request.url);
const jsonUrl = url.searchParams.get("url");
const base64EncodedJson = url.searchParams.get("j");
const ttl = url.searchParams.get("ttl");
const readOnly = url.searchParams.get("readonly");
const title = url.searchParams.get("title");
const injest = url.searchParams.get("injest");
if (!jsonUrl && !base64EncodedJson) {
return redirect("/");
}
const options: CreateJsonOptions = {};
if (typeof ttl === "string") {
invariant(ttl.match(/^\d+$/), "ttl must be a number");
options.ttl = parseInt(ttl, 10);
invariant(options.ttl >= 60, "ttl must be at least 60 seconds");
}
if (typeof readOnly === "string") {
options.readOnly = readOnly === "true";
}
if (typeof injest === "string") {
options.injest = injest === "true";
}
if (jsonUrl) {
const jsonURL = new URL(jsonUrl);
invariant(jsonURL, "url must be a valid URL");
const doc = await createFromUrl(jsonURL, jsonURL.href);
const doc = await createFromUrl(jsonURL, title ?? jsonURL.href, options);
context.waitUntil(
sendEvent({
@@ -33,7 +59,11 @@ export let loader: LoaderFunction = async ({ request, context }) => {
}
if (base64EncodedJson) {
const doc = await createFromRawJson("Untitled", atob(base64EncodedJson));
const doc = await createFromRawJson(
title ?? "Untitled",
atob(base64EncodedJson),
options
);
context.waitUntil(
sendEvent({
+8 -22
View File
@@ -1,17 +1,6 @@
// Should truncate the number to not show the exact number of stars
// If over 1000, show 1k
// If between 1100 and 1199, show 1.1k
// If between 1200 and 1299, show 1.2k
// If between 1300 and 1399, show 1.3k
// If over 10000, show 10k
// if between 10100 and 10199, show 10.1k
// if between 10200 and 10299, show 10.2k
// if between 10300 and 10399, show 10.3k
// if between 11000 and 11099, show 11k
// if between 11100 and 11199, show 11.1k
// if over 100000, show 100k
// if between 100100 and 100199, show 100.1k
// if between 100200 and 100299, show 100.2k
// Round up to the nearest hundred, so for example, 1150 becomes 1.2k, 1101 becomes 1.1k, etc.
export function formatStarCount(count: number | undefined): string {
if (count === undefined) {
return "⭐️";
@@ -20,14 +9,11 @@ export function formatStarCount(count: number | undefined): string {
if (count < 1000) {
return count.toString();
}
if (count < 10000) {
return `${Math.floor(count / 100) / 10}k`;
}
if (count < 100000) {
return `${Math.floor(count / 100) / 10}k`;
}
if (count < 1000000) {
return `${Math.floor(count / 100) / 10}k`;
}
return `${Math.floor(count / 100000)}k`;
return `${roundWithPrecision(count / 1000, 1)}k`;
}
function roundWithPrecision(value: number, precision: number): number {
const multiplier = Math.pow(10, precision);
return Math.round(value * multiplier) / multiplier;
}
+402
View File
@@ -0,0 +1,402 @@
[
{
"movie": "Cars 3",
"year": 2017,
"release_date": "2017-05-23",
"director": "Brian Fee",
"character": "Lighting McQueen",
"movie_duration": "01:42:25",
"timestamp": "00:25:10",
"full_line": "Oh, hey, Mr. Sterling. Wow.",
"current_wow_in_movie": 3,
"total_wows_in_movie": 10,
"poster": "https://images.ctfassets.net/bs8ntwkklfua/43fOBsgY8iOJL0dijvFnfl/ea361efc5131a859c173ab5dd3fdfe1e/Cars_3_Poster.jpg",
"video": {
"1080p": "https://videos.ctfassets.net/bs8ntwkklfua/MwAHRe21gYt7qgtQqQSgi/20188401d4ae548db5c4f861fad96f49/Cars_3_Wow_3_1080p.mp4",
"720p": "https://videos.ctfassets.net/bs8ntwkklfua/7dL6LdSOmZ2mbXgdi4KeZl/6fe6efef423d28e5704637658a3650ec/Cars_3_Wow_3_720p.mp4",
"480p": "https://videos.ctfassets.net/bs8ntwkklfua/7uciNXhLeVXBByZu8ipAr4/5bcae43dde19fa0bb249e9318953ba09/Cars_3_Wow_3_480p.mp4",
"360p": "https://videos.ctfassets.net/bs8ntwkklfua/KL91bYDqfWVeK6NdaXh1O/f1baca7b2988a2e5853256be5f9e2c89/Cars_3_Wow_3_360p.mp4"
},
"audio": "https://assets.ctfassets.net/bs8ntwkklfua/6PlYbs7cO3iXEEM1szcfHZ/bf10fba861c8e0d2c20ac4714974bde1/Cars_3_Wow_3.mp3"
},
{
"movie": "The Internship",
"year": 2013,
"release_date": "2013-05-29",
"director": "Shawn Levy",
"character": "Nick Campbell",
"movie_duration": "02:05:02",
"timestamp": "00:38:59",
"full_line": "Wow! Seven projects.",
"current_wow_in_movie": 1,
"total_wows_in_movie": 5,
"poster": "https://images.ctfassets.net/bs8ntwkklfua/6XhmTpc1PoiTf0q1nDYMyv/594947f72b4dba7f3c24938a680dd603/The_Internship_Poster.jpg",
"video": {
"1080p": "https://videos.ctfassets.net/bs8ntwkklfua/11VKtDgrvqvU4atLRF9QV9/f1f1ae91486a001e41fb3f66821f1e62/The_Internship_Wow_1_1080p.mp4",
"720p": "https://videos.ctfassets.net/bs8ntwkklfua/6uDteZpujqfebwhVAXoFkm/768bbb0a63dcef265b0040b54e417483/The_Internship_Wow_1_720p.mp4",
"480p": "https://videos.ctfassets.net/bs8ntwkklfua/4JTqjEo1kTWtALpSdE9pVs/f9242e24e89c9bd32aa3f5212eca08e9/The_Internship_Wow_1_480p.mp4",
"360p": "https://videos.ctfassets.net/bs8ntwkklfua/5VGJ7XEkBhxEbLMuDR4AIC/e5b80d57316dbb910cd72685f72b8f2b/The_Internship_Wow_1_360p.mp4"
},
"audio": "https://assets.ctfassets.net/bs8ntwkklfua/2vwdiSubw2iEcsl1yCUk0A/3cb1ba763c12fd7711823619b4c3d904/The_Internship_Wow_1.mp3"
},
{
"movie": "Cars 3",
"year": 2017,
"release_date": "2017-05-23",
"director": "Brian Fee",
"character": "Lighting McQueen",
"movie_duration": "01:42:25",
"timestamp": "01:03:08",
"full_line": "Wow. You don't mince words around here, do you?",
"current_wow_in_movie": 10,
"total_wows_in_movie": 10,
"poster": "https://images.ctfassets.net/bs8ntwkklfua/43fOBsgY8iOJL0dijvFnfl/ea361efc5131a859c173ab5dd3fdfe1e/Cars_3_Poster.jpg",
"video": {
"1080p": "https://videos.ctfassets.net/bs8ntwkklfua/3KJZ435sr6Y5k00AnZdOJ2/2aadc7fbc687b38c3f1536a9efc16cdb/Cars_3_Wow_10_1080p.mp4",
"720p": "https://videos.ctfassets.net/bs8ntwkklfua/5vQtn6ny9J79PNG16jsJMR/1b4804366cfb9206d70472ab9b7f37e6/Cars_3_Wow_10_720p.mp4",
"480p": "https://videos.ctfassets.net/bs8ntwkklfua/6kIDoftFyBJOgvCBe5Jr5k/51be6dcd0a40aff052a4215258f6f2cb/Cars_3_Wow_10_480p.mp4",
"360p": "https://videos.ctfassets.net/bs8ntwkklfua/923BnYGWVuiPpWeM7e6wv/db39600a6bd7c6a807e95d397d129dd4/Cars_3_Wow_10_360p.mp4"
},
"audio": "https://assets.ctfassets.net/bs8ntwkklfua/3iZaUMMLnxCH1fIqcXpYZ0/78e6ee967cae46e64a6f65c6f451115a/Cars_3_Wow_10.mp3"
},
{
"movie": "Night at the Museum: Secret of the Tomb",
"year": 2014,
"release_date": "2014-12-11",
"director": "Shawn Levy",
"character": "Jedediah Smith",
"movie_duration": "01:37:48",
"timestamp": "00:39:36",
"full_line": "Wow. We're a long way from home, boy.",
"current_wow_in_movie": 1,
"total_wows_in_movie": 1,
"poster": "https://images.ctfassets.net/bs8ntwkklfua/4cwNnRAOKM1HuAtlH6dscl/52762736d4815c2390619a4009d66d93/Night_at_the_Museum_Secret_of_the_Tomb_Poster.jpg",
"video": {
"1080p": "https://videos.ctfassets.net/bs8ntwkklfua/uHF5B0MFoK8H0S40Sqld9/fa9aca4d8c52ebff4ab4f390196ab0f1/Night_at_the_Museum_Secret_of_the_Tomb_Wow_1080p.mp4",
"720p": "https://videos.ctfassets.net/bs8ntwkklfua/47qSLctyurmw22A3z7X1YG/8651f02d7e58ebb1eb312a26f732515b/Night_at_the_Museum_Secret_of_the_Tomb_Wow_720p.mp4",
"480p": "https://videos.ctfassets.net/bs8ntwkklfua/3ZLiolsGZkiTp3p8aLJb3d/e330311fcaed4716f7a603d058ee8767/Night_at_the_Museum_Secret_of_the_Tomb_Wow_480p.mp4",
"360p": "https://videos.ctfassets.net/bs8ntwkklfua/1ZwmDYe0zdKzsy1MJrANSE/e5e0f5ea1c745baf61b205b6e6574605/Night_at_the_Museum_Secret_of_the_Tomb_Wow_360p.mp4"
},
"audio": "https://assets.ctfassets.net/bs8ntwkklfua/5o3CSC67KEoIojYH0UMghP/fbf9dea48ac1914eb7f7bffca802b012/Night_at_the_Museum_Secret_of_the_Tomb_Wow.mp3"
},
{
"movie": "The Internship",
"year": 2013,
"release_date": "2013-05-29",
"director": "Shawn Levy",
"character": "Nick Campbell",
"movie_duration": "02:05:02",
"timestamp": "01:26:54",
"full_line": "You're so cute. It's beautiful. Wow.",
"current_wow_in_movie": 4,
"total_wows_in_movie": 5,
"poster": "https://images.ctfassets.net/bs8ntwkklfua/6XhmTpc1PoiTf0q1nDYMyv/594947f72b4dba7f3c24938a680dd603/The_Internship_Poster.jpg",
"video": {
"1080p": "https://videos.ctfassets.net/bs8ntwkklfua/61gtrYzY8qsD3Onc4hUNdx/e1685933255e13fce8578594dbfc30aa/The_Internship_Wow_4_1080p.mp4",
"720p": "https://videos.ctfassets.net/bs8ntwkklfua/3CQQDswj7JP1UU95KdHZ2x/af3e2709d1b8c730a177a56e0395bce3/The_Internship_Wow_4_720p.mp4",
"480p": "https://videos.ctfassets.net/bs8ntwkklfua/67gEv3fXOzKu5IsscIYAxw/bf8e18b6543eec4ac1952d6c6845cb8f/The_Internship_Wow_4_480p.mp4",
"360p": "https://videos.ctfassets.net/bs8ntwkklfua/1ZrN3pCfLhimJ2fxxf86n3/43116038f56d789a1514778081c7dcc2/The_Internship_Wow_4_360p.mp4"
},
"audio": "https://assets.ctfassets.net/bs8ntwkklfua/5Jphk5c8qFsc7ZiWNeJOKQ/80f43d3758ee8730987c6a859ffec667/The_Internship_Wow_4.mp3"
},
{
"movie": "Cars 3",
"year": 2017,
"release_date": "2017-05-23",
"director": "Brian Fee",
"character": "Lighting McQueen",
"movie_duration": "01:42:25",
"timestamp": "00:22:42",
"full_line": "Wow!",
"current_wow_in_movie": 2,
"total_wows_in_movie": 10,
"poster": "https://images.ctfassets.net/bs8ntwkklfua/43fOBsgY8iOJL0dijvFnfl/ea361efc5131a859c173ab5dd3fdfe1e/Cars_3_Poster.jpg",
"video": {
"1080p": "https://videos.ctfassets.net/bs8ntwkklfua/1xpGU65BqXMplnWjcs0HG7/19ad3a66f22c976b7a345ba7a2d931ce/Cars_3_Wow_2_1080p.mp4",
"720p": "https://videos.ctfassets.net/bs8ntwkklfua/NXF2Haueh2DGbaHOpLDuV/21d43d80d211f6f93062b4f35c6d68ba/Cars_3_Wow_2_720p.mp4",
"480p": "https://videos.ctfassets.net/bs8ntwkklfua/6mN4TgA3TUcgqUEec3qCOV/c30c18bc851f43db95b3cab17aea0fb0/Cars_3_Wow_2_480p.mp4",
"360p": "https://videos.ctfassets.net/bs8ntwkklfua/316quJhdUfAkcmKkzPjww0/1780af73d03a7a631c71429d59a064e9/Cars_3_Wow_2_360p.mp4"
},
"audio": "https://assets.ctfassets.net/bs8ntwkklfua/7rkrQVW7vsUj6amBflfTQ9/fe38afabd1eeaed70d14a8bbcf68d417/Cars_3_Wow_2.mp3"
},
{
"movie": "Hall Pass",
"year": 2011,
"release_date": "2011-02-25",
"director": "Peter Farrelly and Bobby Farrelly",
"character": "Rick Mills",
"movie_duration": "01:51:35",
"timestamp": "01:26:28",
"full_line": "Wow.",
"current_wow_in_movie": 6,
"total_wows_in_movie": 6,
"poster": "https://images.ctfassets.net/bs8ntwkklfua/6jFEUPmYiKifaTuC2cugm8/22087834d091445fc9393cdd9163a901/Hall_Pass_Poster.jpg",
"video": {
"1080p": "https://videos.ctfassets.net/bs8ntwkklfua/V9JGQ3qgfufm4vUkzyJYI/6182dce642b9f31ab698d7ab448688bd/Hall_Pass_Wow_6_1080p.mp4",
"720p": "https://videos.ctfassets.net/bs8ntwkklfua/3RkKyI4NFfakkYzUEO83hG/21d29c6b7695d1b072084a92e05b6283/Hall_Pass_Wow_6_720p.mp4",
"480p": "https://videos.ctfassets.net/bs8ntwkklfua/54Zd2b5BQrQi4hvZ8IAg8j/9a719307cdf6525257e0dba0afa9ceee/Hall_Pass_Wow_6_480p.mp4",
"360p": "https://videos.ctfassets.net/bs8ntwkklfua/7Jo3JA6zl3vzAiIkriLLOR/5f488e12cdc1c7792484f098732b790d/Hall_Pass_Wow_6_360p.mp4"
},
"audio": "https://assets.ctfassets.net/bs8ntwkklfua/7mZxDvl1IsR95NAg8mKj0t/43a0b44fd325c1de3f86cf6cec3a80d5/Hall_Pass_Wow_6.mp3"
},
{
"movie": "Cars",
"year": 2006,
"release_date": "2006-06-09",
"director": "John Lasseter",
"character": "Lightning McQueen",
"movie_duration": "01:56:36",
"timestamp": "01:23:13",
"full_line": "Wow, you were right!",
"current_wow_in_movie": 4,
"total_wows_in_movie": 5,
"poster": "https://images.ctfassets.net/bs8ntwkklfua/6dsHUil72TJLYqbwYMEjH4/387a2d9994a2f1fb069d970a0f30ba32/Cars_Poster.jpg",
"video": {
"1080p": "https://videos.ctfassets.net/bs8ntwkklfua/5WH68GXLSVdosuz0ouwFDF/0b55b456669a8bf8a13e6aecd46cae89/Cars_Wow_4_1080p.mp4",
"720p": "https://videos.ctfassets.net/bs8ntwkklfua/7KSwlI2izHJDNtiGAH32SU/7d7698ac27be72753ddc1e2291399b46/Cars_Wow_4_720p.mp4",
"480p": "https://videos.ctfassets.net/bs8ntwkklfua/6OExsgI6VJHXcjNZtlqBkx/3152bd6700486416ec0b818829de9374/Cars_Wow_4_480p.mp4",
"360p": "https://videos.ctfassets.net/bs8ntwkklfua/2ibZvAYOWwmoqQtANL3d8o/9c09f29a7f42ecd996ac991b52b2cc65/Cars_Wow_4_360p.mp4"
},
"audio": "https://assets.ctfassets.net/bs8ntwkklfua/iJ5LIuyc3pmvEAaiSVSLS/85e36dc0324b28549ea56a6882a03062/Cars_Wow_4.mp3"
},
{
"movie": "The Big Bounce",
"year": 2004,
"release_date": "2004-01-30",
"director": "George Armitage",
"character": "Jack Ryan",
"movie_duration": "01:28:10",
"timestamp": "00:58:00",
"full_line": "What is it about the produce section? Wow.",
"current_wow_in_movie": 2,
"total_wows_in_movie": 2,
"poster": "https://images.ctfassets.net/bs8ntwkklfua/4hPpMWXhuXQ8HzLusmcaS5/4c46fa4fa8bce13b06d8b4444f6150ef/The_Big_Bounce_Poster.jpg",
"video": {
"1080p": "https://videos.ctfassets.net/bs8ntwkklfua/6TPALdqhVTMztHMSw2qOOj/81d512b2ee8b7c05e7e851eae7790d0e/The_Big_Bounce_Wow_2_1080p.mp4",
"720p": "https://videos.ctfassets.net/bs8ntwkklfua/dEuiSxnBLWP26YHaWiHbz/599ac6fb7a6aead87e8e6efdab759f74/The_Big_Bounce_Wow_2_720p.mp4",
"480p": "https://videos.ctfassets.net/bs8ntwkklfua/7tA15W0lgJr7CpJNCf1m4Z/86a953df81d88f5fa79e35aa1e4e4dc1/The_Big_Bounce_Wow_2_480p.mp4",
"360p": "https://videos.ctfassets.net/bs8ntwkklfua/1iKZTtL0qQRDek4y4SduD5/bbd83798b30435b43a2df4c360130d72/The_Big_Bounce_Wow_2_360p.mp4"
},
"audio": "https://assets.ctfassets.net/bs8ntwkklfua/6pjOYj0xK38VAkPAl1QK7R/a5f083c60ce9e9c4fa93a8be70817d22/The_Big_Bounce_Wow_2.mp3"
},
{
"movie": "The Big Year",
"year": 2011,
"release_date": "2011-10-14",
"director": "David Frankel",
"character": "Kenny Bostick",
"movie_duration": "01:43:09",
"timestamp": "01:08:23",
"full_line": "Wow, and here I thought I was the bomb at 728.",
"current_wow_in_movie": 3,
"total_wows_in_movie": 3,
"poster": "https://images.ctfassets.net/bs8ntwkklfua/pCjGOhbTCQVjLRN9zTwIi/ce7cdf4b40f3549326d881697aa468a1/The_Big_Year_Poster.jpg",
"video": {
"1080p": "https://videos.ctfassets.net/bs8ntwkklfua/uOdKu6VANHZjtwZXeBU8M/bf1f0f44c7bc01faa648a1b97b1205d3/The_Big_Year_Wow_3_1080p.mp4",
"720p": "https://videos.ctfassets.net/bs8ntwkklfua/y9wR96fHZaNnZr367mjyI/1262c2af0400cb1dc8528c3c8e6a83a4/The_Big_Year_Wow_3_720p.mp4",
"480p": "https://videos.ctfassets.net/bs8ntwkklfua/55z94kbUxNFIL0rYb1i2mH/ca0505ca5e2017b663f18cca8af571b2/The_Big_Year_Wow_3_480p.mp4",
"360p": "https://videos.ctfassets.net/bs8ntwkklfua/6moRL0SzmkTGzgEuZeVByI/c63fa33d0992c0594ecbaed4fcd7e891/The_Big_Year_Wow_3_360p.mp4"
},
"audio": "https://assets.ctfassets.net/bs8ntwkklfua/1dXEYZQP1Fl6CX1roy7ZAH/f31b90a6f961dcc16c16b294f4faf664/The_Big_Year_Wow_3.mp3"
},
{
"movie": "Hall Pass",
"year": 2011,
"release_date": "2011-02-25",
"director": "Peter Farrelly and Bobby Farrelly",
"character": "Rick Mills",
"movie_duration": "01:51:35",
"timestamp": "00:46:55",
"full_line": "Wow. This is awkward. I feel like I'm at my first junior high mixer. You know? When you don't know what to say.",
"current_wow_in_movie": 3,
"total_wows_in_movie": 6,
"poster": "https://images.ctfassets.net/bs8ntwkklfua/6jFEUPmYiKifaTuC2cugm8/22087834d091445fc9393cdd9163a901/Hall_Pass_Poster.jpg",
"video": {
"1080p": "https://videos.ctfassets.net/bs8ntwkklfua/4QcL02MHJ8ApVkbfN8cP6E/264c28c1e9195d87f0206e143c5ca54a/Hall_Pass_Wow_3_1080p.mp4",
"720p": "https://videos.ctfassets.net/bs8ntwkklfua/15h0sMoIhdeaPDB8qSsUN9/36245f66352b595dc40bc4d9903fa5b3/Hall_Pass_Wow_3_720p.mp4",
"480p": "https://videos.ctfassets.net/bs8ntwkklfua/74fQiVcwuT7ePQemGC7ih4/b102922c97c9ff38f47268d648628a22/Hall_Pass_Wow_3_480p.mp4",
"360p": "https://videos.ctfassets.net/bs8ntwkklfua/7mSGl1rSVtGdfSacwnKVsu/e7ac36e5684f6b64978987d2f68c43db/Hall_Pass_Wow_3_360p.mp4"
},
"audio": "https://assets.ctfassets.net/bs8ntwkklfua/2NBIVPDF4o7cy0epTvPOwR/406cd5c17e9b01511f1e350bb96df352/Hall_Pass_Wow_3.mp3"
},
{
"movie": "No Escape",
"year": 2015,
"release_date": "2015-08-26",
"director": "John Erick Dowdle",
"character": "Jack Dwyer",
"movie_duration": "01:43:06",
"timestamp": "00:08:11",
"full_line": "Wow.",
"current_wow_in_movie": 1,
"total_wows_in_movie": 2,
"poster": "https://images.ctfassets.net/bs8ntwkklfua/R3PGhNHo0k5uhnPM7bqIp/5e2b3723c69f7c70313be19417c6fdba/No_Escape_Poster.jpg",
"video": {
"1080p": "https://videos.ctfassets.net/bs8ntwkklfua/3WxIt1hZQtpqnveyAb4xw3/54773cfd70ab4380c47048324f216fc4/No_Escape_Wow_1_1080p.mp4",
"720p": "https://videos.ctfassets.net/bs8ntwkklfua/2JcYMonzmocD3qABm1dpNg/019b752a33b2517033d75ea5bfd20b59/No_Escape_Wow_1_720p.mp4",
"480p": "https://videos.ctfassets.net/bs8ntwkklfua/5QMtRpqqyfg9irqjPPx5JO/5a24212c22dc20944b5831e5b81efe4d/No_Escape_Wow_1_480p.mp4",
"360p": "https://videos.ctfassets.net/bs8ntwkklfua/6aou6AH16gfvXSccxgcZFf/347d5ff951c851d5fe9928323e6067eb/No_Escape_Wow_1_360p.mp4"
},
"audio": "https://assets.ctfassets.net/bs8ntwkklfua/24lDtvWBG8mDqaqBM8Qsr0/4c08f87367dd3399efb6a5554f6f4858/No_Escape_Wow_1.mp3"
},
{
"movie": "Midnight in Paris",
"year": 2011,
"release_date": "2011-05-20",
"director": "Woody Allen",
"character": "Gil Pender",
"movie_duration": "01:33:57",
"timestamp": "00:58:22",
"full_line": "Wow.",
"current_wow_in_movie": 5,
"total_wows_in_movie": 10,
"poster": "https://images.ctfassets.net/bs8ntwkklfua/2ZcfSCe2dlfoVzYMr4b9nK/d566e5ad044dee56645f3bffc7200d64/Midnight_in_Paris_Poster.jpg",
"video": {
"1080p": "https://videos.ctfassets.net/bs8ntwkklfua/1DSaYjQ8SnL1Imeuxe0eXE/100d3106c8a0bf5e80372e2187daf325/Midnight_in_Paris_Wow_5_1080p.mp4",
"720p": "https://videos.ctfassets.net/bs8ntwkklfua/eFI1n7voe4CsxkWEWv2q0/36781ed8f50e508e8e1f79c8e65e601a/Midnight_in_Paris_Wow_5_720p.mp4",
"480p": "https://videos.ctfassets.net/bs8ntwkklfua/6oSaIzfy7k3DJMvvy3j1kN/d77192d7decb3ba9d06a5210a58df1fa/Midnight_in_Paris_Wow_5_480p.mp4",
"360p": "https://videos.ctfassets.net/bs8ntwkklfua/4LQ4lkhx41XDC6UcMlmjbA/dfd5e3604c85c9fb9d38ea2dfdd02a2f/Midnight_in_Paris_Wow_5_360p.mp4"
},
"audio": "https://assets.ctfassets.net/bs8ntwkklfua/2A5G34x8JCQBuYwU4D2l9S/27cc467b3ff5796ff1bbe113a06a6e64/Midnight_in_Paris_Wow_5.mp3"
},
{
"movie": "The Haunting",
"year": 1999,
"release_date": "1999-07-23",
"director": "Jan de Bont",
"character": "Luke Sanderson",
"movie_duration": "01:52:37",
"timestamp": "00:41:34",
"full_line": "Wow, I sorta got screwed on the ol' bedroom selection.",
"current_wow_in_movie": 4,
"total_wows_in_movie": 5,
"poster": "https://images.ctfassets.net/bs8ntwkklfua/6Zu7ux0JYrUWk0UC8vxPtj/d0da8bfbad670655779a9921ac514759/The_Haunting_Poster.jpg",
"video": {
"1080p": "https://videos.ctfassets.net/bs8ntwkklfua/01C128tlGJ79vtlFEPc7V9/184759a9ca6fb1ab57114977afccb2ff/The_Haunting_Wow_4_1080p.mp4",
"720p": "https://videos.ctfassets.net/bs8ntwkklfua/3fR8AigFIp2I1vkSLRhzur/afb0c226daf7806bc9bd9c5e7b008fb4/The_Haunting_Wow_4_720p.mp4",
"480p": "https://videos.ctfassets.net/bs8ntwkklfua/1OCimrvhXGGfffHqBKaqz7/52d16b12a2d1a8a264b09364afc4c87c/The_Haunting_Wow_4_480p.mp4",
"360p": "https://videos.ctfassets.net/bs8ntwkklfua/6bwt57tdhBCbhsXZ4i0pce/7f6debc7e52f80c78a618f19c8087985/The_Haunting_Wow_4_360p.mp4"
},
"audio": "https://assets.ctfassets.net/bs8ntwkklfua/3yvd3DvmY0XUyqWPLUYLqp/1aabc0f7a6b58003ccdaa61469b8d5ee/The_Haunting_Wow_4.mp3"
},
{
"movie": "Cars 3",
"year": 2017,
"release_date": "2017-05-23",
"director": "Brian Fee",
"character": "Lighting McQueen",
"movie_duration": "01:42:25",
"timestamp": "00:26:23",
"full_line": "Wow.",
"current_wow_in_movie": 5,
"total_wows_in_movie": 10,
"poster": "https://images.ctfassets.net/bs8ntwkklfua/43fOBsgY8iOJL0dijvFnfl/ea361efc5131a859c173ab5dd3fdfe1e/Cars_3_Poster.jpg",
"video": {
"1080p": "https://videos.ctfassets.net/bs8ntwkklfua/4Vp8pVwekVrXfmHT3xYCBm/5775501444824d3ee90acda0698e3472/Cars_3_Wow_5_1080p.mp4",
"720p": "https://videos.ctfassets.net/bs8ntwkklfua/6IoA0jwJiPwyXcSbDCrbJ5/5e65c8caa57c62ce9fc862aaeb327255/Cars_3_Wow_5_720p.mp4",
"480p": "https://videos.ctfassets.net/bs8ntwkklfua/JrOrzMzr2aHCVPkRJUCae/06c5cb7543e95211a6f99ed89e6ea498/Cars_3_Wow_5_480p.mp4",
"360p": "https://videos.ctfassets.net/bs8ntwkklfua/6uufhJa3yX9e0Ib0xsOb46/16f9e55246ad32a33a34d58e621b8942/Cars_3_Wow_5_360p.mp4"
},
"audio": "https://assets.ctfassets.net/bs8ntwkklfua/4aUtZzrqLHol2Hhb52zgHQ/ed5c4ef03ebba9ae34659e9058e58461/Cars_3_Wow_5.mp3"
},
{
"movie": "Cars 3",
"year": 2017,
"release_date": "2017-05-23",
"director": "Brian Fee",
"character": "Lighting McQueen",
"movie_duration": "01:42:25",
"timestamp": "00:26:11",
"full_line": "Wow.",
"current_wow_in_movie": 4,
"total_wows_in_movie": 10,
"poster": "https://images.ctfassets.net/bs8ntwkklfua/43fOBsgY8iOJL0dijvFnfl/ea361efc5131a859c173ab5dd3fdfe1e/Cars_3_Poster.jpg",
"video": {
"1080p": "https://videos.ctfassets.net/bs8ntwkklfua/7G6jLS5xfYBOepUxkW7R3c/c78d22f63ba51f371a1550834888b9ca/Cars_3_Wow_4_1080p.mp4",
"720p": "https://videos.ctfassets.net/bs8ntwkklfua/lIhPcmNS5L4DnpY41EpUA/96b0c5b44acf3fe16e68d1e67156d41c/Cars_3_Wow_4_720p.mp4",
"480p": "https://videos.ctfassets.net/bs8ntwkklfua/1h3am3BfJYUm0VXV4uKF9P/e75775ec0621889efd40ce1bc372735a/Cars_3_Wow_4_480p.mp4",
"360p": "https://videos.ctfassets.net/bs8ntwkklfua/6eO8c3ojQA4rWMlE1hMMOa/c3a9ce4c01b2d342b33144c9f90b9e3e/Cars_3_Wow_4_360p.mp4"
},
"audio": "https://assets.ctfassets.net/bs8ntwkklfua/6aJuGg3bTjqd20j59vP1Wv/6cb60311194d3a9aa040882911a5f516/Cars_3_Wow_4.mp3"
},
{
"movie": "Midnight in Paris",
"year": 2011,
"release_date": "2011-05-20",
"director": "Woody Allen",
"character": "Gil Pender",
"movie_duration": "01:33:57",
"timestamp": "01:19:23",
"full_line": "Wow. Yes. Yes. Yes.",
"current_wow_in_movie": 7,
"total_wows_in_movie": 10,
"poster": "https://images.ctfassets.net/bs8ntwkklfua/2ZcfSCe2dlfoVzYMr4b9nK/d566e5ad044dee56645f3bffc7200d64/Midnight_in_Paris_Poster.jpg",
"video": {
"1080p": "https://videos.ctfassets.net/bs8ntwkklfua/7N21xMDnGKfrmBO42xvoc9/c8e05e53e5841c6ba8a659aaf4f6736a/Midnight_in_Paris_Wow_7_1080p.mp4",
"720p": "https://videos.ctfassets.net/bs8ntwkklfua/6CF2q8U9OYgt3yFA7JUBkr/d6ddbf8d57b1cf9d38127735a6266431/Midnight_in_Paris_Wow_7_720p.mp4",
"480p": "https://videos.ctfassets.net/bs8ntwkklfua/4IoMNf4pF7L3wDZ72Ad31d/efd2e6859fecf3a82a7ecdaf43175826/Midnight_in_Paris_Wow_7_480p.mp4",
"360p": "https://videos.ctfassets.net/bs8ntwkklfua/5yF8k9rwFj99H69WASEZWU/f9741ed0771da2afd9a26454863137bb/Midnight_in_Paris_Wow_7_360p.mp4"
},
"audio": "https://assets.ctfassets.net/bs8ntwkklfua/UmpPa6skALBubhqeKQFpn/2a64c51d16877b8c0bf8c23a74749f7e/Midnight_in_Paris_Wow_7.mp3"
},
{
"movie": "Midnight in Paris",
"year": 2011,
"release_date": "2011-05-20",
"director": "Woody Allen",
"character": "Gil Pender",
"movie_duration": "01:33:57",
"timestamp": "00:48:45",
"full_line": "Wow.",
"current_wow_in_movie": 3,
"total_wows_in_movie": 10,
"poster": "https://images.ctfassets.net/bs8ntwkklfua/2ZcfSCe2dlfoVzYMr4b9nK/d566e5ad044dee56645f3bffc7200d64/Midnight_in_Paris_Poster.jpg",
"video": {
"1080p": "https://videos.ctfassets.net/bs8ntwkklfua/7BBJiej0qdBbuB3Xvimrae/932bc77c9e9a9918949709f846035541/Midnight_in_Paris_Wow_3_1080p.mp4",
"720p": "https://videos.ctfassets.net/bs8ntwkklfua/5tBiVUM6ef2mARH3OXzP92/8ad999e82b7bff5ac4a07d1b4dcb45f3/Midnight_in_Paris_Wow_3_720p.mp4",
"480p": "https://videos.ctfassets.net/bs8ntwkklfua/39zl8hKK4QP1kASldccYyN/7fc8a7c86872abb9fd527a350365beea/Midnight_in_Paris_Wow_3_480p.mp4",
"360p": "https://videos.ctfassets.net/bs8ntwkklfua/6rl512MqIH6r04L4O9jHU2/2d843c19937d770860f17719960d8352/Midnight_in_Paris_Wow_3_360p.mp4"
},
"audio": "https://assets.ctfassets.net/bs8ntwkklfua/7BDkqWADSOxxYOSQGqNRcr/6fc4696de0d8bcafa487ddea97bb6d3b/Midnight_in_Paris_Wow_3.mp3"
},
{
"movie": "The Internship",
"year": 2013,
"release_date": "2013-05-29",
"director": "Shawn Levy",
"character": "Nick Campbell",
"movie_duration": "02:05:02",
"timestamp": "01:27:38",
"full_line": "Wow, a little heart and everything.",
"current_wow_in_movie": 5,
"total_wows_in_movie": 5,
"poster": "https://images.ctfassets.net/bs8ntwkklfua/6XhmTpc1PoiTf0q1nDYMyv/594947f72b4dba7f3c24938a680dd603/The_Internship_Poster.jpg",
"video": {
"1080p": "https://videos.ctfassets.net/bs8ntwkklfua/1tZ97B7BWV7WhuYJSRL0aQ/c86ca6ce4c67a0f1f5343e68a7b1f428/The_Internship_Wow_5_1080p.mp4",
"720p": "https://videos.ctfassets.net/bs8ntwkklfua/2QlKc3s0Nf10vMLaKGFhVf/a76e1c99d80b13687ae593ad95837706/The_Internship_Wow_5_720p.mp4",
"480p": "https://videos.ctfassets.net/bs8ntwkklfua/2t0rkVxOB7MVJVzUtVobAV/c396fd523e190052d4b009081b22423f/The_Internship_Wow_5_480p.mp4",
"360p": "https://videos.ctfassets.net/bs8ntwkklfua/1W9V229ebo4J74HLZGO7pF/f4379d501080fc77221ca8df020a7cd6/The_Internship_Wow_5_360p.mp4"
},
"audio": "https://assets.ctfassets.net/bs8ntwkklfua/30nYCeovpGBN5dwVmnTxrf/62c07aaab901bb488bc53ed61d2edffc/The_Internship_Wow_5.mp3"
},
{
"movie": "The Big Year",
"year": 2011,
"release_date": "2011-10-14",
"director": "David Frankel",
"character": "Kenny Bostick",
"movie_duration": "01:43:09",
"timestamp": "00:44:48",
"full_line": "Wow!",
"current_wow_in_movie": 1,
"total_wows_in_movie": 3,
"poster": "https://images.ctfassets.net/bs8ntwkklfua/pCjGOhbTCQVjLRN9zTwIi/ce7cdf4b40f3549326d881697aa468a1/The_Big_Year_Poster.jpg",
"video": {
"1080p": "https://videos.ctfassets.net/bs8ntwkklfua/2IZMW5Aeytz5rtu5MzcFk8/f675c3f6ef153c5df0c81679c80677d4/The_Big_Year_Wow_1_1080p.mp4",
"720p": "https://videos.ctfassets.net/bs8ntwkklfua/2K0XjRiHYd8zbdYcmiTMH4/b2e4a9b02db184a4d4bd71fe15ab37a9/The_Big_Year_Wow_1_720p.mp4",
"480p": "https://videos.ctfassets.net/bs8ntwkklfua/6QMkzBk6HrfuVxVT97mQg/5877904b163d7ccdc4baa92d449858d8/The_Big_Year_Wow_1_480p.mp4",
"360p": "https://videos.ctfassets.net/bs8ntwkklfua/5SFeCoTmyTUEJYtK0kOO92/462afffbd6315182db59271f99a5a829/The_Big_Year_Wow_1_360p.mp4"
},
"audio": "https://assets.ctfassets.net/bs8ntwkklfua/45tt9xH4Uf3Rgt0EJtykZZ/b5cef0c5d09d368359f4a9c0e66a6663/The_Big_Year_Wow_1.mp3"
}
]
+936
View File
@@ -0,0 +1,936 @@
{
"abilities": [
{
"ability": {
"name": "limber",
"url": "https://pokeapi.co/api/v2/ability/7/"
},
"is_hidden": false,
"slot": 1
},
{
"ability": {
"name": "imposter",
"url": "https://pokeapi.co/api/v2/ability/150/"
},
"is_hidden": true,
"slot": 3
}
],
"base_experience": 101,
"forms": [
{
"name": "ditto",
"url": "https://pokeapi.co/api/v2/pokemon-form/132/"
}
],
"game_indices": [
{
"game_index": 76,
"version": {
"name": "red",
"url": "https://pokeapi.co/api/v2/version/1/"
}
},
{
"game_index": 76,
"version": {
"name": "blue",
"url": "https://pokeapi.co/api/v2/version/2/"
}
},
{
"game_index": 76,
"version": {
"name": "yellow",
"url": "https://pokeapi.co/api/v2/version/3/"
}
},
{
"game_index": 132,
"version": {
"name": "gold",
"url": "https://pokeapi.co/api/v2/version/4/"
}
},
{
"game_index": 132,
"version": {
"name": "silver",
"url": "https://pokeapi.co/api/v2/version/5/"
}
},
{
"game_index": 132,
"version": {
"name": "crystal",
"url": "https://pokeapi.co/api/v2/version/6/"
}
},
{
"game_index": 132,
"version": {
"name": "ruby",
"url": "https://pokeapi.co/api/v2/version/7/"
}
},
{
"game_index": 132,
"version": {
"name": "sapphire",
"url": "https://pokeapi.co/api/v2/version/8/"
}
},
{
"game_index": 132,
"version": {
"name": "emerald",
"url": "https://pokeapi.co/api/v2/version/9/"
}
},
{
"game_index": 132,
"version": {
"name": "firered",
"url": "https://pokeapi.co/api/v2/version/10/"
}
},
{
"game_index": 132,
"version": {
"name": "leafgreen",
"url": "https://pokeapi.co/api/v2/version/11/"
}
},
{
"game_index": 132,
"version": {
"name": "diamond",
"url": "https://pokeapi.co/api/v2/version/12/"
}
},
{
"game_index": 132,
"version": {
"name": "pearl",
"url": "https://pokeapi.co/api/v2/version/13/"
}
},
{
"game_index": 132,
"version": {
"name": "platinum",
"url": "https://pokeapi.co/api/v2/version/14/"
}
},
{
"game_index": 132,
"version": {
"name": "heartgold",
"url": "https://pokeapi.co/api/v2/version/15/"
}
},
{
"game_index": 132,
"version": {
"name": "soulsilver",
"url": "https://pokeapi.co/api/v2/version/16/"
}
},
{
"game_index": 132,
"version": {
"name": "black",
"url": "https://pokeapi.co/api/v2/version/17/"
}
},
{
"game_index": 132,
"version": {
"name": "white",
"url": "https://pokeapi.co/api/v2/version/18/"
}
},
{
"game_index": 132,
"version": {
"name": "black-2",
"url": "https://pokeapi.co/api/v2/version/21/"
}
},
{
"game_index": 132,
"version": {
"name": "white-2",
"url": "https://pokeapi.co/api/v2/version/22/"
}
}
],
"height": 3,
"held_items": [
{
"item": {
"name": "metal-powder",
"url": "https://pokeapi.co/api/v2/item/234/"
},
"version_details": [
{
"rarity": 5,
"version": {
"name": "ruby",
"url": "https://pokeapi.co/api/v2/version/7/"
}
},
{
"rarity": 5,
"version": {
"name": "sapphire",
"url": "https://pokeapi.co/api/v2/version/8/"
}
},
{
"rarity": 5,
"version": {
"name": "emerald",
"url": "https://pokeapi.co/api/v2/version/9/"
}
},
{
"rarity": 5,
"version": {
"name": "firered",
"url": "https://pokeapi.co/api/v2/version/10/"
}
},
{
"rarity": 5,
"version": {
"name": "leafgreen",
"url": "https://pokeapi.co/api/v2/version/11/"
}
},
{
"rarity": 5,
"version": {
"name": "diamond",
"url": "https://pokeapi.co/api/v2/version/12/"
}
},
{
"rarity": 5,
"version": {
"name": "pearl",
"url": "https://pokeapi.co/api/v2/version/13/"
}
},
{
"rarity": 5,
"version": {
"name": "platinum",
"url": "https://pokeapi.co/api/v2/version/14/"
}
},
{
"rarity": 5,
"version": {
"name": "heartgold",
"url": "https://pokeapi.co/api/v2/version/15/"
}
},
{
"rarity": 5,
"version": {
"name": "soulsilver",
"url": "https://pokeapi.co/api/v2/version/16/"
}
},
{
"rarity": 5,
"version": {
"name": "black",
"url": "https://pokeapi.co/api/v2/version/17/"
}
},
{
"rarity": 5,
"version": {
"name": "white",
"url": "https://pokeapi.co/api/v2/version/18/"
}
},
{
"rarity": 5,
"version": {
"name": "black-2",
"url": "https://pokeapi.co/api/v2/version/21/"
}
},
{
"rarity": 5,
"version": {
"name": "white-2",
"url": "https://pokeapi.co/api/v2/version/22/"
}
},
{
"rarity": 5,
"version": {
"name": "x",
"url": "https://pokeapi.co/api/v2/version/23/"
}
},
{
"rarity": 5,
"version": {
"name": "y",
"url": "https://pokeapi.co/api/v2/version/24/"
}
},
{
"rarity": 5,
"version": {
"name": "omega-ruby",
"url": "https://pokeapi.co/api/v2/version/25/"
}
},
{
"rarity": 5,
"version": {
"name": "alpha-sapphire",
"url": "https://pokeapi.co/api/v2/version/26/"
}
},
{
"rarity": 5,
"version": {
"name": "sun",
"url": "https://pokeapi.co/api/v2/version/27/"
}
},
{
"rarity": 5,
"version": {
"name": "moon",
"url": "https://pokeapi.co/api/v2/version/28/"
}
},
{
"rarity": 5,
"version": {
"name": "ultra-sun",
"url": "https://pokeapi.co/api/v2/version/29/"
}
},
{
"rarity": 5,
"version": {
"name": "ultra-moon",
"url": "https://pokeapi.co/api/v2/version/30/"
}
}
]
},
{
"item": {
"name": "quick-powder",
"url": "https://pokeapi.co/api/v2/item/251/"
},
"version_details": [
{
"rarity": 50,
"version": {
"name": "diamond",
"url": "https://pokeapi.co/api/v2/version/12/"
}
},
{
"rarity": 50,
"version": {
"name": "pearl",
"url": "https://pokeapi.co/api/v2/version/13/"
}
},
{
"rarity": 50,
"version": {
"name": "platinum",
"url": "https://pokeapi.co/api/v2/version/14/"
}
},
{
"rarity": 50,
"version": {
"name": "heartgold",
"url": "https://pokeapi.co/api/v2/version/15/"
}
},
{
"rarity": 50,
"version": {
"name": "soulsilver",
"url": "https://pokeapi.co/api/v2/version/16/"
}
},
{
"rarity": 50,
"version": {
"name": "black",
"url": "https://pokeapi.co/api/v2/version/17/"
}
},
{
"rarity": 50,
"version": {
"name": "white",
"url": "https://pokeapi.co/api/v2/version/18/"
}
},
{
"rarity": 50,
"version": {
"name": "black-2",
"url": "https://pokeapi.co/api/v2/version/21/"
}
},
{
"rarity": 50,
"version": {
"name": "white-2",
"url": "https://pokeapi.co/api/v2/version/22/"
}
},
{
"rarity": 50,
"version": {
"name": "x",
"url": "https://pokeapi.co/api/v2/version/23/"
}
},
{
"rarity": 50,
"version": {
"name": "y",
"url": "https://pokeapi.co/api/v2/version/24/"
}
},
{
"rarity": 50,
"version": {
"name": "omega-ruby",
"url": "https://pokeapi.co/api/v2/version/25/"
}
},
{
"rarity": 50,
"version": {
"name": "alpha-sapphire",
"url": "https://pokeapi.co/api/v2/version/26/"
}
},
{
"rarity": 50,
"version": {
"name": "sun",
"url": "https://pokeapi.co/api/v2/version/27/"
}
},
{
"rarity": 50,
"version": {
"name": "moon",
"url": "https://pokeapi.co/api/v2/version/28/"
}
},
{
"rarity": 50,
"version": {
"name": "ultra-sun",
"url": "https://pokeapi.co/api/v2/version/29/"
}
},
{
"rarity": 50,
"version": {
"name": "ultra-moon",
"url": "https://pokeapi.co/api/v2/version/30/"
}
}
]
}
],
"id": 132,
"is_default": true,
"location_area_encounters": "https://pokeapi.co/api/v2/pokemon/132/encounters",
"moves": [
{
"move": {
"name": "transform",
"url": "https://pokeapi.co/api/v2/move/144/"
},
"version_group_details": [
{
"level_learned_at": 1,
"move_learn_method": {
"name": "level-up",
"url": "https://pokeapi.co/api/v2/move-learn-method/1/"
},
"version_group": {
"name": "red-blue",
"url": "https://pokeapi.co/api/v2/version-group/1/"
}
},
{
"level_learned_at": 1,
"move_learn_method": {
"name": "level-up",
"url": "https://pokeapi.co/api/v2/move-learn-method/1/"
},
"version_group": {
"name": "yellow",
"url": "https://pokeapi.co/api/v2/version-group/2/"
}
},
{
"level_learned_at": 1,
"move_learn_method": {
"name": "level-up",
"url": "https://pokeapi.co/api/v2/move-learn-method/1/"
},
"version_group": {
"name": "gold-silver",
"url": "https://pokeapi.co/api/v2/version-group/3/"
}
},
{
"level_learned_at": 1,
"move_learn_method": {
"name": "level-up",
"url": "https://pokeapi.co/api/v2/move-learn-method/1/"
},
"version_group": {
"name": "crystal",
"url": "https://pokeapi.co/api/v2/version-group/4/"
}
},
{
"level_learned_at": 1,
"move_learn_method": {
"name": "level-up",
"url": "https://pokeapi.co/api/v2/move-learn-method/1/"
},
"version_group": {
"name": "ruby-sapphire",
"url": "https://pokeapi.co/api/v2/version-group/5/"
}
},
{
"level_learned_at": 1,
"move_learn_method": {
"name": "level-up",
"url": "https://pokeapi.co/api/v2/move-learn-method/1/"
},
"version_group": {
"name": "emerald",
"url": "https://pokeapi.co/api/v2/version-group/6/"
}
},
{
"level_learned_at": 1,
"move_learn_method": {
"name": "level-up",
"url": "https://pokeapi.co/api/v2/move-learn-method/1/"
},
"version_group": {
"name": "firered-leafgreen",
"url": "https://pokeapi.co/api/v2/version-group/7/"
}
},
{
"level_learned_at": 1,
"move_learn_method": {
"name": "level-up",
"url": "https://pokeapi.co/api/v2/move-learn-method/1/"
},
"version_group": {
"name": "diamond-pearl",
"url": "https://pokeapi.co/api/v2/version-group/8/"
}
},
{
"level_learned_at": 1,
"move_learn_method": {
"name": "level-up",
"url": "https://pokeapi.co/api/v2/move-learn-method/1/"
},
"version_group": {
"name": "platinum",
"url": "https://pokeapi.co/api/v2/version-group/9/"
}
},
{
"level_learned_at": 1,
"move_learn_method": {
"name": "level-up",
"url": "https://pokeapi.co/api/v2/move-learn-method/1/"
},
"version_group": {
"name": "heartgold-soulsilver",
"url": "https://pokeapi.co/api/v2/version-group/10/"
}
},
{
"level_learned_at": 1,
"move_learn_method": {
"name": "level-up",
"url": "https://pokeapi.co/api/v2/move-learn-method/1/"
},
"version_group": {
"name": "black-white",
"url": "https://pokeapi.co/api/v2/version-group/11/"
}
},
{
"level_learned_at": 1,
"move_learn_method": {
"name": "level-up",
"url": "https://pokeapi.co/api/v2/move-learn-method/1/"
},
"version_group": {
"name": "colosseum",
"url": "https://pokeapi.co/api/v2/version-group/12/"
}
},
{
"level_learned_at": 1,
"move_learn_method": {
"name": "level-up",
"url": "https://pokeapi.co/api/v2/move-learn-method/1/"
},
"version_group": {
"name": "xd",
"url": "https://pokeapi.co/api/v2/version-group/13/"
}
},
{
"level_learned_at": 1,
"move_learn_method": {
"name": "level-up",
"url": "https://pokeapi.co/api/v2/move-learn-method/1/"
},
"version_group": {
"name": "black-2-white-2",
"url": "https://pokeapi.co/api/v2/version-group/14/"
}
},
{
"level_learned_at": 1,
"move_learn_method": {
"name": "level-up",
"url": "https://pokeapi.co/api/v2/move-learn-method/1/"
},
"version_group": {
"name": "x-y",
"url": "https://pokeapi.co/api/v2/version-group/15/"
}
},
{
"level_learned_at": 1,
"move_learn_method": {
"name": "level-up",
"url": "https://pokeapi.co/api/v2/move-learn-method/1/"
},
"version_group": {
"name": "omega-ruby-alpha-sapphire",
"url": "https://pokeapi.co/api/v2/version-group/16/"
}
},
{
"level_learned_at": 1,
"move_learn_method": {
"name": "level-up",
"url": "https://pokeapi.co/api/v2/move-learn-method/1/"
},
"version_group": {
"name": "sun-moon",
"url": "https://pokeapi.co/api/v2/version-group/17/"
}
},
{
"level_learned_at": 1,
"move_learn_method": {
"name": "level-up",
"url": "https://pokeapi.co/api/v2/move-learn-method/1/"
},
"version_group": {
"name": "ultra-sun-ultra-moon",
"url": "https://pokeapi.co/api/v2/version-group/18/"
}
},
{
"level_learned_at": 1,
"move_learn_method": {
"name": "level-up",
"url": "https://pokeapi.co/api/v2/move-learn-method/1/"
},
"version_group": {
"name": "lets-go-pikachu-lets-go-eevee",
"url": "https://pokeapi.co/api/v2/version-group/19/"
}
},
{
"level_learned_at": 1,
"move_learn_method": {
"name": "level-up",
"url": "https://pokeapi.co/api/v2/move-learn-method/1/"
},
"version_group": {
"name": "sword-shield",
"url": "https://pokeapi.co/api/v2/version-group/20/"
}
}
]
}
],
"name": "ditto",
"order": 214,
"past_types": [],
"species": {
"name": "ditto",
"url": "https://pokeapi.co/api/v2/pokemon-species/132/"
},
"sprites": {
"back_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/back/132.png",
"back_female": null,
"back_shiny": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/back/shiny/132.png",
"back_shiny_female": null,
"front_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/132.png",
"front_female": null,
"front_shiny": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/shiny/132.png",
"front_shiny_female": null,
"other": {
"dream_world": {
"front_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/dream-world/132.svg",
"front_female": null
},
"home": {
"front_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/home/132.png",
"front_female": null,
"front_shiny": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/home/shiny/132.png",
"front_shiny_female": null
},
"official-artwork": {
"front_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/132.png"
}
},
"versions": {
"generation-i": {
"red-blue": {
"back_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-i/red-blue/back/132.png",
"back_gray": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-i/red-blue/back/gray/132.png",
"back_transparent": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-i/red-blue/transparent/back/132.png",
"front_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-i/red-blue/132.png",
"front_gray": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-i/red-blue/gray/132.png",
"front_transparent": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-i/red-blue/transparent/132.png"
},
"yellow": {
"back_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-i/yellow/back/132.png",
"back_gray": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-i/yellow/back/gray/132.png",
"back_transparent": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-i/yellow/transparent/back/132.png",
"front_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-i/yellow/132.png",
"front_gray": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-i/yellow/gray/132.png",
"front_transparent": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-i/yellow/transparent/132.png"
}
},
"generation-ii": {
"crystal": {
"back_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-ii/crystal/back/132.png",
"back_shiny": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-ii/crystal/back/shiny/132.png",
"back_shiny_transparent": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-ii/crystal/transparent/back/shiny/132.png",
"back_transparent": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-ii/crystal/transparent/back/132.png",
"front_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-ii/crystal/132.png",
"front_shiny": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-ii/crystal/shiny/132.png",
"front_shiny_transparent": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-ii/crystal/transparent/shiny/132.png",
"front_transparent": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-ii/crystal/transparent/132.png"
},
"gold": {
"back_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-ii/gold/back/132.png",
"back_shiny": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-ii/gold/back/shiny/132.png",
"front_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-ii/gold/132.png",
"front_shiny": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-ii/gold/shiny/132.png",
"front_transparent": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-ii/gold/transparent/132.png"
},
"silver": {
"back_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-ii/silver/back/132.png",
"back_shiny": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-ii/silver/back/shiny/132.png",
"front_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-ii/silver/132.png",
"front_shiny": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-ii/silver/shiny/132.png",
"front_transparent": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-ii/silver/transparent/132.png"
}
},
"generation-iii": {
"emerald": {
"front_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-iii/emerald/132.png",
"front_shiny": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-iii/emerald/shiny/132.png"
},
"firered-leafgreen": {
"back_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-iii/firered-leafgreen/back/132.png",
"back_shiny": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-iii/firered-leafgreen/back/shiny/132.png",
"front_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-iii/firered-leafgreen/132.png",
"front_shiny": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-iii/firered-leafgreen/shiny/132.png"
},
"ruby-sapphire": {
"back_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-iii/ruby-sapphire/back/132.png",
"back_shiny": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-iii/ruby-sapphire/back/shiny/132.png",
"front_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-iii/ruby-sapphire/132.png",
"front_shiny": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-iii/ruby-sapphire/shiny/132.png"
}
},
"generation-iv": {
"diamond-pearl": {
"back_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-iv/diamond-pearl/back/132.png",
"back_female": null,
"back_shiny": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-iv/diamond-pearl/back/shiny/132.png",
"back_shiny_female": null,
"front_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-iv/diamond-pearl/132.png",
"front_female": null,
"front_shiny": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-iv/diamond-pearl/shiny/132.png",
"front_shiny_female": null
},
"heartgold-soulsilver": {
"back_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-iv/heartgold-soulsilver/back/132.png",
"back_female": null,
"back_shiny": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-iv/heartgold-soulsilver/back/shiny/132.png",
"back_shiny_female": null,
"front_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-iv/heartgold-soulsilver/132.png",
"front_female": null,
"front_shiny": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-iv/heartgold-soulsilver/shiny/132.png",
"front_shiny_female": null
},
"platinum": {
"back_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-iv/platinum/back/132.png",
"back_female": null,
"back_shiny": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-iv/platinum/back/shiny/132.png",
"back_shiny_female": null,
"front_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-iv/platinum/132.png",
"front_female": null,
"front_shiny": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-iv/platinum/shiny/132.png",
"front_shiny_female": null
}
},
"generation-v": {
"black-white": {
"animated": {
"back_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-v/black-white/animated/back/132.gif",
"back_female": null,
"back_shiny": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-v/black-white/animated/back/shiny/132.gif",
"back_shiny_female": null,
"front_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-v/black-white/animated/132.gif",
"front_female": null,
"front_shiny": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-v/black-white/animated/shiny/132.gif",
"front_shiny_female": null
},
"back_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-v/black-white/back/132.png",
"back_female": null,
"back_shiny": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-v/black-white/back/shiny/132.png",
"back_shiny_female": null,
"front_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-v/black-white/132.png",
"front_female": null,
"front_shiny": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-v/black-white/shiny/132.png",
"front_shiny_female": null
}
},
"generation-vi": {
"omegaruby-alphasapphire": {
"front_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-vi/omegaruby-alphasapphire/132.png",
"front_female": null,
"front_shiny": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-vi/omegaruby-alphasapphire/shiny/132.png",
"front_shiny_female": null
},
"x-y": {
"front_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-vi/x-y/132.png",
"front_female": null,
"front_shiny": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-vi/x-y/shiny/132.png",
"front_shiny_female": null
}
},
"generation-vii": {
"icons": {
"front_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-vii/icons/132.png",
"front_female": null
},
"ultra-sun-ultra-moon": {
"front_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-vii/ultra-sun-ultra-moon/132.png",
"front_female": null,
"front_shiny": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-vii/ultra-sun-ultra-moon/shiny/132.png",
"front_shiny_female": null
}
},
"generation-viii": {
"icons": {
"front_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-viii/icons/132.png",
"front_female": null
}
}
}
},
"stats": [
{
"base_stat": 48,
"effort": 1,
"stat": {
"name": "hp",
"url": "https://pokeapi.co/api/v2/stat/1/"
}
},
{
"base_stat": 48,
"effort": 0,
"stat": {
"name": "attack",
"url": "https://pokeapi.co/api/v2/stat/2/"
}
},
{
"base_stat": 48,
"effort": 0,
"stat": {
"name": "defense",
"url": "https://pokeapi.co/api/v2/stat/3/"
}
},
{
"base_stat": 48,
"effort": 0,
"stat": {
"name": "special-attack",
"url": "https://pokeapi.co/api/v2/stat/4/"
}
},
{
"base_stat": 48,
"effort": 0,
"stat": {
"name": "special-defense",
"url": "https://pokeapi.co/api/v2/stat/5/"
}
},
{
"base_stat": 48,
"effort": 0,
"stat": {
"name": "speed",
"url": "https://pokeapi.co/api/v2/stat/6/"
}
}
],
"types": [
{
"slot": 1,
"type": {
"name": "normal",
"url": "https://pokeapi.co/api/v2/type/1/"
}
}
],
"weight": 40
}
+22
View File
@@ -0,0 +1,22 @@
[
"There has never been a sadness that cant been cured by breakfast food.",
"Dear frozen yogurt, you are the celery of desserts. Be ice cream or be nothing. Zero stars.",
"Shorts over six inches are capri pants, shorts under six inches are European.",
"Sting like a bee. Do not float like a butterfly. Thats ridiculous.",
"Well, I am not usually one for speeches. So, goodbye.",
"I change my locks every 16 days.",
"Are you going to tell a man that he can't fart in his own car?",
"I love nothing.",
"Fishing relaxes me. It's like yoga, except I still get to kill something.",
"My son is several weeks old. He is very familiar with the sound of power tools.",
"Do you have any history of mental illness in your family? I have an uncle who does yoga.",
"If there were more food and fewer people, this would be a perfect party.",
"The three most useless jobs in the world in order are: lawyer, congressman, and doctor.",
"One rage every three months is permitted. Try not to hurt anyone who doesn't deserve it.",
"I hate everything.",
"Ive had the same haircut since 1978 and Ive driven the same car since 1991. Ive used the same wooden comb for three decades. I have one bowl. I still get my milk delivered by horse.",
"Tom put all my records into this rectangle!",
"Its pointless for a human to paint scenes of nature when they can go outside and stand in it.",
"My first ex-wifes name is Tammy. My second ex-wifes name is Tammy. My Moms name is Tamara…she goes by Tammy.",
"Live your life how you want, but dont confuse drama with happiness."
]
+6 -3
View File
@@ -20,9 +20,12 @@ module.exports = {
},
extend: {
height: {
viewerHeight: "calc(100vh - 146px)",
inspectorHeight: "calc(100vh - 70px)",
jsonViewerHeight: "calc(100vh - 106px)",
// viewerHeight: "calc(100vh - 146px)",
// inspectorHeight: "calc(100vh - 70px)",
// jsonViewerHeight: "calc(100vh - 106px)",
},
gridTemplateColumns: {
'json-column': 'auto',
},
fontFamily: {
sans: ["Source Sans Pro", "sans-serif"],
+5 -3
View File
@@ -6,16 +6,18 @@ describe("formatStarCount", () => {
expect(formatStarCount(0)).toBe("0");
expect(formatStarCount(999)).toBe("999");
expect(formatStarCount(1000)).toBe("1k");
expect(formatStarCount(1050)).toBe("1.1k");
expect(formatStarCount(1100)).toBe("1.1k");
expect(formatStarCount(1200)).toBe("1.2k");
expect(formatStarCount(1300)).toBe("1.3k");
expect(formatStarCount(10000)).toBe("10k");
expect(formatStarCount(10050)).toBe("10.1k");
expect(formatStarCount(10100)).toBe("10.1k");
expect(formatStarCount(10101)).toBe("10.1k");
expect(formatStarCount(10199)).toBe("10.1k");
expect(formatStarCount(10199)).toBe("10.2k");
expect(formatStarCount(10200)).toBe("10.2k");
expect(formatStarCount(52678)).toBe("52.6k");
expect(formatStarCount(99999)).toBe("99.9k");
expect(formatStarCount(52678)).toBe("52.7k");
expect(formatStarCount(99949)).toBe("99.9k");
expect(formatStarCount(100000)).toBe("100k");
expect(formatStarCount(100100)).toBe("100.1k");
expect(formatStarCount(101100)).toBe("101.1k");