Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Shows the "Sponsor" button on the repo page.
github: quickdrawjs
custom: ['https://tryquickdraw.com/sponsors/']
4 changes: 2 additions & 2 deletions .github/ISSUE_TEMPLATE/config.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
blank_issues_enabled: true
contact_links:
- name: Question or idea
url: https://github.com/nmndwivedi/quickdraw/discussions
url: https://github.com/quickdrawjs/quickdraw/discussions
about: Not a bug or a concrete feature request? Start a discussion.
- name: Security issue
url: https://github.com/nmndwivedi/quickdraw/security/advisories/new
url: https://github.com/quickdrawjs/quickdraw/security/advisories/new
about: Please report vulnerabilities privately, not as public issues.
55 changes: 55 additions & 0 deletions .github/workflows/sponsors.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: Sponsors

# Keeps docs/sponsors.json current so the website's sponsor wall features
# sponsors automatically — new sponsors appear within a day of sponsoring,
# lapsed ones roll off, no manual list edits.
#
# The Sponsors GraphQL API rejects anonymous reads, so this runs here with a
# token. If the default Actions token can't read the org's sponsorships, add
# a classic PAT with read:org as the SPONSORS_TOKEN secret — it's preferred
# automatically when set.

on:
schedule:
- cron: '43 4 * * *' # daily, 04:43 UTC
workflow_dispatch: # refresh on demand
push:
branches: [main]
paths:
- 'scripts/sponsors.mjs'
- '.github/workflows/sponsors.yml'

permissions:
contents: write

concurrency:
group: sponsors
cancel-in-progress: false

jobs:
update:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20

- name: Refresh sponsors snapshot
env:
GITHUB_TOKEN: ${{ secrets.SPONSORS_TOKEN || secrets.GITHUB_TOKEN }}
run: node scripts/sponsors.mjs

- name: Commit if the wall changed
run: |
# Stage first: on the very first run the file is untracked, and
# `git diff` alone would report no change and skip the seed commit.
git add -A docs/sponsors.json
if git diff --cached --quiet; then
echo "No change."
exit 0
fi
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git commit -m "chore: update sponsors [skip ci]"
git push
15 changes: 15 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,18 @@ fastest way to poke the raw engine.
- Hand-written type declarations live in each package's `types/index.d.ts`;
update them together with API changes.
- Keep comments in the existing voice: explain *why*, not *what*.

## Add your project to the showcase

