(html: string): T | undefined => {
20 | const match = html.match(/\(noya\)(.*?)<\/p>/);
21 |
22 | if (!match) return;
23 |
24 | const base64 = match[1];
25 | const utf8 = Base64.decode(base64);
26 | const json = UTF16.fromUTF8(utf8);
27 | const data = JSON.parse(json);
28 |
29 | return data;
30 | },
31 | };
32 |
--------------------------------------------------------------------------------
/packages/noya-utils/src/delimitedPath.ts:
--------------------------------------------------------------------------------
1 | export const sep = '/';
2 |
3 | export function basename(filename: string) {
4 | return filename.slice(filename.lastIndexOf(sep) + 1);
5 | }
6 |
7 | export function dirname(filename: string) {
8 | const base = basename(filename);
9 | return filename.slice(0, -(base.length + 1));
10 | }
11 |
12 | export function join(components: (string | null | undefined)[]) {
13 | return components.filter((component) => !!component).join(sep);
14 | }
15 |
--------------------------------------------------------------------------------
/packages/noya-utils/src/getIncrementedName.ts:
--------------------------------------------------------------------------------
1 | const numberSuffixRegExp = /(.*?)(\s\d+)?$/;
2 |
3 | export function getIncrementedName(
4 | originalName: string,
5 | names: string[],
6 | ): string {
7 | const [, prefix] = originalName.match(numberSuffixRegExp) || [];
8 |
9 | const numbers = [originalName, ...names]
10 | .filter((name) => name.startsWith(prefix))
11 | .map((name) => {
12 | const [, , number] = name.match(numberSuffixRegExp) || [];
13 | return number ? parseInt(number) : 1;
14 | })
15 | .sort();
16 |
17 | const maxNumber = numbers[numbers.length - 1];
18 |
19 | return `${prefix} ${maxNumber + 1}`;
20 | }
21 |
--------------------------------------------------------------------------------
/packages/noya-utils/src/groupBy.ts:
--------------------------------------------------------------------------------
1 | export function groupBy(
2 | values: T[],
3 | projection: (value: T) => U,
4 | ) {
5 | const result: { [key in PropertyKey]: T[] } = {};
6 |
7 | values.forEach((value) => {
8 | const key = projection(value);
9 |
10 | if (key in result) {
11 | result[key].push(value);
12 | } else {
13 | result[key] = [value];
14 | }
15 | });
16 |
17 | return result;
18 | }
19 |
--------------------------------------------------------------------------------
/packages/noya-utils/src/invert.ts:
--------------------------------------------------------------------------------
1 | export type Invert> = {
2 | [K in keyof T as T[K]]: K;
3 | };
4 |
5 | export function invert>(
6 | record: T,
7 | ): Invert {
8 | return Object.fromEntries(
9 | Object.entries(record).map(([type, extension]) => [extension, type]),
10 | ) as Invert;
11 | }
12 |
--------------------------------------------------------------------------------
/packages/noya-utils/src/isDeepEqual.ts:
--------------------------------------------------------------------------------
1 | import { isEqual } from './internal/isEqual';
2 |
3 | export function isDeepEqual(a: T, b: T): boolean {
4 | return isEqual(a, b, true, false);
5 | }
6 |
--------------------------------------------------------------------------------
/packages/noya-utils/src/isEqualIgnoringUndefinedKeys.ts:
--------------------------------------------------------------------------------
1 | import { isEqual } from './internal/isEqual';
2 |
3 | export function isEqualIgnoringUndefinedKeys(a: T, b: T): boolean {
4 | return isEqual(a, b, true, true);
5 | }
6 |
--------------------------------------------------------------------------------
/packages/noya-utils/src/isNumberEqual.ts:
--------------------------------------------------------------------------------
1 | // The built-in Number.EPSILON is too small for some of our calculations.
2 | // Possibly this is due to conversion to/from sketch files or wasm, or
3 | // because we multiply/divide which compounds this error
4 | export function isNumberEqual(a: number, b: number): boolean {
5 | return Math.abs(a - b) < 1e-6;
6 | }
7 |
--------------------------------------------------------------------------------
/packages/noya-utils/src/isShallowEqual.ts:
--------------------------------------------------------------------------------
1 | import { isEqual } from './internal/isEqual';
2 |
3 | export function isShallowEqual(a: T, b: T): boolean {
4 | return isEqual(a, b, false, false);
5 | }
6 |
--------------------------------------------------------------------------------
/packages/noya-utils/src/lerp.ts:
--------------------------------------------------------------------------------
1 | export function lerp(a: number, b: number, t: number) {
2 | return a * (1 - t) + b * t;
3 | }
4 |
--------------------------------------------------------------------------------
/packages/noya-utils/src/memoize.ts:
--------------------------------------------------------------------------------
1 | // TODO: Review this. Is it a good approach for Noya?
2 | export function memoize(
3 | f: (...values: I) => O,
4 | ): (...values: I) => O {
5 | const intermediateCache = new Map();
6 | const cache: Map = new Map();
7 | let intermediateCacheIndex = 0;
8 |
9 | return (...values: I): O => {
10 | let key = '';
11 |
12 | for (const value of values) {
13 | // Assign each argument value a unique index
14 | if (!intermediateCache.has(value)) {
15 | intermediateCache.set(value, `${intermediateCacheIndex++}`);
16 | }
17 |
18 | // Assemble a cache key from the combined indexes
19 | key += intermediateCache.get(value)! + ':';
20 | }
21 |
22 | if (!cache.has(key)) {
23 | cache.set(key, f(...values));
24 | }
25 |
26 | return cache.get(key)!;
27 | };
28 | }
29 |
--------------------------------------------------------------------------------
/packages/noya-utils/src/memoizedGetter.ts:
--------------------------------------------------------------------------------
1 | export function memoizedGetter(
2 | target: unknown,
3 | propertyKey: string,
4 | value: T,
5 | ): T {
6 | Object.defineProperty(target, propertyKey, { value, writable: false });
7 | return value;
8 | }
9 |
--------------------------------------------------------------------------------
/packages/noya-utils/src/partition.ts:
--------------------------------------------------------------------------------
1 | export function partition(
2 | array: readonly T[],
3 | predicate: (item: T) => item is U,
4 | ): [U[], Exclude[]];
5 | export function partition(
6 | array: readonly T[],
7 | predicate: (item: T) => boolean,
8 | ): [T[], T[]];
9 | export function partition(
10 | array: readonly T[],
11 | predicate: (item: T) => boolean,
12 | ): [T[], T[]] {
13 | const left: T[] = [];
14 | const right: T[] = [];
15 |
16 | for (const item of array) {
17 | if (predicate(item)) {
18 | left.push(item);
19 | } else {
20 | right.push(item);
21 | }
22 | }
23 |
24 | return [left, right];
25 | }
26 |
--------------------------------------------------------------------------------
/packages/noya-utils/src/rotate.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Rotates the elements within the given array so that the element at
3 | * the index specified by `toStartAt` becomes the start of the array.
4 | */
5 | export function rotate(array: T[], toStartAt: number): T[] {
6 | let start = toStartAt % array.length;
7 |
8 | if (start < 0) {
9 | start += array.length;
10 | }
11 |
12 | if (array.length < 2 || start === 0) return array;
13 |
14 | return [...array.slice(start), ...array.slice(0, start)];
15 | }
16 |
--------------------------------------------------------------------------------
/packages/noya-utils/src/round.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Round to precision, if necessary.
3 | *
4 | * Examples:
5 | * - round(1.111, 2) => 1.11
6 | * - round(1.115, 2) => 1.12
7 | * - round(1, 2) => 1
8 | *
9 | * https://stackoverflow.com/a/11832950
10 | */
11 | export function round(number: number, precision = 0) {
12 | const base = Math.pow(10, precision);
13 | return Math.round((number + Number.EPSILON) * base) / base;
14 | }
15 |
--------------------------------------------------------------------------------
/packages/noya-utils/src/sortBy.ts:
--------------------------------------------------------------------------------
1 | export function sortBy<
2 | K extends PropertyKey,
3 | Item extends { [key in K]: string }
4 | >(array: Item[], key: K) {
5 | return [...array].sort((a, b) => {
6 | const aName = a[key].toUpperCase();
7 | const bName = b[key].toUpperCase();
8 |
9 | return aName > bName ? 1 : aName < bName ? -1 : 0;
10 | });
11 | }
12 |
--------------------------------------------------------------------------------
/packages/noya-utils/src/sum.ts:
--------------------------------------------------------------------------------
1 | export function sum(values: number[]) {
2 | let value = 0;
3 |
4 | for (let i = 0; i < values.length; i++) {
5 | value += values[i];
6 | }
7 |
8 | return value;
9 | }
10 |
--------------------------------------------------------------------------------
/packages/noya-utils/src/types.ts:
--------------------------------------------------------------------------------
1 | // TypeScript will be adding an official `TupleOf` utility type, which will
2 | // be more performant in the case of a large N, but in the meantime, we can
3 | // use this one.
4 | // https://github.com/microsoft/TypeScript/pull/40002
5 | // https://github.com/piotrwitek/utility-types/pull/162
6 | export type TupleOf = N extends N
7 | ? number extends N
8 | ? T[]
9 | : _TupleOf
10 | : never;
11 |
12 | type _TupleOf = R['length'] extends N
13 | ? R
14 | : _TupleOf;
15 |
16 | export type Brand = K & { __brand: T };
17 |
--------------------------------------------------------------------------------
/packages/noya-utils/src/unique.ts:
--------------------------------------------------------------------------------
1 | export function unique(array: T[]) {
2 | return [...new Set(array)];
3 | }
4 |
--------------------------------------------------------------------------------
/packages/noya-utils/src/upperFirst.ts:
--------------------------------------------------------------------------------
1 | export function upperFirst(string: string) {
2 | return string.slice(0, 1).toUpperCase() + string.slice(1);
3 | }
4 |
--------------------------------------------------------------------------------
/packages/noya-utils/src/utf16.ts:
--------------------------------------------------------------------------------
1 | function toUTF8(string: string) {
2 | const encoder = new TextEncoder();
3 | return encoder.encode(string);
4 | }
5 |
6 | function fromUTF8(encoded: Uint8Array) {
7 | const decoder = new TextDecoder();
8 | return decoder.decode(encoded);
9 | }
10 |
11 | export const UTF16 = {
12 | toUTF8,
13 | fromUTF8,
14 | };
15 |
--------------------------------------------------------------------------------
/packages/noya-utils/src/windowsOf.ts:
--------------------------------------------------------------------------------
1 | import { TupleOf } from './types';
2 |
3 | export function windowsOf(
4 | array: T[],
5 | size: N,
6 | wrapsAround = false,
7 | ): TupleOf[] {
8 | let arr = array;
9 |
10 | if (arr.length < size) return [];
11 |
12 | if (wrapsAround) {
13 | arr = array.slice();
14 |
15 | for (let i = 0; i < size - 1; i++) {
16 | arr.push(array[i]);
17 | }
18 | }
19 |
20 | const result: TupleOf[] = [];
21 |
22 | for (let i = 0; i <= arr.length - size; i++) {
23 | result.push(arr.slice(i, i + size) as TupleOf);
24 | }
25 |
26 | return result;
27 | }
28 |
--------------------------------------------------------------------------------
/packages/pathkit/README.md:
--------------------------------------------------------------------------------
1 | This is where our custom pathkit-wasm build lives.
2 |
--------------------------------------------------------------------------------
/packages/pathkit/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "pathkit",
3 | "version": "1.0.0",
4 | "main": "src/index.ts",
5 | "license": "UNLICENSED"
6 | }
7 |
--------------------------------------------------------------------------------
/packages/pathkit/src/index.ts:
--------------------------------------------------------------------------------
1 | const init: any = require('./pathkit.js');
2 |
3 | export { init as PathKitInit };
4 |
--------------------------------------------------------------------------------
/packages/site/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["next/babel"],
3 | "plugins": [["styled-components", { "ssr": true }]]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/site/guidebook.d.ts:
--------------------------------------------------------------------------------
1 | import type { TreeNode } from 'generate-guidebook';
2 |
3 | const value: TreeNode;
4 |
5 | export default value;
6 |
--------------------------------------------------------------------------------
/packages/site/next-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | // NOTE: This file should not be edited
5 | // see https://nextjs.org/docs/basic-features/typescript for more information.
6 |
--------------------------------------------------------------------------------
/packages/site/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | };
7 |
--------------------------------------------------------------------------------
/packages/site/public/SocialCard.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/noya-app/noya/45e9bd99d4efa84d773bb6ff3e72f323f16e85eb/packages/site/public/SocialCard.png
--------------------------------------------------------------------------------
/packages/site/searchIndex.d.ts:
--------------------------------------------------------------------------------
1 | const index: any;
2 |
3 | export default index;
4 |
--------------------------------------------------------------------------------
/packages/site/src/__tests__/__snapshots__/fuzzyScorer.test.ts.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`filter multiple matches 1`] = `
4 | Array [
5 | Object {
6 | "index": 1,
7 | "labelMatch": Array [
8 | Object {
9 | "end": 1,
10 | "start": 0,
11 | },
12 | ],
13 | "score": 131072,
14 | },
15 | Object {
16 | "index": 0,
17 | "labelMatch": Array [
18 | Object {
19 | "end": 6,
20 | "start": 5,
21 | },
22 | ],
23 | "score": 32768,
24 | },
25 | ]
26 | `;
27 |
28 | exports[`filter single match 1`] = `
29 | Array [
30 | Object {
31 | "index": 0,
32 | "labelMatch": Array [
33 | Object {
34 | "end": 9,
35 | "start": 5,
36 | },
37 | ],
38 | "score": 32768,
39 | },
40 | ]
41 | `;
42 |
--------------------------------------------------------------------------------
/packages/site/src/__tests__/tailwind.test.ts:
--------------------------------------------------------------------------------
1 | import { readFileSync } from 'fs';
2 | import path from 'path';
3 | import { getBlockClassName } from '../ayon/blocks/tailwind';
4 |
5 | // Jest doesn't know how to import a text file, so we mock it
6 | jest.mock('../../safelist.txt', () => {
7 | return {
8 | default: readFileSync(path.join(__dirname, '../../safelist.txt'), 'utf8'),
9 | };
10 | });
11 |
12 | it('only applies last class within a group', () => {
13 | expect(getBlockClassName(['bg-red-500', 'bg-blue-500'])).toEqual(
14 | 'bg-blue-500',
15 | );
16 | });
17 |
18 | it('applies one class within every group', () => {
19 | expect(getBlockClassName(['text-red-500', 'bg-blue-500'])).toEqual(
20 | 'text-red-500 bg-blue-500',
21 | );
22 | });
23 |
--------------------------------------------------------------------------------
/packages/site/src/assets/ConfigureBlockText.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/noya-app/noya/45e9bd99d4efa84d773bb6ff3e72f323f16e85eb/packages/site/src/assets/ConfigureBlockText.webp
--------------------------------------------------------------------------------
/packages/site/src/assets/ConfigureBlockType.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/noya-app/noya/45e9bd99d4efa84d773bb6ff3e72f323f16e85eb/packages/site/src/assets/ConfigureBlockType.webp
--------------------------------------------------------------------------------
/packages/site/src/assets/InsertBlock.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/noya-app/noya/45e9bd99d4efa84d773bb6ff3e72f323f16e85eb/packages/site/src/assets/InsertBlock.webp
--------------------------------------------------------------------------------
/packages/site/src/assets/PremiumTemplateMosaic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/noya-app/noya/45e9bd99d4efa84d773bb6ff3e72f323f16e85eb/packages/site/src/assets/PremiumTemplateMosaic.png
--------------------------------------------------------------------------------
/packages/site/src/assets/docs/InsertTool.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/noya-app/noya/45e9bd99d4efa84d773bb6ff3e72f323f16e85eb/packages/site/src/assets/docs/InsertTool.png
--------------------------------------------------------------------------------
/packages/site/src/assets/docs/ProjectConfiguration.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/noya-app/noya/45e9bd99d4efa84d773bb6ff3e72f323f16e85eb/packages/site/src/assets/docs/ProjectConfiguration.png
--------------------------------------------------------------------------------
/packages/site/src/assets/docs/ProjectContextMenu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/noya-app/noya/45e9bd99d4efa84d773bb6ff3e72f323f16e85eb/packages/site/src/assets/docs/ProjectContextMenu.png
--------------------------------------------------------------------------------
/packages/site/src/assets/docs/RegionTool.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/noya-app/noya/45e9bd99d4efa84d773bb6ff3e72f323f16e85eb/packages/site/src/assets/docs/RegionTool.png
--------------------------------------------------------------------------------
/packages/site/src/assets/docs/RegionToolExample.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/noya-app/noya/45e9bd99d4efa84d773bb6ff3e72f323f16e85eb/packages/site/src/assets/docs/RegionToolExample.webp
--------------------------------------------------------------------------------
/packages/site/src/ayon/AttributionCard.tsx:
--------------------------------------------------------------------------------
1 | import Link from 'next/link';
2 | import React from 'react';
3 | import { Attribution } from './resolve/RandomImageResolver';
4 |
5 | function AttributionLink({ url, children }: { url: string; children: string }) {
6 | return (
7 |
8 |
13 | {children}
14 |
15 |
16 | );
17 | }
18 |
19 | export function AttributionCard({ user, source }: Attribution) {
20 | return (
21 | <>
22 | Photo by {user.name} on{' '}
23 | {source.name}
24 | >
25 | );
26 | }
27 |
--------------------------------------------------------------------------------
/packages/site/src/ayon/blocks/CardBlock.tsx:
--------------------------------------------------------------------------------
1 | import { BlockDefinition } from 'noya-state';
2 | import { BoxBlock } from './BoxBlock';
3 | import { renderStack } from './render';
4 | import { isWithinRectRange } from './score';
5 | import { cardSymbol } from './symbols';
6 |
7 | export const CardBlock: BlockDefinition = {
8 | editorVersion: 2,
9 | symbol: cardSymbol,
10 | parser: 'regular',
11 | hashtags: BoxBlock.hashtags,
12 | infer: ({ frame, blockText, siblingBlocks }) => {
13 | return Math.max(
14 | isWithinRectRange({
15 | rect: frame,
16 | minWidth: 200,
17 | minHeight: 250,
18 | maxWidth: 300,
19 | maxHeight: 400,
20 | })
21 | ? 1
22 | : 0,
23 | 0.1,
24 | );
25 | },
26 | render: (props) => renderStack({ props, block: CardBlock }),
27 | };
28 |
--------------------------------------------------------------------------------
/packages/site/src/ayon/blocks/SignInBlock.tsx:
--------------------------------------------------------------------------------
1 | import { BlockDefinition } from 'noya-state';
2 | import { BoxBlock } from './BoxBlock';
3 | import { renderStack } from './render';
4 | import { signInSymbol } from './symbols';
5 |
6 | export const SignInBlock: BlockDefinition = {
7 | editorVersion: 2,
8 | symbol: signInSymbol,
9 | parser: 'regular',
10 | hashtags: BoxBlock.hashtags,
11 | infer: ({ frame, blockText, siblingBlocks }) => {
12 | if (
13 | siblingBlocks.find((block) => block.symbolId === signInSymbol.symbolID)
14 | ) {
15 | return 0;
16 | }
17 |
18 | return 0.1;
19 | },
20 | render: (props) => renderStack({ props, block: SignInBlock }),
21 | };
22 |
--------------------------------------------------------------------------------
/packages/site/src/ayon/blocks/SpacerBlock.tsx:
--------------------------------------------------------------------------------
1 | import { Spacer } from '@chakra-ui/react';
2 | import { BlockDefinition } from 'noya-state';
3 | import React from 'react';
4 | import { parseBlock } from '../parse';
5 | import { spacerSymbol } from './symbols';
6 | import { getBlockClassName, tailwindBlockClasses } from './tailwind';
7 |
8 | export const SpacerBlock: BlockDefinition = {
9 | symbol: spacerSymbol,
10 | parser: 'regular',
11 | hashtags: tailwindBlockClasses,
12 | infer: ({ frame, blockText }) => 0,
13 | isPassthrough: true,
14 | render: (props) => {
15 | const { parameters } = parseBlock(props.blockText, 'regular');
16 | const hashtags = Object.keys(parameters);
17 |
18 | return (
19 |
25 | );
26 | },
27 | };
28 |
--------------------------------------------------------------------------------
/packages/site/src/ayon/blocks/SwitchBlock.tsx:
--------------------------------------------------------------------------------
1 | import { Switch } from '@chakra-ui/react';
2 | import { BlockDefinition } from 'noya-state';
3 | import React from 'react';
4 | import { parseBlock } from '../parse';
5 | import { switchSymbol } from './symbols';
6 |
7 | const placeholderText = '#off';
8 |
9 | const globalHashtags = ['on', 'off', 'disabled'];
10 |
11 | const parser = 'regular';
12 |
13 | export const SwitchBlock: BlockDefinition = {
14 | symbol: switchSymbol,
15 | parser,
16 | hashtags: globalHashtags,
17 | placeholderText,
18 | infer: ({ frame, blockText }) => 0.1,
19 | render: (props) => {
20 | const {
21 | parameters: { on, disabled },
22 | } = parseBlock(props.blockText, parser, {
23 | placeholder: placeholderText,
24 | });
25 | const height = props.frame?.height ?? 25;
26 | const size = height >= 35 ? 'lg' : height >= 25 ? 'md' : 'sm';
27 | return ;
28 | },
29 | };
30 |
--------------------------------------------------------------------------------
/packages/site/src/ayon/blocks/TileCardBlock.tsx:
--------------------------------------------------------------------------------
1 | import { BlockDefinition } from 'noya-state';
2 | import { BoxBlock } from './BoxBlock';
3 | import { renderStack } from './render';
4 | import { isWithinRectRange } from './score';
5 | import { tileCardSymbol } from './symbols';
6 |
7 | export const TileCardBlock: BlockDefinition = {
8 | editorVersion: 2,
9 | symbol: tileCardSymbol,
10 | parser: 'regular',
11 | hashtags: BoxBlock.hashtags,
12 | infer: ({ frame, blockText, siblingBlocks }) => {
13 | return Math.max(
14 | isWithinRectRange({
15 | rect: frame,
16 | minWidth: 200,
17 | minHeight: 200,
18 | maxWidth: 250,
19 | maxHeight: 250,
20 | })
21 | ? 1.2
22 | : 0,
23 | 0.1,
24 | );
25 | },
26 | render: (props) => renderStack({ props, block: TileCardBlock }),
27 | };
28 |
--------------------------------------------------------------------------------
/packages/site/src/ayon/blocks/blockTheme.ts:
--------------------------------------------------------------------------------
1 | export const accentColor = {
2 | 50: '#eff6ff',
3 | 100: '#dbeafe',
4 | 200: '#bfdbfe',
5 | 300: '#93c5fd',
6 | 400: '#60a5fa',
7 | 500: '#3b82f6',
8 | 600: '#2563eb',
9 | 700: '#1d4ed8',
10 | 800: '#1e40af',
11 | 900: '#1e3a8a',
12 | };
13 |
14 | export const buttonColors = {
15 | default: {
16 | backgroundColor: '#f1f5f9',
17 | color: '#000',
18 | },
19 | light: {
20 | backgroundColor: '#f1f5f9',
21 | color: '#000',
22 | },
23 | dark: {
24 | backgroundColor: '#475569',
25 | color: '#fff',
26 | },
27 | primary: {
28 | backgroundColor: '#15803d',
29 | color: '#fff',
30 | },
31 | secondary: {
32 | backgroundColor: '#94a3b8',
33 | color: '#fff',
34 | },
35 | warning: {
36 | backgroundColor: '#fde047',
37 | color: '#000',
38 | },
39 | danger: {
40 | backgroundColor: '#dc2626',
41 | color: '#fff',
42 | },
43 | };
44 |
--------------------------------------------------------------------------------
/packages/site/src/ayon/blocks/flattenPassthroughLayers.tsx:
--------------------------------------------------------------------------------
1 | import Sketch from 'noya-file-format';
2 | import { Layers } from 'noya-state';
3 | import { Blocks } from './blocks';
4 |
5 | export function flattenPassthroughLayers(
6 | symbolMaster: Sketch.SymbolMaster,
7 | ): Sketch.SymbolInstance[] {
8 | return symbolMaster.layers
9 | .filter(Layers.isSymbolInstance)
10 | .flatMap((layer) => {
11 | const block = Blocks[layer.symbolID];
12 |
13 | if (block && block.isPassthrough) {
14 | return flattenPassthroughLayers(block.symbol);
15 | }
16 |
17 | return layer;
18 | });
19 | }
20 |
--------------------------------------------------------------------------------
/packages/site/src/ayon/createAyonDocument.ts:
--------------------------------------------------------------------------------
1 | import { SketchModel } from 'noya-sketch-model';
2 | import { createSketchFile } from 'noya-state';
3 |
4 | const artboard = SketchModel.artboard({
5 | name: 'Wireframe',
6 | frame: SketchModel.rect({
7 | x: 0,
8 | y: 0,
9 | width: 1280,
10 | height: 720,
11 | }),
12 | });
13 |
14 | export function createAyonDocument() {
15 | const sketch = createSketchFile(SketchModel.page({ layers: [artboard] }));
16 |
17 | return sketch;
18 | }
19 |
--------------------------------------------------------------------------------
/packages/site/src/ayon/editor/resetNodes.tsx:
--------------------------------------------------------------------------------
1 | import { Editor, Node, Point, Transforms } from 'slate';
2 | import { CustomEditor } from './types';
3 |
4 | export function resetNodes(
5 | editor: CustomEditor,
6 | options: {
7 | nodes?: Node[];
8 | at?: Location;
9 | } = {},
10 | ): void {
11 | const children = [...editor.children];
12 |
13 | children.forEach((node) =>
14 | editor.apply({ type: 'remove_node', path: [0], node }),
15 | );
16 |
17 | const nodes = options.nodes;
18 |
19 | if (nodes) {
20 | Editor.withoutNormalizing(editor, () => {
21 | nodes.forEach((node, i) =>
22 | editor.apply({ type: 'insert_node', path: [i], node: node }),
23 | );
24 | });
25 | }
26 |
27 | const point =
28 | options.at && Point.isPoint(options.at)
29 | ? options.at
30 | : Editor.end(editor, []);
31 |
32 | if (point) {
33 | Transforms.select(editor, point);
34 | }
35 |
36 | Editor.normalize(editor, { force: true });
37 | }
38 |
--------------------------------------------------------------------------------
/packages/site/src/ayon/editor/types.ts:
--------------------------------------------------------------------------------
1 | import { BaseEditor, Descendant } from 'slate';
2 | import { HistoryEditor } from 'slate-history';
3 | import { ReactEditor } from 'slate-react';
4 |
5 | /**
6 | * In general, the fewer things we need in here the better, since it can be
7 | * tricky to sync editor state with the rest of the app.
8 | */
9 | export type ParagraphElement = {
10 | type: 'paragraph';
11 | label?: string;
12 | placeholder?: string;
13 | symbolId: string;
14 | children: Descendant[];
15 | layerId?: string;
16 | };
17 |
18 | export type CustomElement = ParagraphElement;
19 |
20 | export type CustomEditor = BaseEditor &
21 | ReactEditor &
22 | HistoryEditor & {
23 | symbolId: string;
24 | };
25 |
26 | declare module 'slate' {
27 | interface CustomTypes {
28 | Editor: CustomEditor;
29 | Element: CustomElement;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/packages/site/src/ayon/inferBlock.ts:
--------------------------------------------------------------------------------
1 | import { DrawableLayerType, InferBlockProps, InferBlockType } from 'noya-state';
2 | import { allInsertableBlocks } from './blocks/blocks';
3 | import { InferredBlockTypeResult } from './types';
4 |
5 | export function inferBlockTypes(
6 | input: InferBlockProps,
7 | ): InferredBlockTypeResult[] {
8 | let results: InferredBlockTypeResult[] = [];
9 |
10 | for (const block of allInsertableBlocks) {
11 | results.push({
12 | type: { symbolId: block.symbol.symbolID },
13 | score: block.infer(input),
14 | });
15 | }
16 |
17 | results.sort(
18 | (
19 | a: { type: DrawableLayerType; score: number },
20 | b: { type: DrawableLayerType; score: number },
21 | ) => b.score - a.score,
22 | );
23 |
24 | return results;
25 | }
26 |
27 | export const inferBlockType: InferBlockType = (input) => {
28 | return inferBlockTypes(input)[0].type;
29 | };
30 |
--------------------------------------------------------------------------------
/packages/site/src/ayon/stacking.ts:
--------------------------------------------------------------------------------
1 | export const Stacking = {
2 | level: {
3 | interactive: 1,
4 | overlay: 2,
5 | menu: 3,
6 | },
7 | };
8 |
--------------------------------------------------------------------------------
/packages/site/src/ayon/types.ts:
--------------------------------------------------------------------------------
1 | import { DrawableLayerType } from 'noya-state';
2 |
3 | export type InferredBlockTypeResult = {
4 | type: DrawableLayerType;
5 | score: number;
6 | };
7 |
--------------------------------------------------------------------------------
/packages/site/src/components/OnboardingAnimation.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export function OnboardingAnimation({ src }: { src: string }) {
4 | return (
5 |
17 | );
18 | }
19 |
--------------------------------------------------------------------------------
/packages/site/src/components/OptionalNoyaAPIProvider.tsx:
--------------------------------------------------------------------------------
1 | import { NoyaAPI, NoyaAPIProvider } from 'noya-api';
2 | import React, { useEffect, useState } from 'react';
3 | import { networkClientThatThrows } from '../utils/noyaClient';
4 |
5 | export function OptionalNoyaAPIProvider({
6 | children,
7 | }: {
8 | children: React.ReactNode;
9 | }) {
10 | const [client, setClient] = useState();
11 |
12 | useEffect(() => {
13 | async function main() {
14 | try {
15 | if (!networkClientThatThrows) return;
16 | await networkClientThatThrows.auth.session();
17 | setClient(
18 | new NoyaAPI.Client({ networkClient: networkClientThatThrows }),
19 | );
20 | } catch {
21 | // Ignore
22 | }
23 | }
24 |
25 | main();
26 | }, []);
27 |
28 | return {children};
29 | }
30 |
--------------------------------------------------------------------------------
/packages/site/src/components/ProjectTitle.tsx:
--------------------------------------------------------------------------------
1 | import { Button, Spacer, useDesignSystemTheme } from 'noya-designsystem';
2 | import { ChevronDownIcon, DashboardIcon } from 'noya-icons';
3 | import React, { forwardRef, ReactNode } from 'react';
4 |
5 | interface Props {
6 | children: ReactNode;
7 | onClick?: () => void;
8 | }
9 |
10 | export const ProjectTitle = forwardRef(function ProjectTitle(
11 | { children, onClick }: Props,
12 | ref: React.Ref,
13 | ) {
14 | const theme = useDesignSystemTheme();
15 |
16 | return (
17 |
24 | );
25 | });
26 |
--------------------------------------------------------------------------------
/packages/site/src/contexts/ProjectContext.tsx:
--------------------------------------------------------------------------------
1 | import { createContext, ReactNode, useContext } from 'react';
2 |
3 | export type ProjectContextValue = {
4 | setLeftToolbar: (value: ReactNode) => void;
5 | setRightToolbar: (value: ReactNode) => void;
6 | setCenterToolbar: (value: ReactNode) => void;
7 | };
8 |
9 | const ProjectContext = createContext({
10 | setLeftToolbar: () => {},
11 | setRightToolbar: () => {},
12 | setCenterToolbar: () => {},
13 | });
14 |
15 | export const ProjectProvider = ProjectContext.Provider;
16 |
17 | export function useProject() {
18 | return useContext(ProjectContext);
19 | }
20 |
--------------------------------------------------------------------------------
/packages/site/src/docs/socialConfig.ts:
--------------------------------------------------------------------------------
1 | import { GuidebookConfig } from 'react-guidebook';
2 |
3 | export const socialConfig: GuidebookConfig = {
4 | title: 'Noya Documentation',
5 | location: {
6 | host: (
7 | process.env.NEXT_PUBLIC_NOYA_WEB_URL ?? 'https://www.noya.io'
8 | ).replace(/https?:\/\//, ''),
9 | path: '/app/docs',
10 | },
11 | author: {
12 | twitter: 'noyasoftware',
13 | },
14 | favicons: [
15 | {
16 | type: 'image/x-icon',
17 | path: '/favicon.ico',
18 | },
19 | ],
20 | previewImage: {
21 | type: 'image/png',
22 | width: '1200',
23 | height: '630',
24 | alt: 'Noya Documentation',
25 | path: '/app/SocialCard.png',
26 | },
27 | };
28 |
--------------------------------------------------------------------------------
/packages/site/src/hooks/useToggleTimer.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export function useToggleTimer(delay: number) {
4 | const [value, setValue] = React.useState(false);
5 |
6 | React.useEffect(() => {
7 | if (!value) return;
8 |
9 | const timeout = setTimeout(() => setValue(false), delay);
10 |
11 | return () => clearTimeout(timeout);
12 | }, [delay, value]);
13 |
14 | return { value, trigger: () => setValue(true) };
15 | }
16 |
--------------------------------------------------------------------------------
/packages/site/src/pages/docs/blocks.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Block Reference
3 | ---
4 |
5 | ## Categories
6 |
7 | There are 3 main categories of block:
8 |
9 | - [Application](/app/docs/blocks/application) - blocks that are used to build
10 | web and mobile apps
11 | - [Marketing](/app/docs/blocks/marketing) - blocks that are used to build
12 | landing pages and marketing sites
13 | - [Elements](/app/docs/blocks/elements) - smaller blocks like "Button" and
14 | "Image" that can be used to build other, bigger blocks
15 |
--------------------------------------------------------------------------------
/packages/site/src/pages/docs/blocks/application.mdx:
--------------------------------------------------------------------------------
1 | ## Available blocks
2 |
3 | Here are some of the top blocks you might find useful when creating
4 | applications:
5 |
6 |
7 |
--------------------------------------------------------------------------------
/packages/site/src/pages/docs/blocks/application/sidebar.mdx:
--------------------------------------------------------------------------------
1 | import { sidebarSymbolId } from '../../../../ayon/blocks/symbolIds';
2 |
3 | ## Live Preview
4 |
5 | You can double click the block to edit its contents.
6 |
7 |
8 |
9 | ## Configuration
10 |
11 | Each item in the sidebar should be on a separate line. To show a specific item
12 | as active, type `*` before the item name.
13 |
14 | You can further configure the sidebar's style by adding:
15 |
16 | - `#title` - to use the first item as a title
17 | - `#dark` - to use the dark theme
18 |
19 | Here's an example configuration that uses these options (double click to edit):
20 |
21 |
29 |
--------------------------------------------------------------------------------
/packages/site/src/pages/docs/blocks/application/sign-in.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Sign In
3 | ---
4 |
5 | import { signInSymbolId } from '../../../../ayon/blocks/symbolIds';
6 |
7 | ## Live Preview
8 |
9 | You can double click the block to edit its contents.
10 |
11 |
12 |
--------------------------------------------------------------------------------
/packages/site/src/pages/docs/blocks/application/table.mdx:
--------------------------------------------------------------------------------
1 | import { tableSymbolId } from '../../../../ayon/blocks/symbolIds';
2 |
3 | ## Live Preview
4 |
5 | You can double click the block to edit its contents.
6 |
7 |
8 |
9 | ## Customization
10 |
11 | You can type or paste any comma-separated or tab-separated data into the table
12 | block to customize its contents. For example:
13 |
14 | ```
15 | Name, Age, Height
16 | John, 30, 6'0"
17 | Jane, 25, 5'8"
18 | ```
19 |
20 | Results in the following table:
21 |
22 |
29 |
--------------------------------------------------------------------------------
/packages/site/src/pages/docs/blocks/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "order": ["application", "marketing", "elements"]
3 | }
4 |
--------------------------------------------------------------------------------
/packages/site/src/pages/docs/blocks/elements.mdx:
--------------------------------------------------------------------------------
1 | ## Available blocks
2 |
3 | Elements are smaller, more versatile blocks that can be used to build many
4 | different kinds of apps:
5 |
6 |
7 |
--------------------------------------------------------------------------------
/packages/site/src/pages/docs/blocks/elements/avatar.mdx:
--------------------------------------------------------------------------------
1 | import { avatarSymbolId } from '../../../../ayon/blocks/symbolIds';
2 |
3 | ## Live Preview
4 |
5 | You can double click the block to edit its contents.
6 |
7 |
8 |
9 | ## Configuration
10 |
11 | ### Monogram
12 |
13 | Type any letter into the avatar block to display a monogram instead of an image.
14 |
15 |
16 |
17 | ### Image
18 |
19 | You can also use any url:
20 |
21 |
25 |
--------------------------------------------------------------------------------
/packages/site/src/pages/docs/blocks/elements/box.mdx:
--------------------------------------------------------------------------------
1 | import { boxSymbolId } from '../../../../ayon/blocks/symbolIds';
2 |
3 | ## Live Preview
4 |
5 | You can double click the block to edit its contents.
6 |
7 |
8 |
9 | ## Configuration
10 |
11 | The box is an extremely configurable block. It can be used to create a wide
12 | variety of layouts and styles using the many available `#` commands.
13 |
14 |
18 |
--------------------------------------------------------------------------------
/packages/site/src/pages/docs/blocks/elements/button.mdx:
--------------------------------------------------------------------------------
1 | import { buttonSymbolId } from '../../../../ayon/blocks/symbolIds';
2 |
3 | ## Live Preview
4 |
5 | You can double click the block to edit its contents.
6 |
7 |
8 |
--------------------------------------------------------------------------------
/packages/site/src/pages/docs/blocks/elements/checkbox.mdx:
--------------------------------------------------------------------------------
1 | import { checkboxSymbolId } from '../../../../ayon/blocks/symbolIds';
2 |
3 | ## Live Preview
4 |
5 | You can double click the block to edit its contents.
6 |
7 |
8 |
9 | ## Configuration
10 |
11 | You can configure the checkbox with text, which will appear as a label to the
12 | right. If you do not want a label, resize the block to a square that fits only
13 | the checkbox. The checkbox has a few different states that can be configured by
14 | adding:
15 |
16 | - `#off` - to uncheck the checkbox (default)
17 | - `#on` - to check the checkbox
18 | - `#disabled` - to disable the checkbox
19 |
20 | Here's an example with a text label, `#on` and `#disabled`:
21 |
22 |
26 |
--------------------------------------------------------------------------------
/packages/site/src/pages/docs/blocks/elements/heading.mdx:
--------------------------------------------------------------------------------
1 | import {
2 | heading1SymbolId,
3 | heading3SymbolId,
4 | } from '../../../../ayon/blocks/symbolIds';
5 |
6 | ## Live Preview
7 |
8 | You can double click the block to edit its contents.
9 |
10 |
11 |
12 | ## Configuration
13 |
14 | There are 6 different pre-defined heading styles, each represented by a
15 | different block type. The block types are:
16 |
17 | - `Heading 1`
18 | - `Heading 2`
19 | - `Heading 3`
20 | - `Heading 4`
21 | - `Heading 5`
22 | - `Heading 6`
23 |
24 | The heading block accepts many `#` commands for styling text. For example,
25 | here's a `Heading 3` block with the styles `#text-red-600 #center`:
26 |
27 |
31 |
32 | ## Limitations
33 |
34 | There is currently no way to change the font size or font family.
35 |
--------------------------------------------------------------------------------
/packages/site/src/pages/docs/blocks/elements/input.mdx:
--------------------------------------------------------------------------------
1 | import { inputSymbolId } from '../../../../ayon/blocks/symbolIds';
2 |
3 | ## Live Preview
4 |
5 | You can double click the block to edit its contents.
6 |
7 |
8 |
9 | ## Configuration
10 |
11 | The input can be prefilled with text.
12 |
13 | You can further configure the input's style by adding:
14 |
15 | - `#dark` - to use the dark theme
16 | - `#accent` - to use the accent color variant of the theme
17 | - `#disabled` - to disable the select
18 |
19 | Here's an example configuration that uses these options (double click to edit):
20 |
21 |
25 |
--------------------------------------------------------------------------------
/packages/site/src/pages/docs/blocks/elements/link.mdx:
--------------------------------------------------------------------------------
1 | import { linkSymbolId } from '../../../../ayon/blocks/symbolIds';
2 |
3 | ## Live Preview
4 |
5 | You can double click the block to edit its contents.
6 |
7 |
8 |
9 | ## Configuration
10 |
11 | The text block accepts many `#` commands for styling text, such as
12 | `#text-red-600 #font-bold`. You can also use the `#icon-arrow-forward` command
13 | to add an arrow icon:
14 |
15 |
19 |
--------------------------------------------------------------------------------
/packages/site/src/pages/docs/blocks/elements/radio.mdx:
--------------------------------------------------------------------------------
1 | import { radioSymbolId } from '../../../../ayon/blocks/symbolIds';
2 |
3 | ## Live Preview
4 |
5 | You can double click the block to edit its contents.
6 |
7 |
8 |
9 | ## Configuration
10 |
11 | You can configure the radio with text, which will appear as a label to the
12 | right. If you do not want a label, resize the block to a square that fits only
13 | the radio. The radio has a few different states that can be configured by
14 | adding:
15 |
16 | - `#off` - to deselect the radio (default)
17 | - `#on` - to select the radio
18 | - `#disabled` - to disable the radio
19 |
20 | Here's an example with a text label, `#on` and `#disabled`:
21 |
22 |
26 |
--------------------------------------------------------------------------------
/packages/site/src/pages/docs/blocks/elements/select.mdx:
--------------------------------------------------------------------------------
1 | import { selectSymbolId } from '../../../../ayon/blocks/symbolIds';
2 |
3 | ## Live Preview
4 |
5 | You can double click the block to edit its contents.
6 |
7 |
8 |
9 | ## Configuration
10 |
11 | Each item in the select should be on a separate line.
12 |
13 | You can further configure the select's style by adding:
14 |
15 | - `#dark` - to use the dark theme
16 | - `#accent` - to use the accent color variant of the theme
17 | - `#disabled` - to disable the select
18 |
19 | Here's an example configuration that uses these options (double click to edit):
20 |
21 |
29 |
--------------------------------------------------------------------------------
/packages/site/src/pages/docs/blocks/elements/switch.mdx:
--------------------------------------------------------------------------------
1 | import { switchSymbolId } from '../../../../ayon/blocks/symbolIds';
2 |
3 | ## Live Preview
4 |
5 | You can double click the block to edit its contents.
6 |
7 |
8 |
9 | ## Configuration
10 |
11 | The switch has a few different states that can be configured by adding:
12 |
13 | - `#off` - to switch to the off position (default)
14 | - `#on` - to switch to the on position
15 | - `#disabled` - to disable the switch
16 |
17 | Here's an example with `#on` and `#disabled`:
18 |
19 |
20 |
--------------------------------------------------------------------------------
/packages/site/src/pages/docs/blocks/elements/text.mdx:
--------------------------------------------------------------------------------
1 | import { textSymbolId } from '../../../../ayon/blocks/symbolIds';
2 |
3 | ## Live Preview
4 |
5 | You can double click the block to edit its contents.
6 |
7 |
8 |
9 | ## Configuration
10 |
11 | The text block accepts many `#` commands for styling text, such as
12 | `#text-red-600 #font-bold`:
13 |
14 |
18 |
19 | ## Limitations
20 |
21 | There is currently no way to change the font size or font family.
22 |
--------------------------------------------------------------------------------
/packages/site/src/pages/docs/blocks/elements/textarea.mdx:
--------------------------------------------------------------------------------
1 | import { textareaSymbolId } from '../../../../ayon/blocks/symbolIds';
2 |
3 | ## Live Preview
4 |
5 | You can double click the block to edit its contents.
6 |
7 |
8 |
9 | ## Configuration
10 |
11 | The textarea can be prefilled with text.
12 |
13 | You can further configure the textarea's style by adding:
14 |
15 | - `#dark` - to use the dark theme
16 | - `#accent` - to use the accent color variant of the theme
17 | - `#disabled` - to disable the select
18 |
19 | Here's an example configuration that uses these options (double click to edit):
20 |
21 |
26 |
--------------------------------------------------------------------------------
/packages/site/src/pages/docs/blocks/marketing.mdx:
--------------------------------------------------------------------------------
1 | ## Available blocks
2 |
3 | Here are some of the top blocks you might find useful when creating marketing
4 | pages:
5 |
6 |
7 |
--------------------------------------------------------------------------------
/packages/site/src/pages/docs/blocks/marketing/card.mdx:
--------------------------------------------------------------------------------
1 | import { cardSymbolId } from '../../../../ayon/blocks/symbolIds';
2 |
3 | ## Live Preview
4 |
5 | You can double click the block to edit its contents.
6 |
7 |
8 |
--------------------------------------------------------------------------------
/packages/site/src/pages/docs/blocks/marketing/hero.mdx:
--------------------------------------------------------------------------------
1 | import { heroSymbolV2Id } from '../../../../ayon/blocks/symbolIds';
2 |
3 | ## Live Preview
4 |
5 | You can double click the block to edit its contents.
6 |
7 |
8 |
--------------------------------------------------------------------------------
/packages/site/src/pages/docs/blocks/marketing/tile-card.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Tile Card
3 | ---
4 |
5 | import { tileCardSymbolId } from '../../../../ayon/blocks/symbolIds';
6 |
7 | ## Live Preview
8 |
9 | You can double click the block to edit its contents.
10 |
11 |
12 |
--------------------------------------------------------------------------------
/packages/site/src/pages/docs/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "order": ["index", "overview", "blocks"]
3 | }
4 |
--------------------------------------------------------------------------------
/packages/site/src/pages/docs/overview.mdx:
--------------------------------------------------------------------------------
1 | ## A better way to wireframe
2 |
3 | Noya is a wireframing tool based around the concept of **blocks**. A **block**
4 | is a wireframe-like representation of a UI component such as "Hero" or "Button".
5 | The Noya tool automatically generates a high-fidelity output in real time by
6 | mapping low-fidelity blocks into high-fidelity design system and React
7 | components.
8 |
9 | ## Why?
10 |
11 | Traditionally, there’s a tricky tradeoff between working in low-fidelity
12 | wireframes vs. high-fidelity mockups when working on a product idea. Working in
13 | low-fidelity allows for quick iteration while hammering out the big picture, but
14 | people often have a hard time imagining the final product.
15 |
16 | Because of this, teams frequently jump to high-fidelity and stop using
17 | wireframes too early, which slows down progress dramatically as they spend an
18 | excessive amount of time perfecting unimportant details.
19 |
--------------------------------------------------------------------------------
/packages/site/src/pages/docs/overview/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "order": [
3 | "projects",
4 | "design-systems",
5 | "creating-blocks",
6 | "styling-blocks",
7 | "region-tool",
8 | "ai-generation",
9 | "export",
10 | "sharing",
11 | "views"
12 | ]
13 | }
14 |
--------------------------------------------------------------------------------
/packages/site/src/pages/docs/overview/design-systems.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Design Systems
3 | ---
4 |
5 | ## Choosing a base design system
6 |
7 | Noya currently supports a single base design system,
8 | [Chakra UI](https://chakra-ui.com/). We chose Chakra for its simplicity and ease
9 | of use in React code. We plan to add first-class support for more open source
10 | design systems in the future, including [MUI](https://mui.com/) and
11 | [Ant Design](https://ant.design/).
12 |
13 | ## Importing your company's design system
14 |
15 | Importing a custom design system isn't supported yet. If you're interested in
16 | being an early adopter of this feature, please
17 | [reach out](https://noyasoftware.notion.site/Noya-Contact-9a95e0895eba4f578517dfdc4d94ccdd)!
18 |
--------------------------------------------------------------------------------
/packages/site/src/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | // This isn't actually used, since this tsconfig is in the "src" dir,
5 | // but this prevents VSCode "organize imports" from deleting the React import.
6 | // Next automatically sets the "jsx" option to "preserve" when it builds,
7 | // so we can't change it in the parent directory.
8 | "jsx": "react"
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/packages/site/src/utils/cookies.ts:
--------------------------------------------------------------------------------
1 | import cookie from 'cookie';
2 |
3 | export function addShareCookie(id: string) {
4 | if (typeof document === 'undefined') return;
5 |
6 | const map = cookie.parse(document.cookie);
7 | const updatedShares = encodeListCookie(map.noya_shares, id);
8 |
9 | document.cookie = cookie.serialize('noya_shares', updatedShares, {
10 | path: '/',
11 | });
12 | }
13 |
14 | function encodeListCookie(list: string | undefined, value: string) {
15 | const shares = (list ?? '').split(',').filter(Boolean);
16 | const sharesSet = new Set(shares);
17 | sharesSet.add(value);
18 |
19 | return [...sharesSet].join(',');
20 | }
21 |
--------------------------------------------------------------------------------
/packages/site/src/utils/download.ts:
--------------------------------------------------------------------------------
1 | export function downloadBlob(blob: Blob, name: string): void;
2 | export function downloadBlob(blob: File): void;
3 | export function downloadBlob(
4 | ...params: [blob: Blob, name: string] | [blob: File]
5 | ): void {
6 | const exportUrl = URL.createObjectURL(params[0]);
7 | const name = params.length === 1 ? params[0].name : params[1];
8 |
9 | const a = document.createElement('a');
10 | a.href = exportUrl;
11 | a.download = name;
12 | a.style.display = 'none';
13 | document.body.appendChild(a);
14 | a.click();
15 |
16 | document.body.removeChild(a);
17 | URL.revokeObjectURL(exportUrl);
18 | }
19 |
20 | export async function downloadUrl(url: string, name: string) {
21 | const response = await fetch(url, { credentials: 'include' });
22 | const blob = await response.blob();
23 |
24 | downloadBlob(blob, name);
25 | }
26 |
--------------------------------------------------------------------------------
/packages/site/src/utils/measureImage.ts:
--------------------------------------------------------------------------------
1 | export async function measureImage(data: ArrayBuffer) {
2 | const image = new Image();
3 | image.src = URL.createObjectURL(new Blob([data]));
4 |
5 | await new Promise((resolve) => {
6 | image.onload = resolve;
7 | });
8 |
9 | return {
10 | width() {
11 | return image.width;
12 | },
13 | height() {
14 | return image.height;
15 | },
16 | };
17 | }
18 |
--------------------------------------------------------------------------------
/packages/site/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "module": "esnext",
5 | "incremental": true,
6 | "jsx": "preserve"
7 | },
8 | "include": [
9 | "next-env.d.ts",
10 | "guidebook.d.ts",
11 | "searchIndex.d.ts",
12 | "**/*.ts",
13 | "**/*.tsx"
14 | ],
15 | "exclude": ["node_modules"]
16 | }
17 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "modular-scripts/tsconfig.json",
3 | "include": [
4 | "modular",
5 | "packages/**/src",
6 | "packages/**/next-env.d.ts",
7 | "packages/**/*.config.ts"
8 | ],
9 | "compilerOptions": {
10 | "paths": {
11 | "*": ["./packages/*/src"]
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------