├── .github └── workflows │ ├── build.yml │ └── release.yml ├── .gitignore ├── .prettierrc ├── AUTHORS.TXT ├── CONTRIBUTORS.txt ├── LICENSE.md ├── README.md ├── animations.scss ├── app ├── head.tsx ├── layout.tsx ├── manifest.ts ├── page.tsx ├── robots.ts └── sitemap.ts ├── common ├── constants.ts ├── queries.ts ├── server.ts └── utilities.ts ├── components ├── AnyTextHeader.module.scss ├── AnyTextHeader.tsx ├── DefaultLayout.module.scss ├── DefaultLayout.tsx ├── DefaultMetaTags.tsx ├── ListItem.module.scss ├── ListItem.tsx ├── MarketingServerMono.module.scss ├── MarketingServerMono.tsx ├── Page.tsx └── Providers.tsx ├── fonts ├── ServerMono-Regular-Italic.otf ├── ServerMono-Regular-Italic.woff ├── ServerMono-Regular-Italic.woff2 ├── ServerMono-Regular.glyphs ├── ServerMono-Regular.otf ├── ServerMono-Regular.woff └── ServerMono-Regular.woff2 ├── global.scss ├── modules ├── cors.ts ├── object-assign.ts └── vary.ts ├── next-env.d.ts ├── package.json ├── public ├── apple-touch-icon-precomposed.png ├── apple-touch-icon.png ├── favicon-16x16.png ├── favicon-32x32.png ├── favicon.ico └── template-app-icon.png ├── system ├── Button.module.scss ├── Button.tsx ├── Checkbox.module.scss ├── Checkbox.tsx ├── FlippableTiltCard.module.scss ├── FlippableTiltCard.tsx ├── Footer.module.scss ├── Footer.tsx ├── FormUpload.module.scss ├── FormUpload.tsx ├── ImageBlock.module.scss ├── ImageBlock.tsx ├── Input.module.scss ├── Input.tsx ├── IsometricProductBox.module.scss ├── IsometricProductBox.tsx ├── IsometricRect.module.scss ├── IsometricRect.tsx ├── KeyHeader.module.scss ├── KeyHeader.tsx ├── Loader.module.scss ├── Loader.tsx ├── MonospacePreview.module.scss ├── MonospacePreview.tsx ├── Navigation.module.scss ├── Navigation.tsx ├── ResizableMonospaceWindow.module.scss ├── ResizableMonospaceWindow.tsx ├── Select.module.scss ├── Select.tsx ├── Table.module.scss ├── Table.tsx ├── TextArea.module.scss ├── TextArea.tsx ├── ToolbarControlsFixed.module.scss ├── ToolbarControlsFixed.tsx ├── Video.module.scss ├── Video.tsx ├── animations │ ├── TextSwapper.module.scss │ └── TextSwapper.tsx ├── detectors │ └── OutsideElementEvent.tsx ├── diagrams │ ├── ArrowLine.module.scss │ └── ArrowLine.tsx ├── documents │ ├── ActionItem.module.scss │ ├── ActionItem.tsx │ ├── CheckmarkItem.module.scss │ ├── CheckmarkItem.tsx │ ├── SignatureBox.module.scss │ ├── SignatureBox.tsx │ ├── SmallButton.module.scss │ ├── SmallButton.tsx │ ├── Tag.module.scss │ ├── Tag.tsx │ └── index.module.scss ├── forms │ ├── FormChangePassword.module.scss │ ├── FormChangePassword.tsx │ ├── FormSettingsPrivacy.module.scss │ └── FormSettingsPrivacy.tsx ├── graphs │ ├── AreaChart.tsx │ ├── BarGraphWithLines.tsx │ ├── BubbleChart.tsx │ ├── CandlestickChart.tsx │ ├── ChartLegend.module.scss │ ├── ChartLegend.tsx │ ├── ChartPlaceholder.module.scss │ ├── ChartPlaceholder.tsx │ ├── CohortChart.tsx │ ├── ColumnChart.tsx │ ├── DistributionChart.tsx │ ├── DivergingStackedBarChart.tsx │ ├── DonutChart.tsx │ ├── DotPlot.tsx │ ├── GroupedBubblesChart.tsx │ ├── HistogramChart.tsx │ ├── HorizontalBarChart.tsx │ ├── LineChart.tsx │ ├── RadarChart.tsx │ └── TreeChart.tsx ├── layouts │ ├── AppLayout.module.scss │ ├── AppLayout.tsx │ ├── CardHandLayout.module.scss │ ├── CardHandLayout.tsx │ ├── Content.module.scss │ ├── Content.tsx │ ├── DashboardWithSidebarLayout.module.scss │ ├── DashboardWithSidebarLayout.tsx │ ├── GridLayout.module.scss │ ├── GridLayout.tsx │ ├── InvoiceLayout.module.scss │ ├── InvoiceLayout.tsx │ ├── IsometricGridLayout.module.scss │ ├── IsometricGridLayout.tsx │ ├── ThinAppLayout.module.scss │ ├── ThinAppLayout.tsx │ ├── ThinAppLayoutHeader.module.scss │ ├── ThinAppLayoutHeader.tsx │ ├── ThreeColumnAppLayout.module.scss │ ├── ThreeColumnAppLayout.tsx │ ├── TwoColumnLayout.module.scss │ ├── TwoColumnLayout.tsx │ ├── TwoColumnLayoutFull.module.scss │ ├── TwoColumnLayoutFull.tsx │ ├── TwoColumnLayoutSidebar.module.scss │ ├── TwoColumnLayoutSidebar.tsx │ ├── WideAppLayout.module.scss │ └── WideAppLayout.tsx ├── modals │ └── GlobalModalManager.tsx ├── providers │ └── ModalContextProvider.tsx ├── scroll │ ├── ScrollCarouselHorizontal.module.scss │ ├── ScrollCarouselHorizontal.tsx │ ├── ScrollVertical.module.scss │ └── ScrollVertical.tsx ├── sections │ ├── SectionFullHeight.module.scss │ ├── SectionFullHeight.tsx │ ├── SectionHalfHeight.module.scss │ ├── SectionHalfHeight.tsx │ ├── SectionHorizontalStack.module.scss │ └── SectionHorizontalStack.tsx ├── svg │ ├── Caret.tsx │ ├── Checkmark.tsx │ ├── Cross.tsx │ ├── Edit.tsx │ ├── Error.tsx │ ├── IntDev.tsx │ ├── Map.tsx │ └── social │ │ ├── Bluesky.tsx │ │ ├── Google.tsx │ │ ├── Twitter.tsx │ │ └── X.tsx └── typography │ ├── FormTypography.module.scss │ ├── Typography.module.scss │ ├── forms.tsx │ └── index.tsx └── tsconfig.json /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | 6 | jobs: 7 | test: 8 | runs-on: ubuntu-latest 9 | permissions: 10 | contents: write 11 | pull-requests: write 12 | repository-projects: write 13 | steps: 14 | - name: Checkout repository 15 | uses: actions/checkout@v3 16 | 17 | - name: Install Node.js 18 | uses: actions/setup-node@v3 19 | with: 20 | node-version: 18 21 | 22 | - name: Install dependencies 23 | run: npm install 24 | env: 25 | CI: true 26 | 27 | - name: Test build app 28 | run: npm run build 29 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Create a new GitHub release with the files 2 | 3 | on: 4 | push: 5 | tags: ['*'] 6 | workflow_dispatch: 7 | 8 | jobs: 9 | release: 10 | runs-on: ubuntu-latest 11 | permissions: 12 | contents: write 13 | pull-requests: write 14 | repository-projects: write 15 | steps: 16 | - name: Checkout repository 17 | uses: actions/checkout@v4 18 | - name: Create a ZIP archive from the fonts directory 19 | run: zip -r fonts.zip fonts 20 | - name: Create new release and upload fonts.zip 21 | uses: ncipollo/release-action@v1 22 | with: 23 | artifacts: 'fonts.zip' 24 | generateReleaseNotes: true 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .next 2 | .nova 3 | .env 4 | .env.local 5 | .env-custom-development 6 | .env-development 7 | .env-textile 8 | .env-production 9 | .DS_STORE 10 | DS_STORE 11 | yarn.lock 12 | node_modules 13 | dist 14 | analytics.txt 15 | package-lock.json 16 | 17 | /**/*/.DS_STORE 18 | /**/*/node_modules 19 | /**/*/.next 20 | /**/*/.data -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 180, 3 | "tabWidth": 2, 4 | "useTabs": false, 5 | "semi": true, 6 | "singleQuote": true, 7 | "trailingComma": "es5", 8 | "bracketSpacing": true, 9 | "jsxBracketSameLine": false, 10 | "arrowParens": "always", 11 | "requirePragma": false, 12 | "insertPragma": false, 13 | "proseWrap": "preserve", 14 | "parser": "babel", 15 | "overrides": [ 16 | { 17 | "files": "*.js", 18 | "options": { 19 | "parser": "babel" 20 | } 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /AUTHORS.TXT: -------------------------------------------------------------------------------- 1 | # This is the official list of project authors for copyright purposes. 2 | # This file is distinct from the CONTRIBUTORS.txt file. 3 | # See the latter for an explanation. 4 | # 5 | # Names should be added to this file as: 6 | # Name or Organization 7 | 8 | Internet Development Studio Company <> 9 | Matthieu Salvaggio 10 | Tim Vanhille <> -------------------------------------------------------------------------------- /CONTRIBUTORS.txt: -------------------------------------------------------------------------------- 1 | # This is the list of people who have contributed to this project, 2 | # and includes those not listed in AUTHORS.txt because they are not 3 | # copyright authors. For example, company employees may be listed 4 | # here because their company holds the copyright and is listed there. 5 | # 6 | # When adding J Random Contributor's name to this file, either J's 7 | # name or J's organization's name should be added to AUTHORS.txt 8 | # 9 | # Names should be added to this file as: 10 | # Name 11 | 12 | Matthieu Salvaggio 13 | Tim Vanhille <> 14 | Jimmy Lee 15 | Andrew Alimbuyuguen 16 | Adrien Chuttarsing <> -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2024 Internet Development Studio Company (https://internet.dev) 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | This license is copied below, and is also available with a FAQ at: 5 | https://scripts.sil.org/OFL 6 | 7 | --- 8 | 9 | ## SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | 11 | PREAMBLE 12 | The goals of the Open Font License (OFL) are to stimulate worldwide 13 | development of collaborative font projects, to support the font creation 14 | efforts of academic and linguistic communities, and to provide a free and 15 | open framework in which fonts may be shared and improved in partnership 16 | with others. 17 | 18 | The OFL allows the licensed fonts to be used, studied, modified and 19 | redistributed freely as long as they are not sold by themselves. The 20 | fonts, including any derivative works, can be bundled, embedded, 21 | redistributed and/or sold with any software provided that any reserved 22 | names are not used by derivative works. The fonts and derivatives, 23 | however, cannot be released under any other type of license. The 24 | requirement for fonts to remain under this license does not apply 25 | to any document created using the fonts or their derivatives. 26 | 27 | DEFINITIONS 28 | "Font Software" refers to the set of files released by the Copyright 29 | Holder(s) under this license and clearly marked as such. This may 30 | include source files, build scripts and documentation. 31 | 32 | "Reserved Font Name" refers to any names specified as such after the 33 | copyright statement(s). 34 | 35 | "Original Version" refers to the collection of Font Software components as 36 | distributed by the Copyright Holder(s). 37 | 38 | "Modified Version" refers to any derivative made by adding to, deleting, 39 | or substituting -- in part or in whole -- any of the components of the 40 | Original Version, by changing formats or by porting the Font Software to a 41 | new environment. 42 | 43 | "Author" refers to any designer, engineer, programmer, technical 44 | writer or other person who contributed to the Font Software. 45 | 46 | PERMISSION & CONDITIONS 47 | Permission is hereby granted, free of charge, to any person obtaining 48 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 49 | redistribute, and sell modified and unmodified copies of the Font 50 | Software, subject to the following conditions: 51 | 52 | 1. Neither the Font Software nor any of its individual components, 53 | in Original or Modified Versions, may be sold by itself. 54 | 55 | 2. Original or Modified Versions of the Font Software may be bundled, 56 | redistributed and/or sold with any software, provided that each copy 57 | contains the above copyright notice and this license. These can be 58 | included either as stand-alone text files, human-readable headers or 59 | in the appropriate machine-readable metadata fields within text or 60 | binary files as long as those fields can be easily viewed by the user. 61 | 62 | 3. No Modified Version of the Font Software may use the Reserved Font 63 | Name(s) unless explicit written permission is granted by the corresponding 64 | Copyright Holder. This restriction only applies to the primary font name as 65 | presented to the users. 66 | 67 | 4. The name(s) of the Copyright Holder(s) or the Author(s) of the Font 68 | Software shall not be used to promote, endorse or advertise any 69 | Modified Version, except to acknowledge the contribution(s) of the 70 | Copyright Holder(s) and the Author(s) or with their explicit written 71 | permission. 72 | 73 | 5. The Font Software, modified or unmodified, in part or in whole, 74 | must be distributed entirely under this license, and must not be 75 | distributed under any other license. The requirement for fonts to 76 | remain under this license does not apply to any document created 77 | using the Font Software. 78 | 79 | TERMINATION 80 | This license becomes null and void if any of the above conditions are 81 | not met. 82 | 83 | DISCLAIMER 84 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 85 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 86 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 87 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 88 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 89 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 90 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 91 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 92 | OTHER DEALINGS IN THE FONT SOFTWARE. 93 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SERVER MONO 2 | 3 | This repository contains the [source files](https://github.com/internet-development/www-server-mono/tree/main/fonts) for Server Mono and the source code for our website, which is built using our [next-sass-starter](https://github.com/internet-development/nextjs-sass-starter) template system used across all our websites. 4 | 5 | Server Mono is a typeface inspired by typewriters, Apple's San Francisco Mono, ASCII art, command-line interfaces, and programming tools. 6 | 7 | Server Mono continues the long tradition of monospace fonts, renowned for their versatility in command-line interfaces due to their clear readability and uniform character width. You'll notice our own preferences reflected in the design, as we value how it performs across various viewing contexts. Server Mono offers excellent readability and pairs well with its uniform, predictable, and orderly appearance. 8 | 9 | This single-weight font was released in 2024 by the Internet Development Studio Company of Seattle, Washington. Created by designers Tim Vanhille and Matthieu Salvaggio, with supplemental direction from Jimmy Lee and the Internet Development Studio Company community. 10 | 11 | ### Setup Website (MacOS) 12 | 13 | Start by cloning the repository 14 | 15 | Then run the server 16 | 17 | ```sh 18 | npm install 19 | npm run dev 20 | ``` 21 | 22 | Go to `http://localhost:10000` in your browser of choice. 23 | 24 | ### Contact 25 | 26 | - [Internet Development Studio Company](https://internet.dev) 27 | - [X for BlazeType](https://x.com/BlazeFoundry) 28 | - [X for INTDEV](https://x.com/internetxstudio) 29 | 30 | ### Thanks 31 | 32 | - Tim Vanhille 33 | - Matthieu Salvaggio 34 | - Andrew Alimbuyuguen 35 | - Jimmy Lee 36 | - Whyrusleeping 37 | - Philip Bedford -------------------------------------------------------------------------------- /animations.scss: -------------------------------------------------------------------------------- 1 | @keyframes blur { 2 | 0% { 3 | filter: blur(4px); 4 | opacity: 0.5; 5 | } 6 | 25% { 7 | filter: blur(0px); 8 | opacity: 1; 9 | } 10 | 75% { 11 | filter: blur(0px); 12 | opacity: 1; 13 | } 14 | 100% { 15 | filter: blur(4px); 16 | opacity: 0.5; 17 | } 18 | } 19 | 20 | @keyframes fade { 21 | 0% { 22 | opacity: 0; 23 | } 24 | 25% { 25 | opacity: 1; 26 | } 27 | 75% { 28 | opacity: 1; 29 | } 30 | 100% { 31 | opacity: 0; 32 | animation-timing-function: ease-out; 33 | } 34 | } 35 | 36 | @keyframes slideDown { 37 | 0% { 38 | opacity: 0; 39 | transform: translateY(-75%); 40 | } 41 | 25% { 42 | opacity: 1; 43 | transform: translateY(0%); 44 | } 45 | 75% { 46 | opacity: 1; 47 | transform: translateY(0%); 48 | } 49 | 100% { 50 | opacity: 0; 51 | transform: translateY(75%); 52 | } 53 | } 54 | 55 | @keyframes slideUp { 56 | 0% { 57 | opacity: 0; 58 | transform: translateY(75%); 59 | } 60 | 25% { 61 | opacity: 1; 62 | transform: translateY(0%); 63 | } 64 | 75% { 65 | opacity: 1; 66 | transform: translateY(0%); 67 | } 68 | 100% { 69 | opacity: 0; 70 | transform: translateY(-75%); 71 | } 72 | } 73 | 74 | @keyframes slideRight { 75 | 0% { 76 | opacity: 0; 77 | transform: translateX(-75%); 78 | } 79 | 25% { 80 | opacity: 1; 81 | transform: translateX(0%); 82 | } 83 | 75% { 84 | opacity: 1; 85 | transform: translateX(0%); 86 | } 87 | 100% { 88 | opacity: 0; 89 | transform: translateX(75%); 90 | } 91 | } 92 | 93 | @keyframes slideLeft { 94 | 0% { 95 | opacity: 0; 96 | transform: translateX(75%); 97 | } 98 | 25% { 99 | opacity: 1; 100 | transform: translateX(0%); 101 | } 102 | 75% { 103 | opacity: 1; 104 | transform: translateX(0%); 105 | } 106 | 100% { 107 | opacity: 0; 108 | transform: translateX(-75%); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /app/head.tsx: -------------------------------------------------------------------------------- 1 | import DefaultMetaTags from '@components/DefaultMetaTags'; 2 | 3 | export default async function Head({ params }) { 4 | return ( 5 | <> 6 | 7 | 8 | ); 9 | } 10 | -------------------------------------------------------------------------------- /app/layout.tsx: -------------------------------------------------------------------------------- 1 | import Providers from '@components/Providers'; 2 | 3 | export default function RootLayout({ children }: { children: React.ReactNode }) { 4 | return ( 5 | 6 | 7 | {children} 8 | 9 | 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /app/manifest.ts: -------------------------------------------------------------------------------- 1 | import Package from '@root/package.json'; 2 | 3 | import { MetadataRoute } from 'next'; 4 | 5 | export default function manifest(): MetadataRoute.Manifest { 6 | return { 7 | name: Package.name, 8 | short_name: Package.name, 9 | description: Package.description, 10 | start_url: '/', 11 | display: 'standalone', 12 | background_color: '#000000', 13 | theme_color: '#000000', 14 | icons: [ 15 | { 16 | src: '/favicon.ico', 17 | sizes: 'any', 18 | type: 'image/x-icon', 19 | }, 20 | ], 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /app/page.tsx: -------------------------------------------------------------------------------- 1 | import '@root/global.scss'; 2 | import '@root/animations.scss'; 3 | 4 | import * as Constants from '@common/constants'; 5 | import * as Utilities from '@common/utilities'; 6 | 7 | import DefaultLayout from '@components/DefaultLayout'; 8 | import IntDev from '@system/svg/IntDev'; 9 | import Package from '@root/package.json'; 10 | import Script from 'next/script'; 11 | import MarketingServerMono from '@components/MarketingServerMono'; 12 | 13 | export async function generateMetadata({ params, searchParams }) { 14 | const title = 'Server Mono'; 15 | const description = Package.description; 16 | const url = 'https://servermono.com'; 17 | const handle = '@internetxstudio'; 18 | 19 | return { 20 | metadataBase: new URL('https://servermono.com'), 21 | title, 22 | description, 23 | url, 24 | openGraph: { 25 | title, 26 | description, 27 | url, 28 | type: 'website', 29 | images: [ 30 | { 31 | url: 'https://intdev-global.s3.us-west-2.amazonaws.com/public/internet-dev/e5957545-e79c-44d7-a7b0-e82b8edbc314.png', 32 | width: 1200, 33 | height: 628, 34 | }, 35 | ], 36 | }, 37 | twitter: { 38 | title, 39 | description, 40 | url, 41 | handle, 42 | card: 'summary_large_image', 43 | images: ['https://intdev-global.s3.us-west-2.amazonaws.com/public/internet-dev/e5957545-e79c-44d7-a7b0-e82b8edbc314.png'], 44 | }, 45 | icons: { 46 | icon: '/favicon-32x32.png', 47 | shortcut: '/favicon-16x16.png', 48 | apple: [{ url: '/apple-touch-icon.png' }, { url: '/apple-touch-icon.png', sizes: '180x180', type: 'image/png' }], 49 | other: [ 50 | { 51 | rel: 'apple-touch-icon-precomposed', 52 | url: '/apple-touch-icon-precomposed.png', 53 | }, 54 | ], 55 | }, 56 | }; 57 | } 58 | 59 | export default async function Page(props) { 60 | return ( 61 | 62 | 63 | 64 | ); 65 | } 66 | -------------------------------------------------------------------------------- /app/robots.ts: -------------------------------------------------------------------------------- 1 | import { MetadataRoute } from 'next'; 2 | 3 | export default function robots(): MetadataRoute.Robots { 4 | return { 5 | rules: { 6 | userAgent: '*', 7 | allow: '/', 8 | disallow: '/api/', 9 | }, 10 | sitemap: 'https://wireframes.internet.dev/sitemap.xml', 11 | }; 12 | } 13 | -------------------------------------------------------------------------------- /app/sitemap.ts: -------------------------------------------------------------------------------- 1 | import { MetadataRoute } from 'next'; 2 | 3 | export default function sitemap(): MetadataRoute.Sitemap { 4 | return [ 5 | { 6 | url: 'https://wireframes.internet.dev', 7 | lastModified: new Date(), 8 | }, 9 | ]; 10 | } 11 | -------------------------------------------------------------------------------- /common/queries.ts: -------------------------------------------------------------------------------- 1 | import * as Constants from '@common/constants'; 2 | import * as Utilities from '@common/utilities'; 3 | 4 | export async function getData({ route, key, body }, qualifier = 'data') { 5 | let result; 6 | try { 7 | const response = await fetch(route, { 8 | method: 'POST', 9 | headers: { 'X-API-KEY': key, 'Content-Type': 'application/json' }, 10 | body: JSON.stringify(body), 11 | }); 12 | result = await response.json(); 13 | } catch (e) { 14 | return null; 15 | } 16 | 17 | if (!result) { 18 | return null; 19 | } 20 | 21 | if (result.error) { 22 | return null; 23 | } 24 | 25 | if (!result[qualifier]) { 26 | return null; 27 | } 28 | 29 | return result; 30 | } 31 | 32 | export async function onUserListData({ key }) { 33 | const route = `${Constants.API}/data`; 34 | const body = {}; 35 | return await getData({ route, key, body }); 36 | } 37 | 38 | export async function onUserDeleteData({ id, key }) { 39 | const route = `${Constants.API}/data/delete`; 40 | const body = { id }; 41 | return await getData({ route, key, body }); 42 | } 43 | 44 | export async function onGetDocumentById({ id }) { 45 | const route = `${Constants.API}/documents/${id}`; 46 | const body = {}; 47 | return await getData({ route, key: null, body }); 48 | } 49 | 50 | export async function onUserCreateDocument({ key, type }) { 51 | const route = `${Constants.API}/documents/create`; 52 | const body = { type }; 53 | return await getData({ route, key, body }); 54 | } 55 | 56 | export async function onDeleteDocumentById({ id, key }) { 57 | const route = `${Constants.API}/documents/delete`; 58 | const body = { id }; 59 | return await getData({ route, key, body }); 60 | } 61 | 62 | export async function onPublicUserAuthenticate({ email, password }) { 63 | const route = `${Constants.API}/users/authenticate`; 64 | const body = { email, password }; 65 | return getData({ route, key: null, body }, 'user'); 66 | } 67 | 68 | export async function onPublicUserForgotPassword({ email }) { 69 | const route = `${Constants.API}/users/reset-password`; 70 | const body = { email }; 71 | return getData({ route, key: null, body }); 72 | } 73 | 74 | export async function onRefreshDocuments({ key, type }) { 75 | const route = `${Constants.API}/documents`; 76 | const body = { type }; 77 | return await getData({ route, key, body }); 78 | } 79 | 80 | export async function onRefreshPosts({ key, type, user_id }) { 81 | const route = `${Constants.API}/posts`; 82 | const body = { type, user_id }; 83 | return await getData({ route, key, body }); 84 | } 85 | 86 | export async function onUpdateDocumentById({ id, key, updates }) { 87 | const route = `${Constants.API}/documents/update`; 88 | const body = { id, updates }; 89 | return await getData({ route, key, body }); 90 | } 91 | 92 | export async function onUserChangePassword({ key, password }) { 93 | const route = `${Constants.API}/users/update-viewer-password`; 94 | const body = { password }; 95 | return getData({ route, key, body }); 96 | } 97 | 98 | export async function onUserCreatePost({ id, key, src, type }) { 99 | const route = `${Constants.API}/posts/create`; 100 | const body = { type, fields: { fileId: id, public: true }, src }; 101 | return getData({ route, key, body }); 102 | } 103 | 104 | export async function onUserCreateThread({ fields, key, src, type }) { 105 | const route = `${Constants.API}/posts/create`; 106 | const body = { fields, src, type }; 107 | return getData({ route, key, body }); 108 | } 109 | 110 | export async function onUserDeletePost({ id, key }) { 111 | const route = `${Constants.API}/posts/delete`; 112 | const body = { id }; 113 | return getData({ route, key, body }); 114 | } 115 | 116 | export async function onUserListThreadReplies({ id, key, orderBy }) { 117 | const route = `${Constants.API}/posts/all-thread-replies`; 118 | const body = { id, orderBy }; 119 | return getData({ route, key, body }); 120 | } 121 | 122 | export async function onUserListThreads({ key, orderBy }) { 123 | const route = `${Constants.API}/posts/all-threads`; 124 | const body = { orderBy }; 125 | return getData({ route, key, body }); 126 | } 127 | 128 | export async function onUserRegenerateAPIKey({ email, password }) { 129 | const route = `${Constants.API}/users/regenerate-key`; 130 | const body = { email, password }; 131 | return getData({ route, key: null, body }, 'user'); 132 | } 133 | 134 | export async function onUserUnsubscribeServices({ key }) { 135 | const route = `${Constants.API}/users/subscriptions/unsubscribe`; 136 | const body = null; 137 | return getData({ route, key, body }, 'user'); 138 | } 139 | 140 | export async function onUserUploadDataGCS({ domain, file, key }) { 141 | let signedResult; 142 | const name = file.name; 143 | const type = file.type; 144 | const size = file.size; 145 | 146 | if (size > Constants.MAX_SIZE_BYTES) { 147 | return { error: 'File size exceeds 15mb limit' }; 148 | } 149 | 150 | try { 151 | const route = `${Constants.API}/data/generate-presigned-url-gcs`; 152 | const body = { domain, type, file: name, size }; 153 | signedResult = await getData({ route, key, body }, 'uploadURL'); 154 | } catch (e) { 155 | return null; 156 | } 157 | 158 | try { 159 | fetch(signedResult.uploadURL, { 160 | method: 'PUT', 161 | headers: { 162 | 'Content-Type': 'application/octet-stream', 163 | }, 164 | body: file, 165 | }); 166 | } catch (e) { 167 | return null; 168 | } 169 | 170 | return signedResult; 171 | } 172 | 173 | export async function onUserUploadDataS3({ domain, file, key }) { 174 | let signedResult; 175 | const name = file.name; 176 | const type = file.type; 177 | const size = file.size; 178 | 179 | if (size > Constants.MAX_SIZE_BYTES) { 180 | return { error: 'File size exceeds 15mb limit' }; 181 | } 182 | 183 | try { 184 | const route = `${Constants.API}/data/generate-presigned-url`; 185 | const body = { domain, type, file: name, size }; 186 | signedResult = await getData({ route, key, body }, 'uploadURL'); 187 | } catch (e) { 188 | return null; 189 | } 190 | 191 | try { 192 | fetch(signedResult.uploadURL, { 193 | method: 'PUT', 194 | body: file, 195 | }); 196 | } catch (e) { 197 | return null; 198 | } 199 | 200 | return signedResult; 201 | } 202 | -------------------------------------------------------------------------------- /common/server.ts: -------------------------------------------------------------------------------- 1 | import * as Utilities from '@common/utilities'; 2 | 3 | export async function setup(context): Promise<{ sessionKey?: any; viewer?: Record | null }> { 4 | let viewer = null; 5 | let sessionKey = context.req.cookies['sitekey'] || ''; 6 | 7 | if (!Utilities.isEmpty(sessionKey)) { 8 | try { 9 | const response = await fetch('https://api.internet.dev/api/users/viewer', { 10 | method: 'PUT', 11 | headers: { 'X-API-KEY': sessionKey, 'Content-Type': 'application/json' }, 12 | }); 13 | const result = await response.json(); 14 | if (result && result.viewer) { 15 | viewer = result.viewer; 16 | } 17 | } catch (e) {} 18 | } 19 | 20 | return { sessionKey, viewer }; 21 | } 22 | 23 | export async function tryKeyWithoutCookie(key): Promise<{ sessionKey?: any; viewer?: Record | null }> { 24 | let viewer = null; 25 | 26 | if (!Utilities.isEmpty(key)) { 27 | try { 28 | const response = await fetch('https://api.internet.dev/api/users/viewer', { 29 | method: 'PUT', 30 | headers: { 'X-API-KEY': key, 'Content-Type': 'application/json' }, 31 | }); 32 | const result = await response.json(); 33 | if (result && result.viewer) { 34 | viewer = result.viewer; 35 | } 36 | } catch (e) {} 37 | } 38 | 39 | return { sessionKey: key, viewer }; 40 | } 41 | -------------------------------------------------------------------------------- /components/AnyTextHeader.module.scss: -------------------------------------------------------------------------------- 1 | .root { 2 | display: flex; 3 | justify-content: space-between; 4 | align-items: center; 5 | box-shadow: 0 1px 0 0 var(--theme-border); 6 | min-height: 48px; 7 | } 8 | 9 | .left { 10 | min-height: 48px; 11 | flex-shrink: 0; 12 | display: inline-flex; 13 | align-items: center; 14 | justify-content: flex-start; 15 | padding: 0 12px 0 12px; 16 | -webkit-appearance: none; 17 | transition: 200ms ease width; 18 | } 19 | 20 | .item { 21 | font-size: var(--type-scale-fixed-tiny); 22 | text-transform: uppercase; 23 | letter-spacing: 0.2px; 24 | font-weight: 600; 25 | padding: 0 12px 0 12px; 26 | cursor: pointer; 27 | user-select: none; 28 | transition: 200ms ease all; 29 | text-decoration: none; 30 | color: var(--theme-text); 31 | 32 | &:visited { 33 | color: var(--theme-text); 34 | } 35 | 36 | &:hover { 37 | opacity: 0.8; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /components/AnyTextHeader.tsx: -------------------------------------------------------------------------------- 1 | import styles from '@components/AnyTextHeader.module.scss'; 2 | 3 | import * as React from 'react'; 4 | 5 | export default function AnyTextHeader(props) { 6 | return ( 7 | 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /components/DefaultLayout.module.scss: -------------------------------------------------------------------------------- 1 | .body { 2 | } 3 | 4 | .pixel { 5 | position: absolute; 6 | top: 0; 7 | left: 0; 8 | width: 1px; 9 | height: 1px; 10 | } 11 | -------------------------------------------------------------------------------- /components/DefaultLayout.tsx: -------------------------------------------------------------------------------- 1 | import styles from '@components/DefaultLayout.module.scss'; 2 | 3 | import * as React from 'react'; 4 | 5 | export default function App(props) { 6 | return ( 7 |
8 | {''} 9 | {props.children} 10 |
11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /components/DefaultMetaTags.tsx: -------------------------------------------------------------------------------- 1 | export default function DefaultMetaTags() { 2 | return ( 3 | <> 4 | 5 | 6 | 7 | 8 | 9 | 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /components/ListItem.module.scss: -------------------------------------------------------------------------------- 1 | .item { 2 | padding: 16px 0 16px 0; 3 | border-bottom: 1px solid var(--theme-border); 4 | list-style-type: none; 5 | display: grid; 6 | grid-template-columns: auto auto 1fr; 7 | 8 | &:first-child { 9 | border-top: 1px solid var(--theme-border); 10 | margin-top: 48px; 11 | } 12 | 13 | @media (max-width: 480px) { 14 | grid-template-columns: 72px 96px 1fr; 15 | } 16 | } 17 | 18 | .column { 19 | padding-right: 24px; 20 | } 21 | 22 | .link { 23 | text-decoration: none; 24 | color: var(--theme-text); 25 | 26 | &:visited { 27 | color: var(--theme-text); 28 | } 29 | 30 | &:hover { 31 | color: var(--theme-primary); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /components/ListItem.tsx: -------------------------------------------------------------------------------- 1 | import styles from '@components/ListItem.module.scss'; 2 | 3 | import * as React from 'react'; 4 | 5 | export default function ListItem(props) { 6 | return ( 7 |
  • 8 | 9 | WIP 10 | 11 | {props.index} 12 | 13 | 14 | {props.children} 15 | 16 | 17 |
  • 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /components/MarketingServerMono.module.scss: -------------------------------------------------------------------------------- 1 | .root { 2 | max-width: 768px; 3 | width: 100%; 4 | margin: 128px auto 0 auto; 5 | } 6 | 7 | .row { 8 | display: flex; 9 | align-items: flex-start; 10 | justify-content: space-between; 11 | 12 | &:last-child { 13 | border-bottom: 0px; 14 | } 15 | 16 | @media (max-width: 768px) { 17 | display: block; 18 | } 19 | } 20 | 21 | .left { 22 | padding: 64px 24px 64px 24px; 23 | width: 50%; 24 | align-self: stretch; 25 | 26 | @media (max-width: 768px) { 27 | padding: 24px 24px 24px 24px; 28 | width: 100%; 29 | } 30 | } 31 | 32 | .right { 33 | padding: 64px 24px 64px 24px; 34 | width: 50%; 35 | align-self: stretch; 36 | 37 | @media (max-width: 768px) { 38 | padding: 24px 24px 24px 24px; 39 | width: 100%; 40 | } 41 | } 42 | 43 | .content { 44 | padding: 24px 24px 24px 24px; 45 | width: 100%; 46 | } 47 | 48 | .footer { 49 | margin: 88px auto 128px auto; 50 | display: block; 51 | color: var(--theme-text); 52 | transition: 2000ms ease opacity; 53 | 54 | &:visited { 55 | color: var(--theme-text); 56 | } 57 | 58 | &:hover { 59 | opacity: 1; 60 | color: var(--theme-text); 61 | } 62 | } 63 | 64 | .divider { 65 | margin: 88px 0 0 0; 66 | width: 100%; 67 | display: block; 68 | height: 1px; 69 | flex-shrink: 0; 70 | border: 0; 71 | outline: 0; 72 | background: linear-gradient(to right, var(--theme-background), var(--theme-border), var(--theme-background)); 73 | } 74 | 75 | .imageHoist { 76 | padding: 0px 24px 0px 24px; 77 | } 78 | 79 | .image { 80 | display: block; 81 | width: 100%; 82 | } 83 | 84 | .quote { 85 | display: flex; 86 | align-items: flex-start; 87 | justify-content: space-between; 88 | padding: 64px 20px 24px 20px; 89 | width: 100%; 90 | } 91 | 92 | .quoteLeft { 93 | flex-shrink: 0; 94 | } 95 | 96 | .quoteRight { 97 | min-width: 10%; 98 | width: 100%; 99 | } 100 | 101 | .video { 102 | display: block; 103 | width: 100%; 104 | } 105 | -------------------------------------------------------------------------------- /components/Page.tsx: -------------------------------------------------------------------------------- 1 | import styles from '@components/DefaultLayout.module.scss'; 2 | 3 | import * as React from 'react'; 4 | 5 | import Head from 'next/head'; 6 | 7 | export default function Page(props) { 8 | return ( 9 | <> 10 | 11 | {props.title} 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | {props.children} 35 | 36 | ); 37 | } 38 | -------------------------------------------------------------------------------- /components/Providers.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import * as React from 'react'; 4 | 5 | import { ModalContext } from '@system/providers/ModalContextProvider'; 6 | 7 | interface ModalContent { 8 | data?: any; 9 | name?: string; 10 | message?: string; 11 | parentId?: string; 12 | } 13 | 14 | interface ModalContextType { 15 | modalContent: ModalContent | null; 16 | showModal: (nextContent: ModalContent | null) => void; 17 | } 18 | 19 | const initialModalContext: ModalContextType = { 20 | modalContent: null, 21 | showModal: () => {}, 22 | }; 23 | 24 | export default function Providers({ children }) { 25 | const [modalContent, setContent] = React.useState(null); 26 | 27 | const showModal = (nextContent) => { 28 | if (nextContent && modalContent && nextContent.name === modalContent.name) { 29 | setContent(null); 30 | return; 31 | } 32 | 33 | setContent(nextContent); 34 | }; 35 | 36 | const modalContextValue: ModalContextType = { 37 | modalContent, 38 | showModal, 39 | }; 40 | 41 | return {children}; 42 | } 43 | -------------------------------------------------------------------------------- /fonts/ServerMono-Regular-Italic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/internet-development/www-server-mono/be28bbd27af508b747c0a7d52fac5fdbb1c30a28/fonts/ServerMono-Regular-Italic.otf -------------------------------------------------------------------------------- /fonts/ServerMono-Regular-Italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/internet-development/www-server-mono/be28bbd27af508b747c0a7d52fac5fdbb1c30a28/fonts/ServerMono-Regular-Italic.woff -------------------------------------------------------------------------------- /fonts/ServerMono-Regular-Italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/internet-development/www-server-mono/be28bbd27af508b747c0a7d52fac5fdbb1c30a28/fonts/ServerMono-Regular-Italic.woff2 -------------------------------------------------------------------------------- /fonts/ServerMono-Regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/internet-development/www-server-mono/be28bbd27af508b747c0a7d52fac5fdbb1c30a28/fonts/ServerMono-Regular.otf -------------------------------------------------------------------------------- /fonts/ServerMono-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/internet-development/www-server-mono/be28bbd27af508b747c0a7d52fac5fdbb1c30a28/fonts/ServerMono-Regular.woff -------------------------------------------------------------------------------- /fonts/ServerMono-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/internet-development/www-server-mono/be28bbd27af508b747c0a7d52fac5fdbb1c30a28/fonts/ServerMono-Regular.woff2 -------------------------------------------------------------------------------- /modules/object-assign.ts: -------------------------------------------------------------------------------- 1 | // @ts-nocheck 2 | 3 | /* 4 | object-assign 5 | (c) Sindre Sorhus 6 | @license MIT 7 | */ 8 | 9 | 'use strict'; 10 | 11 | /* eslint-disable no-unused-vars */ 12 | var getOwnPropertySymbols = Object.getOwnPropertySymbols; 13 | var hasOwnProperty = Object.prototype.hasOwnProperty; 14 | var propIsEnumerable = Object.prototype.propertyIsEnumerable; 15 | 16 | function toObject(val) { 17 | if (val === null || val === undefined) { 18 | throw new TypeError('Object.assign cannot be called with null or undefined'); 19 | } 20 | 21 | return Object(val); 22 | } 23 | 24 | function shouldUseNative() { 25 | try { 26 | if (!Object.assign) { 27 | return false; 28 | } 29 | 30 | // Detect buggy property enumeration order in older V8 versions. 31 | 32 | // https://bugs.chromium.org/p/v8/issues/detail?id=4118 33 | var test1 = new String('abc'); // eslint-disable-line no-new-wrappers 34 | // @ts-ignore 35 | test1[5] = 'de'; 36 | if (Object.getOwnPropertyNames(test1)[0] === '5') { 37 | return false; 38 | } 39 | 40 | // https://bugs.chromium.org/p/v8/issues/detail?id=3056 41 | var test2 = {}; 42 | for (var i = 0; i < 10; i++) { 43 | test2['_' + String.fromCharCode(i)] = i; 44 | } 45 | var order2 = Object.getOwnPropertyNames(test2).map(function (n) { 46 | return test2[n]; 47 | }); 48 | if (order2.join('') !== '0123456789') { 49 | return false; 50 | } 51 | 52 | // https://bugs.chromium.org/p/v8/issues/detail?id=3056 53 | var test3 = {}; 54 | 'abcdefghijklmnopqrst'.split('').forEach(function (letter) { 55 | test3[letter] = letter; 56 | }); 57 | if (Object.keys(Object.assign({}, test3)).join('') !== 'abcdefghijklmnopqrst') { 58 | return false; 59 | } 60 | 61 | return true; 62 | } catch (err) { 63 | // We don't expect any of the above to throw, but better to be safe. 64 | return false; 65 | } 66 | } 67 | 68 | const assign = shouldUseNative() 69 | ? Object.assign 70 | : function (target, source) { 71 | var from; 72 | var to = toObject(target); 73 | var symbols; 74 | 75 | for (var s = 1; s < arguments.length; s++) { 76 | from = Object(arguments[s]); 77 | 78 | for (var key in from) { 79 | if (hasOwnProperty.call(from, key)) { 80 | to[key] = from[key]; 81 | } 82 | } 83 | 84 | if (getOwnPropertySymbols) { 85 | symbols = getOwnPropertySymbols(from); 86 | for (var i = 0; i < symbols.length; i++) { 87 | if (propIsEnumerable.call(from, symbols[i])) { 88 | to[symbols[i]] = from[symbols[i]]; 89 | } 90 | } 91 | } 92 | } 93 | 94 | return to; 95 | }; 96 | 97 | export default assign; 98 | -------------------------------------------------------------------------------- /modules/vary.ts: -------------------------------------------------------------------------------- 1 | // @ts-nocheck 2 | 3 | /*! 4 | * vary 5 | * Copyright(c) 2014-2017 Douglas Christopher Wilson 6 | * MIT Licensed 7 | */ 8 | 9 | 'use strict'; 10 | 11 | /** 12 | * RegExp to match field-name in RFC 7230 sec 3.2 13 | * 14 | * field-name = token 15 | * token = 1*tchar 16 | * tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" 17 | * / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~" 18 | * / DIGIT / ALPHA 19 | * ; any VCHAR, except delimiters 20 | */ 21 | 22 | var FIELD_NAME_REGEXP = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/; 23 | 24 | /** 25 | * Append a field to a vary header. 26 | * 27 | * @param {String} header 28 | * @param {String|Array} field 29 | * @return {String} 30 | * @public 31 | */ 32 | 33 | function append(header, field) { 34 | if (typeof header !== 'string') { 35 | throw new TypeError('header argument is required'); 36 | } 37 | 38 | if (!field) { 39 | throw new TypeError('field argument is required'); 40 | } 41 | 42 | // get fields array 43 | var fields = !Array.isArray(field) ? parse(String(field)) : field; 44 | 45 | // assert on invalid field names 46 | for (var j = 0; j < fields.length; j++) { 47 | if (!FIELD_NAME_REGEXP.test(fields[j])) { 48 | throw new TypeError('field argument contains an invalid header name'); 49 | } 50 | } 51 | 52 | // existing, unspecified vary 53 | if (header === '*') { 54 | return header; 55 | } 56 | 57 | // enumerate current values 58 | var val = header; 59 | var vals = parse(header.toLowerCase()); 60 | 61 | // unspecified vary 62 | if (fields.indexOf('*') !== -1 || vals.indexOf('*') !== -1) { 63 | return '*'; 64 | } 65 | 66 | for (var i = 0; i < fields.length; i++) { 67 | var fld = fields[i].toLowerCase(); 68 | 69 | // append value (case-preserving) 70 | if (vals.indexOf(fld) === -1) { 71 | vals.push(fld); 72 | val = val ? val + ', ' + fields[i] : fields[i]; 73 | } 74 | } 75 | 76 | return val; 77 | } 78 | 79 | /** 80 | * Parse a vary header into an array. 81 | * 82 | * @param {String} header 83 | * @return {Array} 84 | * @private 85 | */ 86 | 87 | function parse(header) { 88 | var end = 0; 89 | var list = []; 90 | var start = 0; 91 | 92 | // gather tokens 93 | for (var i = 0, len = header.length; i < len; i++) { 94 | switch (header.charCodeAt(i)) { 95 | case 0x20 /* */: 96 | if (start === end) { 97 | start = end = i + 1; 98 | } 99 | break; 100 | case 0x2c /* , */: 101 | list.push(header.substring(start, end)); 102 | start = end = i + 1; 103 | break; 104 | default: 105 | end = i + 1; 106 | break; 107 | } 108 | } 109 | 110 | // final token 111 | list.push(header.substring(start, end)); 112 | 113 | return list; 114 | } 115 | 116 | /** 117 | * Mark that a request is varied on a header field. 118 | * 119 | * @param {Object} res 120 | * @param {String|Array} field 121 | * @public 122 | */ 123 | 124 | function vary(res, field) { 125 | if (!res || !res.getHeader || !res.setHeader) { 126 | // quack quack 127 | throw new TypeError('res argument is required'); 128 | } 129 | 130 | // get existing header 131 | var val = res.getHeader('Vary') || ''; 132 | var header = Array.isArray(val) ? val.join(', ') : String(val); 133 | 134 | // set new header 135 | if ((val = append(header, field))) { 136 | res.setHeader('Vary', val); 137 | } 138 | } 139 | 140 | export default vary; 141 | -------------------------------------------------------------------------------- /next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "www-server-mono", 3 | "description": "Server Mono is a typeface inspired by typewriters, Apple's San Francisco Mono, ASCII art, command-line interfaces, and programming tools.", 4 | "engines": { 5 | "node": ">=18" 6 | }, 7 | "license": "MIT", 8 | "version": "0.0.6", 9 | "scripts": { 10 | "dev": "next -p 10000", 11 | "build": "next build", 12 | "start": "PORT=10000 next start", 13 | "lint": "next lint" 14 | }, 15 | "dependencies": { 16 | "d3": "^7.9.0", 17 | "js-cookie": "^3.0.5", 18 | "next": "^15.1.3", 19 | "react": "^19.0.0", 20 | "react-dom": "^19.0.0", 21 | "sass": "1.83.0" 22 | }, 23 | "devDependencies": { 24 | "@types/node": "^22.10.2", 25 | "@types/react": "^19.0.2", 26 | "ts-node": "^10.9.2", 27 | "typescript": "^5.7.2" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /public/apple-touch-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/internet-development/www-server-mono/be28bbd27af508b747c0a7d52fac5fdbb1c30a28/public/apple-touch-icon-precomposed.png -------------------------------------------------------------------------------- /public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/internet-development/www-server-mono/be28bbd27af508b747c0a7d52fac5fdbb1c30a28/public/apple-touch-icon.png -------------------------------------------------------------------------------- /public/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/internet-development/www-server-mono/be28bbd27af508b747c0a7d52fac5fdbb1c30a28/public/favicon-16x16.png -------------------------------------------------------------------------------- /public/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/internet-development/www-server-mono/be28bbd27af508b747c0a7d52fac5fdbb1c30a28/public/favicon-32x32.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/internet-development/www-server-mono/be28bbd27af508b747c0a7d52fac5fdbb1c30a28/public/favicon.ico -------------------------------------------------------------------------------- /public/template-app-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/internet-development/www-server-mono/be28bbd27af508b747c0a7d52fac5fdbb1c30a28/public/template-app-icon.png -------------------------------------------------------------------------------- /system/Button.module.scss: -------------------------------------------------------------------------------- 1 | .root { 2 | background-color: var(--theme-button); 3 | color: var(--theme-button-text); 4 | box-sizing: border-box; 5 | flex-shrink: 0; 6 | border-radius: 8px; 7 | outline: 0; 8 | border: 0; 9 | min-height: 48px; 10 | padding: 4px 24px 4px 24px; 11 | display: inline-flex; 12 | align-items: center; 13 | justify-content: center; 14 | font-size: var(--type-scale-fixed-small); 15 | letter-spacing: 0.2px; 16 | font-weight: 600; 17 | overflow-wrap: break-word; 18 | user-select: none; 19 | cursor: pointer; 20 | box-shadow: var(--theme-button-shadow); 21 | transition: 200ms ease all; 22 | transform: scale(1); 23 | text-decoration: none; 24 | 25 | &:visited { 26 | color: var(--theme-button-text); 27 | } 28 | 29 | &:hover { 30 | box-shadow: var(--theme-button-shadow-hover); 31 | } 32 | } 33 | 34 | .loader { 35 | color: var(--theme-text); 36 | box-shadow: 0 0 0 1px var(--theme-border); 37 | flex-shrink: 0; 38 | border-radius: 8px; 39 | outline: 0; 40 | border: 0; 41 | min-height: 48px; 42 | padding: 4px 24px 4px 24px; 43 | display: inline-flex; 44 | align-items: center; 45 | justify-content: center; 46 | user-select: none; 47 | cursor: wait; 48 | transition: 200ms ease all; 49 | transform: scale(1); 50 | text-decoration: none; 51 | } 52 | 53 | .visual { 54 | color: var(--theme-text); 55 | box-shadow: 0 0 0 1px var(--theme-border); 56 | background: var(--theme-border-subdued); 57 | opacity: 0.5; 58 | filter: blur(0.4px); 59 | flex-shrink: 0; 60 | border-radius: 8px; 61 | outline: 0; 62 | border: 0; 63 | min-height: 48px; 64 | font-size: var(--type-scale-fixed-small); 65 | letter-spacing: 0.2px; 66 | font-weight: 600; 67 | padding: 4px 24px 4px 24px; 68 | display: inline-flex; 69 | align-items: center; 70 | justify-content: center; 71 | user-select: none; 72 | cursor: not-allowed; 73 | transition: 200ms ease all; 74 | transform: scale(1); 75 | text-decoration: none; 76 | } 77 | -------------------------------------------------------------------------------- /system/Button.tsx: -------------------------------------------------------------------------------- 1 | import styles from '@system/Button.module.scss'; 2 | 3 | import Loader from '@system/Loader'; 4 | 5 | export default function Button(props) { 6 | if (props.visual) { 7 | return 29 | 30 | 31 | 46 | 61 | 76 | 77 | 104 | 105 | ); 106 | } 107 | -------------------------------------------------------------------------------- /system/FormUpload.module.scss: -------------------------------------------------------------------------------- 1 | .root { 2 | position: relative; 3 | } 4 | 5 | .input { 6 | width: 1px; 7 | height: 1px; 8 | position: absolute; 9 | display: block; 10 | opacity: 0; 11 | } 12 | 13 | .body { 14 | -webkit-appearance: none; 15 | font-size: var(--type-scale-6); 16 | transition: 200ms ease box-shadow; 17 | box-shadow: var(--theme-box-shadow-modal); 18 | background: var(--theme-background); 19 | border-radius: 2px; 20 | display: flex; 21 | padding: 16px; 22 | justify-content: center; 23 | align-items: center; 24 | height: 288px; 25 | } 26 | 27 | .hover { 28 | box-shadow: 29 | 0 0 0 1px var(--theme-border), 30 | 0 0 0 4px var(--theme-input-active); 31 | } 32 | -------------------------------------------------------------------------------- /system/FormUpload.tsx: -------------------------------------------------------------------------------- 1 | import styles from '@system/FormUpload.module.scss'; 2 | 3 | import React, { useState } from 'react'; 4 | 5 | import * as Utilities from '@common/utilities'; 6 | 7 | import ActionItem from '@system/documents/ActionItem'; 8 | import Loader from '@system/Loader'; 9 | 10 | function FormUpload(props) { 11 | const [isDragOver, setIsDragOver] = useState(false); 12 | 13 | const handleDragOver = (e) => { 14 | e.preventDefault(); 15 | if (!isDragOver) { 16 | setIsDragOver(true); 17 | } 18 | }; 19 | 20 | const handleDragEnter = (e) => { 21 | e.preventDefault(); 22 | setIsDragOver(true); 23 | }; 24 | 25 | const handleDragLeave = (e) => { 26 | e.preventDefault(); 27 | setIsDragOver(false); 28 | }; 29 | 30 | const handleDrop = (e) => { 31 | e.preventDefault(); 32 | if (e.dataTransfer.files && e.dataTransfer.files[0]) { 33 | props.onSetFile(e.dataTransfer.files[0]); 34 | } 35 | }; 36 | 37 | const handleChange = (e) => { 38 | e.preventDefault(); 39 | if (e.target.files && e.target.files[0]) { 40 | props.onSetFile(e.target.files[0]); 41 | } 42 | }; 43 | 44 | if (props.isActionItem) { 45 | return ( 46 | : `⊹`} htmlFor="template-form-upload-input"> 47 | 48 | {props.children} 49 | 50 | ); 51 | } 52 | 53 | return ( 54 |
    55 | 56 | 59 |
    60 | ); 61 | } 62 | 63 | export default FormUpload; 64 | -------------------------------------------------------------------------------- /system/ImageBlock.module.scss: -------------------------------------------------------------------------------- 1 | .root { 2 | width: 100%; 3 | padding: 48px; 4 | display: flex; 5 | align-items: center; 6 | justify-content: center; 7 | } 8 | -------------------------------------------------------------------------------- /system/ImageBlock.tsx: -------------------------------------------------------------------------------- 1 | import styles from '@system/ImageBlock.module.scss'; 2 | 3 | function ImageBlock(props) { 4 | return
    ; 5 | } 6 | 7 | export default ImageBlock; 8 | -------------------------------------------------------------------------------- /system/Input.module.scss: -------------------------------------------------------------------------------- 1 | .input { 2 | -webkit-appearance: none; 3 | color: var(--theme-text); 4 | background: var(--theme-background); 5 | box-sizing: border-box; 6 | width: 100%; 7 | height: 48px; 8 | border-radius: 4px; 9 | display: flex; 10 | font-size: var(--type-scale-fixed-medium); 11 | align-items: center; 12 | justify-content: flex-start; 13 | outline: 0; 14 | border: 0; 15 | box-sizing: border-box; 16 | transition: 200ms ease all; 17 | padding: 0 16px 0 16px; 18 | text-overflow: ellipsis; 19 | white-space: nowrap; 20 | box-shadow: 0 0 0 1px var(--theme-border); 21 | 22 | &:focus { 23 | outline: 0; 24 | border: 0; 25 | outline: 0; 26 | box-shadow: 27 | 0 0 0 1px var(--theme-border), 28 | 0 0 0 4px var(--theme-input-active); 29 | } 30 | 31 | &::placeholder { 32 | opacity: 1; 33 | color: var(--theme-border); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /system/Input.tsx: -------------------------------------------------------------------------------- 1 | import styles from '@system/Input.module.scss'; 2 | 3 | function Input(props) { 4 | return ; 5 | } 6 | 7 | export default Input; 8 | -------------------------------------------------------------------------------- /system/IsometricProductBox.module.scss: -------------------------------------------------------------------------------- 1 | .box { 2 | position: absolute; 3 | box-shadow: 0 0 0 1px transparent; 4 | transition: 200ms ease box-shadow; 5 | 6 | &:hover { 7 | box-shadow: 0 0 0 1px var(--theme-border-box); 8 | } 9 | } 10 | 11 | .anchor { 12 | position: relative; 13 | pointer-events: none; 14 | } 15 | 16 | .cube { 17 | pointer-events: none; 18 | position: relative; 19 | transform-style: preserve-3d; 20 | transform: rotateX(-35deg) rotateY(45deg); 21 | transition: transform 1s; 22 | } 23 | 24 | .face { 25 | position: absolute; 26 | font-size: 40px; 27 | color: var(--theme-text); 28 | height: 100%; 29 | } 30 | 31 | .front { 32 | background: var(--theme-background-box-front); 33 | display: flex; 34 | align-items: flex-start; 35 | } 36 | 37 | .left { 38 | background: var(--theme-background-box-side); 39 | display: flex; 40 | align-items: center; 41 | justify-content: space-between; 42 | } 43 | 44 | .top { 45 | background: var(--theme-background-box-top); 46 | } 47 | 48 | .topTwo { 49 | background: var(--theme-background-box-side); 50 | } 51 | 52 | .brand { 53 | padding: 16px 24px 0 24px; 54 | width: 100%; 55 | opacity: 0.8; 56 | } 57 | 58 | .grid { 59 | display: flex; 60 | justify-content: space-between; 61 | align-items: flex-start; 62 | flex-direction: column; 63 | } 64 | 65 | .row { 66 | width: 100%; 67 | height: 160px; 68 | display: flex; 69 | align-items: flex-start; 70 | justify-content: space-between; 71 | border-bottom: 1px solid var(--theme-border-box); 72 | } 73 | 74 | .column { 75 | width: 50%; 76 | padding: 24px; 77 | font-size: 14px; 78 | font-weight: 400; 79 | } 80 | 81 | .side { 82 | display: flex; 83 | justify-content: space-between; 84 | align-items: flex-start; 85 | width: 100%; 86 | height: 100%; 87 | } 88 | 89 | .panel { 90 | border-right: 1px solid var(--theme-border-box); 91 | height: 100%; 92 | width: 20%; 93 | opacity: 1; 94 | } 95 | -------------------------------------------------------------------------------- /system/IsometricRect.module.scss: -------------------------------------------------------------------------------- 1 | .box { 2 | position: absolute; 3 | box-shadow: 0 0 0 1px transparent; 4 | transition: 200ms ease box-shadow; 5 | 6 | &:hover { 7 | box-shadow: 0 0 0 1px var(--theme-border); 8 | } 9 | } 10 | 11 | .anchor { 12 | position: relative; 13 | pointer-events: none; 14 | } 15 | 16 | .cube { 17 | pointer-events: none; 18 | position: relative; 19 | transform-style: preserve-3d; 20 | transform: rotateX(-35deg) rotateY(45deg); 21 | transition: transform 1s; 22 | } 23 | 24 | .face { 25 | position: absolute; 26 | font-size: 40px; 27 | color: var(--theme-background); 28 | text-align: center; 29 | border: 1px solid var(--theme-border); 30 | } 31 | 32 | .front { 33 | background: var(--theme-text); 34 | } 35 | 36 | .left { 37 | background: var(--theme-text); 38 | display: flex; 39 | align-items: center; 40 | justify-content: center; 41 | } 42 | 43 | .top { 44 | background: var(--theme-text); 45 | } 46 | 47 | .iframe { 48 | height: 100%; 49 | width: 100%; 50 | } 51 | -------------------------------------------------------------------------------- /system/IsometricRect.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as Utilities from '@common/utilities'; 3 | 4 | import styles from '@system/IsometricRect.module.scss'; 5 | 6 | interface ResizableMonospaceWindowProps { 7 | children?: React.ReactNode; 8 | disableResize?: boolean; 9 | ref?: React.Ref; 10 | size: number; 11 | src: string; 12 | x: number; 13 | y: number; 14 | } 15 | 16 | const IsometricRect = React.forwardRef((props, ref) => { 17 | const [isDragging, setIsDragging] = React.useState(false); 18 | const [initialMouseX, setInitialMouseX] = React.useState(0); 19 | const [initialMouseY, setInitialMouseY] = React.useState(0); 20 | const [dx, setDx] = React.useState(props.x); 21 | const [dy, setDy] = React.useState(props.y); 22 | const [width, setWidth] = React.useState(props.size); 23 | const [height, setHeight] = React.useState(props.size); 24 | const [resizeEdge, setResizeEdge] = React.useState(''); 25 | 26 | const handleMouseMove = React.useCallback( 27 | (e) => { 28 | if (isDragging) { 29 | const deltaX = e.clientX - initialMouseX; 30 | const deltaY = e.clientY - initialMouseY; 31 | setDx(dx + deltaX); 32 | setDy(dy + deltaY); 33 | setInitialMouseX(e.clientX); 34 | setInitialMouseY(e.clientY); 35 | } else if (resizeEdge) { 36 | if (resizeEdge === 'right') { 37 | setWidth(Math.max(100, width + (e.clientX - initialMouseX))); 38 | } else if (resizeEdge === 'bottom') { 39 | setHeight(Math.max(100, height + (e.clientY - initialMouseY))); 40 | } else if (resizeEdge === 'left') { 41 | const newWidth = Math.max(100, width - (e.clientX - initialMouseX)); 42 | if (newWidth > 100) { 43 | setDx(dx + (e.clientX - initialMouseX)); 44 | setWidth(newWidth); 45 | } 46 | } else if (resizeEdge === 'top') { 47 | const newHeight = Math.max(100, height - (e.clientY - initialMouseY)); 48 | if (newHeight > 100) { 49 | setDy(dy + (e.clientY - initialMouseY)); 50 | setHeight(newHeight); 51 | } 52 | } 53 | setInitialMouseX(e.clientX); 54 | setInitialMouseY(e.clientY); 55 | } 56 | }, 57 | [isDragging, resizeEdge, initialMouseX, initialMouseY, dx, dy, width, height] 58 | ); 59 | 60 | const handleMouseUp = React.useCallback(() => { 61 | setIsDragging(false); 62 | setResizeEdge(''); 63 | }, []); 64 | 65 | React.useEffect(() => { 66 | if (isDragging || resizeEdge) { 67 | document.addEventListener('mousemove', handleMouseMove); 68 | document.addEventListener('mouseup', handleMouseUp); 69 | return () => { 70 | document.removeEventListener('mousemove', handleMouseMove); 71 | document.removeEventListener('mouseup', handleMouseUp); 72 | }; 73 | } 74 | }, [handleMouseMove, handleMouseUp, isDragging, resizeEdge]); 75 | 76 | const handleMouseDown = (e) => { 77 | if (e.target.dataset.resizer) { 78 | setResizeEdge(e.target.dataset.resizer); 79 | } else if (e.target.dataset.header) { 80 | setIsDragging(true); 81 | } else { 82 | return; 83 | } 84 | setInitialMouseX(e.clientX); 85 | setInitialMouseY(e.clientY); 86 | e.preventDefault(); 87 | }; 88 | 89 | return ( 90 |
    101 |
    108 |
    115 |
    124 | 125 |
    134 | 135 |
    136 |
    145 |
    146 |
    147 |
    148 | ); 149 | }); 150 | 151 | export default IsometricRect; 152 | -------------------------------------------------------------------------------- /system/KeyHeader.module.scss: -------------------------------------------------------------------------------- 1 | .root { 2 | display: flex; 3 | justify-content: space-between; 4 | align-items: center; 5 | box-shadow: 0 1px 0 0 var(--theme-border); 6 | min-height: 48px; 7 | } 8 | 9 | .left { 10 | min-height: 48px; 11 | min-width: 100px; 12 | flex-shrink: 0; 13 | display: inline-flex; 14 | align-items: center; 15 | justify-content: flex-start; 16 | padding: 0 12px 0 12px; 17 | -webkit-appearance: none; 18 | box-shadow: 1px 0 0 0 var(--theme-border); 19 | transition: 200ms ease width; 20 | } 21 | 22 | .stretch { 23 | min-width: 10%; 24 | width: 100%; 25 | display: flex; 26 | align-items: center; 27 | justify-content: flex-start; 28 | } 29 | 30 | .input { 31 | padding: 0 12px 0 24px; 32 | width: 100%; 33 | height: 100%; 34 | display: flex; 35 | font-size: var(--type-scale-fixed-medium); 36 | letter-spacing: 0.2px; 37 | color: var(--theme-text); 38 | 39 | border: 0; 40 | outline: 0; 41 | background: transparent; 42 | 43 | &:focus { 44 | border: 0; 45 | outline: 0; 46 | } 47 | 48 | &::placeholder { 49 | opacity: 1; 50 | color: var(--theme-border); 51 | } 52 | } 53 | 54 | .item { 55 | font-size: var(--type-scale-fixed-tiny); 56 | text-transform: uppercase; 57 | letter-spacing: 0.2px; 58 | font-weight: 600; 59 | padding: 0 12px 0 12px; 60 | cursor: pointer; 61 | user-select: none; 62 | transition: 200ms ease all; 63 | text-decoration: none; 64 | color: var(--theme-text); 65 | 66 | &:visited { 67 | color: var(--theme-text); 68 | } 69 | 70 | &:hover { 71 | opacity: 0.8; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /system/KeyHeader.tsx: -------------------------------------------------------------------------------- 1 | import styles from '@system/KeyHeader.module.scss'; 2 | 3 | import * as React from 'react'; 4 | 5 | import Cookies from 'js-cookie'; 6 | 7 | import { useModal } from '@system/providers/ModalContextProvider'; 8 | 9 | export default function KeyHeader(props) { 10 | const { showModal } = useModal(); 11 | 12 | if (props.isHidden) { 13 | return