Built something with Quickdraw? Add it to the
["Made with Quickdraw" table in the README](README.md#made-with-quickdraw) —
it's the standard drill:

1. Edit `README.md` and add one row to the table, keeping it alphabetical.
2. Link the project name to a live URL (or the repo if it isn't public yet),
and keep the description to 15 words or fewer, no superlatives.
3. Open a PR titled `showcase: <project name>`. Nothing else in the diff.

Any real project that ships Quickdraw qualifies — side projects included.
We only skip entries that are dead links, NSFW, or pure landing pages with no
product behind them.
29 changes: 24 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Quickdraw

[![CI](https://github.com/nmndwivedi/quickdraw/actions/workflows/ci.yml/badge.svg)](https://github.com/nmndwivedi/quickdraw/actions/workflows/ci.yml)
[![CI](https://github.com/quickdrawjs/quickdraw/actions/workflows/ci.yml/badge.svg)](https://github.com/quickdrawjs/quickdraw/actions/workflows/ci.yml)
[![npm: @quickdrawjs/core](https://img.shields.io/npm/v/@quickdrawjs/core?label=%40quickdrawjs%2Fcore&logo=npm)](https://www.npmjs.com/package/@quickdrawjs/core)
[![npm: @quickdrawjs/react](https://img.shields.io/npm/v/@quickdrawjs/react?label=%40quickdrawjs%2Freact&logo=npm)](https://www.npmjs.com/package/@quickdrawjs/react)
[![npm: @quickdrawjs/react-native](https://img.shields.io/npm/v/@quickdrawjs/react-native?label=%40quickdrawjs%2Freact-native&logo=npm)](https://www.npmjs.com/package/@quickdrawjs/react-native)
Expand Down Expand Up @@ -217,15 +217,34 @@ npm run typecheck # validate the published type declarations
Quickdraw is young. What it doesn't have yet: a first-party multiplayer
server (the diff-based store is sync-ready — you bring the transport),
layers, frames, or rich text. The full roadmap lives in
[issue #1](https://github.com/nmndwivedi/quickdraw/issues/1) — comment there
[issue #1](https://github.com/quickdrawjs/quickdraw/issues/1) — comment there
if the missing feature that sent you back to a paid SDK is on it (or isn't).

## Made with Quickdraw

Projects and products built on Quickdraw. Shipped something? **Add it here** —
edit this table and open a PR (one row, alphabetical, ≤ 15-word description;
see the [showcase guidelines](CONTRIBUTING.md#add-your-project-to-the-showcase)).

| Project | Description |
| --- | --- |
| [tryquickdraw.com/app](https://app.tryquickdraw.com) | The official free whiteboard app — no account, autosaves locally. |
| _Your project here_ | [Add yours →](CONTRIBUTING.md#add-your-project-to-the-showcase) |

## Sponsors

Quickdraw is MIT-licensed with no paid tier — [sponsors](https://github.com/sponsors/quickdrawjs)
are what keep it that way. Sponsors get their logo on
[the website](https://tryquickdraw.com/sponsors/) and here:

<a href="https://tryquickdraw.com/sponsors/"><i>No sponsors yet — your logo could be the first.</i></a>

## Star history

Quickdraw is free forever — no license fees, no upsell. If it saves you one, a
star is how other developers find it.

<a href="https://github.com/nmndwivedi/quickdraw/stargazers">
<a href="https://github.com/quickdrawjs/quickdraw/stargazers">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="docs/star-history-dark.svg">
<source media="(prefers-color-scheme: light)" srcset="docs/star-history.svg">
Expand All @@ -241,8 +260,8 @@ same data as the chart on <a href="https://tryquickdraw.com/#support">the websit

Quickdraw is open to contributions from everyone — bug reports, features,
docs, examples. Start with [CONTRIBUTING.md](CONTRIBUTING.md), browse
[`good first issue`](https://github.com/nmndwivedi/quickdraw/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22),
or open a [discussion](https://github.com/nmndwivedi/quickdraw/discussions)
[`good first issue`](https://github.com/quickdrawjs/quickdraw/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22),
or open a [discussion](https://github.com/quickdrawjs/quickdraw/discussions)
if you're not sure where a change belongs. We follow the
[Contributor Covenant](CODE_OF_CONDUCT.md).

Expand Down
2 changes: 1 addition & 1 deletion SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ security fixes.
Please **do not open a public issue** for security problems.

Report privately via
[GitHub Security Advisories](https://github.com/nmndwivedi/quickdraw/security/advisories/new)
[GitHub Security Advisories](https://github.com/quickdrawjs/quickdraw/security/advisories/new)
or email **promptifyapps@gmail.com**.

You can expect an acknowledgment within a few days. Once a fix ships we'll
Expand Down
117 changes: 72 additions & 45 deletions apps/app/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,94 +16,121 @@
<style>
html, body { margin: 0; height: 100%; overscroll-behavior: none; }
#board { position: fixed; inset: 0; }
/* The wordmark — quiet ink on the paper, top left, links home. */
/* Top-left chrome — the wordmark and the file bar follow the board's
theme, same glass recipe as the dock: light glass on light paper,
dark glass on dark. The 320ms transitions match the board's own
theme crossfade so the whole switch reads as one motion. */
:root {
--bar-bg: rgba(255, 255, 255, 0.88);
--bar-border: rgba(28, 27, 24, 0.12);
--bar-shadow: 0 4px 16px rgba(28, 27, 24, 0.14);
--bar-ink: rgba(28, 27, 24, 0.9);
--bar-hover: rgba(28, 27, 24, 0.07);
--menu-bg: rgba(255, 255, 255, 0.94);
--menu-shadow: 0 8px 28px rgba(28, 27, 24, 0.2);
--del-ink: rgba(28, 27, 24, 0.4);
--del-hot: #c0392b;
--del-hot-bg: rgba(192, 57, 43, 0.1);
--current-dot: #2f6fed;
}
:root.dark {
--bar-bg: rgba(19, 17, 13, 0.72);
--bar-border: rgba(255, 255, 255, 0.14);
--bar-shadow: 0 4px 16px rgba(10, 8, 4, 0.25);
--bar-ink: rgba(255, 255, 255, 0.9);
--bar-hover: rgba(255, 255, 255, 0.1);
--menu-bg: rgba(19, 17, 13, 0.85);
--menu-shadow: 0 8px 28px rgba(10, 8, 4, 0.35);
--del-ink: rgba(255, 255, 255, 0.45);
--del-hot: #ff7b6b;
--del-hot-bg: rgba(255, 123, 107, 0.14);
--current-dot: #6285f5;
}
.qd-app-logo {
position: fixed; left: 14px; top: 12px; z-index: 60;
position: fixed; left: 10px; top: 10px; z-index: 60;
display: flex; align-items: center; gap: 8px;
font: 700 16px/1 ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
font: 700 15.5px/1 ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
letter-spacing: -0.01em;
color: #1c1b18; text-decoration: none;
padding: 6px 10px; border-radius: 10px;
color: var(--bar-ink); text-decoration: none;
padding: 8px 14px 8px 11px; border-radius: 999px;
background: var(--bar-bg);
border: 1px solid var(--bar-border);
box-shadow: var(--bar-shadow);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
-webkit-tap-highlight-color: transparent;
transition: background 140ms ease, color 140ms ease;
transition: background 320ms ease, color 320ms ease, border-color 320ms ease, box-shadow 320ms ease;
}
.qd-app-logo svg { width: 21px; height: 21px; display: block; }
.qd-app-logo:hover { background: rgba(28, 27, 24, 0.06); }
.qd-app-logo.dark { color: #f4f2ea; }
.qd-app-logo.dark:hover { background: rgba(244, 242, 234, 0.08); }
.qd-app-logo svg { width: 20px; height: 20px; display: block; }
.qd-app-logo:hover { background: var(--bar-hover); }

/* File bar — files menu, new file, and the current file's name. */
.qd-filebar {
position: fixed; left: 148px; top: 12px; z-index: 60;
position: fixed; left: 152px; top: 10px; z-index: 60;
display: flex; align-items: center; gap: 2px;
padding: 4px 6px; border-radius: 999px;
font: 500 14px/1 ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
color: #1c1b18;
}
.qd-filebar::before {
content: ''; width: 1px; height: 18px; background: rgba(28, 27, 24, 0.16);
margin-right: 8px;
color: var(--bar-ink);
background: var(--bar-bg);
border: 1px solid var(--bar-border);
box-shadow: var(--bar-shadow);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
transition: background 320ms ease, color 320ms ease, border-color 320ms ease, box-shadow 320ms ease;
}
.qd-file-btn {
appearance: none; border: none; background: transparent; color: inherit;
display: flex; align-items: center; justify-content: center;
width: 32px; height: 32px; border-radius: 9px; cursor: pointer;
width: 30px; height: 30px; border-radius: 999px; cursor: pointer;
-webkit-tap-highlight-color: transparent;
transition: background 140ms ease;
}
.qd-file-btn svg { width: 17px; height: 17px; display: block; }
.qd-file-btn:hover { background: rgba(28, 27, 24, 0.07); }
.qd-file-btn:hover { background: var(--bar-hover); }
.qd-file-name {
appearance: none; border: none; background: transparent; color: inherit;
font: inherit; font-weight: 600; letter-spacing: -0.005em;
margin-left: 4px; padding: 7px 10px; border-radius: 9px;
margin-left: 2px; padding: 7px 10px; border-radius: 999px;
min-width: 40px; max-width: 200px; cursor: text;
transition: background 140ms ease;
}
.qd-file-name:hover { background: rgba(28, 27, 24, 0.06); }
.qd-file-name:focus { outline: none; background: rgba(28, 27, 24, 0.08); }
.qd-file-name:hover { background: var(--bar-hover); }
.qd-file-name:focus { outline: none; background: var(--bar-hover); }
.qd-files-menu {
position: fixed; left: 156px; top: 52px; z-index: 70;
position: fixed; left: 156px; top: 54px; z-index: 70;
min-width: 230px; max-height: 60vh; overflow-y: auto;
background: #fff; color: #1c1b18;
border: 1px solid rgba(28, 27, 24, 0.12); border-radius: 12px;
box-shadow: 0 14px 40px -12px rgba(28, 27, 24, 0.3);
background: var(--menu-bg); color: var(--bar-ink);
border: 1px solid var(--bar-border); border-radius: 13px;
box-shadow: var(--menu-shadow);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
padding: 6px;
transition: background 320ms ease, color 320ms ease, border-color 320ms ease, box-shadow 320ms ease;
}
.qd-files-menu[hidden] { display: none; }
.qd-files-menu .qd-file-row {
display: flex; align-items: center; gap: 8px;
padding: 8px 10px; border-radius: 8px; cursor: pointer;
font: 500 14px/1.2 inherit;
}
.qd-file-row:hover { background: rgba(28, 27, 24, 0.06); }
.qd-file-row:hover { background: var(--bar-hover); }
.qd-file-row .name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.qd-file-row.current .name { font-weight: 700; }
.qd-file-row .dot { width: 6px; height: 6px; border-radius: 999px; background: transparent; flex: none; }
.qd-file-row.current .dot { background: #2f6fed; }
.qd-file-row.current .dot { background: var(--current-dot); }
.qd-file-row .del {
appearance: none; border: none; background: transparent; cursor: pointer;
color: rgba(28, 27, 24, 0.4); border-radius: 6px; flex: none;
color: var(--del-ink); border-radius: 6px; flex: none;
width: 22px; height: 22px; display: flex; align-items: center; justify-content: center;
font: 600 14px/1 inherit; padding: 0;
}
.qd-file-row .del:hover { color: #c0392b; background: rgba(192, 57, 43, 0.1); }

/* Dark theme follows the board, same as the logo. */
.dark .qd-filebar, .qd-filebar.dark { color: #f4f2ea; }
.qd-filebar.dark::before { background: rgba(244, 242, 234, 0.22); }
.qd-filebar.dark .qd-file-btn:hover,
.qd-filebar.dark .qd-file-name:hover { background: rgba(244, 242, 234, 0.09); }
.qd-filebar.dark .qd-file-name:focus { background: rgba(244, 242, 234, 0.12); }
.qd-files-menu.dark {
background: #262624; color: #f4f2ea; border-color: rgba(244, 242, 234, 0.14);
box-shadow: 0 14px 40px -12px rgba(0, 0, 0, 0.6);
}
.qd-files-menu.dark .qd-file-row:hover { background: rgba(244, 242, 234, 0.08); }
.qd-files-menu.dark .qd-file-row .del { color: rgba(244, 242, 234, 0.45); }
.qd-files-menu.dark .qd-file-row .del:hover { color: #ff7b6b; background: rgba(255, 123, 107, 0.14); }
.qd-file-row .del:hover { color: var(--del-hot); background: var(--del-hot-bg); }

@media (max-width: 560px) {
.qd-filebar { left: 128px; }
/* the wordmark folds to its glyph so the file bar fits beside it */
.qd-app-logo { padding: 8px 11px; }
.qd-app-logo .logo-word { display: none; }
.qd-filebar { left: 60px; }
.qd-file-name { max-width: 110px; }
.qd-files-menu { left: 14px; }
}
Expand All @@ -116,7 +143,7 @@
<path d="M7 21 C7 12, 13 6.5, 20 7.5 C26.5 8.5, 27.5 16, 22 18.8 C17.5 21, 13 19.5, 14 15" stroke="currentColor" stroke-width="3" stroke-linecap="round" fill="none"/>
<circle cx="24.5" cy="24.5" r="2.6" fill="#2f6fed"/>
</svg>
Quickdraw
<span class="logo-word">Quickdraw</span>
</a>
<div class="qd-filebar" id="filebar">
<button class="qd-file-btn" id="files-btn" type="button" title="Your files" aria-label="Your files" aria-haspopup="true" aria-expanded="false">
Expand Down
8 changes: 3 additions & 5 deletions apps/app/src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -224,14 +224,12 @@ nameInput.addEventListener('blur', () => {
openFile(currentFile().id)
sizeNameInput()

// Remember the theme across visits; the chrome follows the board's colors.
const logo = document.getElementById('logo')
// Remember the theme across visits; the chrome follows the board through the
// root .dark class (the CSS variables up in index.html key off it).
const syncTheme = () => {
const dark = editor.theme.id === 'dark'
localStorage.setItem(THEME_KEY, editor.theme.id)
logo.classList.toggle('dark', dark)
filebar.classList.toggle('dark', dark)
menu.classList.toggle('dark', dark)
document.documentElement.classList.toggle('dark', dark)
document.querySelector('meta[name="theme-color"]')
?.setAttribute('content', dark ? '#1e1e1c' : '#faf8f4')
}
Expand Down
6 changes: 3 additions & 3 deletions apps/docs/app/layout.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ const navbar = (
<Navbar
logo={logo}
logoLink="https://tryquickdraw.com"
projectLink="https://github.com/nmndwivedi/quickdraw"
projectLink="https://github.com/quickdrawjs/quickdraw"
>
{npmLink}
</Navbar>
Expand All @@ -76,7 +76,7 @@ const footer = (
<span>
MIT licensed. Built in the open —{' '}
<a
href="https://github.com/nmndwivedi/quickdraw"
href="https://github.com/quickdrawjs/quickdraw"
target="_blank"
rel="noreferrer"
style={footerLink}
Expand Down Expand Up @@ -124,7 +124,7 @@ export default async function RootLayout({ children }) {
navbar={navbar}
footer={footer}
pageMap={await getPageMap()}
docsRepositoryBase="https://github.com/nmndwivedi/quickdraw/edit/main/apps/docs"
docsRepositoryBase="https://github.com/quickdrawjs/quickdraw/edit/main/apps/docs"
editLink="Edit this page on GitHub"
sidebar={{ defaultMenuCollapseLevel: 2 }}
>
Expand Down
Loading
Loading