mirror of
https://github.com/aljazceru/pear-docs.git
synced 2025-12-17 14:34:19 +01:00
removed app logic and assets (#90)
Co-authored-by: rafapaezbas <rafa@holepunch.com>
This commit is contained in:
23
.github/workflows/ci.yml
vendored
23
.github/workflows/ci.yml
vendored
@@ -1,23 +0,0 @@
|
||||
name: Build Status
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [lts/*]
|
||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- run: npm install
|
||||
- run: npm test
|
||||
27
CHANGELOG.md
27
CHANGELOG.md
@@ -1,27 +0,0 @@
|
||||
# Pear Desktop Changelog
|
||||
|
||||
## v1.1.0
|
||||
|
||||
### Features
|
||||
- Label Pear API's as experimental.
|
||||
- Added guide on terminal debuging.
|
||||
- Added 'Apps' section and keet.md.
|
||||
- Added note on Bare Runtime + cross platform app example.
|
||||
- Added update button.
|
||||
- Removed deprecated bultinMaps configuration from documentation.
|
||||
- Implemented anchor auto scroll on navigation.
|
||||
|
||||
### UX/UI Improvements
|
||||
- Improved system path setup UI.
|
||||
- Various typo fixes.
|
||||
- Language improvements.
|
||||
|
||||
### Fixes
|
||||
- Windows - System Path Setup is manual only for now.
|
||||
- Fixed navigation after Pear installation.
|
||||
- Path text is selectable in installation screen.
|
||||
- Fixed indexes for Gitbook and Pear-Desktop app.
|
||||
|
||||
## v1.0.0
|
||||
|
||||
First public release 🍐
|
||||
@@ -1 +0,0 @@
|
||||
<svg width="64px" height="64px" viewBox="0 0 32.00 32.00" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns" fill="#B0D944" stroke="#B0D944"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round" stroke="#CCCCCC" stroke-width="0.384"></g><g id="SVGRepo_iconCarrier"> <title>arrow-left-square</title> <desc>Created with Sketch Beta.</desc> <defs> </defs> <g id="Page-1" stroke-width="0.48" fill="none" fill-rule="evenodd" sketch:type="MSPage"> <g id="Icon-Set" sketch:type="MSLayerGroup" transform="translate(-412.000000, -983.000000)" fill="#b0D944"> <path d="M434,998 L423.414,998 L427.535,993.879 C427.926,993.488 427.926,992.855 427.535,992.465 C427.145,992.074 426.512,992.074 426.121,992.465 L420.465,998.121 C420.225,998.361 420.15,998.689 420.205,999 C420.15,999.311 420.225,999.639 420.465,999.879 L426.121,1005.54 C426.512,1005.93 427.145,1005.93 427.535,1005.54 C427.926,1005.15 427.926,1004.51 427.535,1004.12 L423.414,1000 L434,1000 C434.553,1000 435,999.553 435,999 C435,998.448 434.553,998 434,998 L434,998 Z M442,1011 C442,1012.1 441.104,1013 440,1013 L416,1013 C414.896,1013 414,1012.1 414,1011 L414,987 C414,985.896 414.896,985 416,985 L440,985 C441.104,985 442,985.896 442,987 L442,1011 L442,1011 Z M440,983 L416,983 C413.791,983 412,984.791 412,987 L412,1011 C412,1013.21 413.791,1015 416,1015 L440,1015 C442.209,1015 444,1013.21 444,1011 L444,987 C444,984.791 442.209,983 440,983 L440,983 Z" id="arrow-left-square" sketch:type="MSShapeGroup"> </path> </g> </g> </g></svg>
|
||||
|
Before Width: | Height: | Size: 1.6 KiB |
@@ -1 +0,0 @@
|
||||
<svg width="64px" height="64px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <path d="M11.75 5.5C11.1977 5.5 10.75 5.05228 10.75 4.5V2C10.75 1.44772 11.1977 1 11.75 1H12.25C12.8023 1 13.25 1.44772 13.25 2V4.5C13.25 5.05228 12.8023 5.5 12.25 5.5H11.75Z" fill="#B0D944"></path> <path d="M16.4194 7.22698C16.0289 6.83646 16.0289 6.20329 16.4194 5.81277L18.1872 4.045C18.5777 3.65447 19.2109 3.65447 19.6014 4.045L19.9549 4.39855C20.3455 4.78908 20.3455 5.42224 19.9549 5.81277L18.1872 7.58053C17.7967 7.97106 17.1635 7.97106 16.773 7.58053L16.4194 7.22698Z" fill="#B0D944"></path> <path d="M18.5 11.75C18.5 11.1977 18.9477 10.75 19.5 10.75H22C22.5523 10.75 23 11.1977 23 11.75V12.25C23 12.8023 22.5523 13.25 22 13.25H19.5C18.9477 13.25 18.5 12.8023 18.5 12.25V11.75Z" fill="#B0D944"></path> <path d="M16.7728 16.4194C17.1633 16.0289 17.7965 16.0289 18.187 16.4194L19.9548 18.1872C20.3453 18.5777 20.3453 19.2109 19.9548 19.6014L19.6012 19.9549C19.2107 20.3455 18.5775 20.3455 18.187 19.9549L16.4192 18.1872C16.0287 17.7967 16.0287 17.1635 16.4192 16.773L16.7728 16.4194Z" fill="#B0D944"></path> <path d="M12.25 18.5C12.8023 18.5 13.25 18.9477 13.25 19.5V22C13.25 22.5523 12.8023 23 12.25 23H11.75C11.1977 23 10.75 22.5523 10.75 22V19.5C10.75 18.9477 11.1977 18.5 11.75 18.5H12.25Z" fill="#B0D944"></path> <path d="M7.58059 16.773C7.97111 17.1635 7.97111 17.7967 7.58059 18.1872L5.81282 19.955C5.4223 20.3455 4.78913 20.3455 4.39861 19.955L4.04505 19.6014C3.65453 19.2109 3.65453 18.5778 4.04505 18.1872L5.81282 16.4195C6.20334 16.0289 6.83651 16.0289 7.22703 16.4195L7.58059 16.773Z" fill="#B0D944"></path> <path d="M5.5 12.25C5.5 12.8023 5.05228 13.25 4.5 13.25H2C1.44772 13.25 1 12.8023 1 12.25V11.75C1 11.1977 1.44772 10.75 2 10.75H4.5C5.05228 10.75 5.5 11.1977 5.5 11.75V12.25Z" fill="#B0D944"></path> <path d="M7.22722 7.58059C6.8367 7.97111 6.20353 7.97111 5.81301 7.58059L4.04524 5.81282C3.65472 5.4223 3.65472 4.78913 4.04524 4.39861L4.3988 4.04505C4.78932 3.65453 5.42248 3.65453 5.81301 4.04505L7.58078 5.81282C7.9713 6.20334 7.9713 6.83651 7.58078 7.22703L7.22722 7.58059Z" fill="#B0D944"></path> <path d="M7 12C7 9.23858 9.23858 7 12 7C14.7614 7 17 9.23858 17 12C17 14.7614 14.7614 17 12 17C9.23858 17 7 14.7614 7 12Z" fill="#B0D944"></path> </g></svg>
|
||||
|
Before Width: | Height: | Size: 2.4 KiB |
BIN
assets/usage.mp4
BIN
assets/usage.mp4
Binary file not shown.
328
index.html
328
index.html
@@ -1,328 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<body>
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: 'overpass-mono';
|
||||
src: url('~redhat-overpass-font/webfonts/overpass-mono-webfont/overpass-mono-regular.woff2') format('woff2');
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'overpass-mono';
|
||||
src: url('~redhat-overpass-font/webfonts/overpass-mono-webfont/overpass-mono-bold.woff2') format('woff2');
|
||||
font-weight: 600;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
body {
|
||||
background: #151517;
|
||||
box-sizing: border-box;
|
||||
border-radius: 8px;
|
||||
font-family: 'overpass-mono';
|
||||
-webkit-font-smoothing: antialiased;
|
||||
color: #efeaea;
|
||||
transition: background-color .4s, color .4s;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: #151517;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: #727272;
|
||||
}
|
||||
|
||||
html.light body {
|
||||
background: #efeaea;
|
||||
color: #151517;
|
||||
}
|
||||
|
||||
#logo {
|
||||
float: left;
|
||||
margin-right: 2rem;
|
||||
margin-left: 0.65rem;
|
||||
margin-top: -0.1rem;
|
||||
}
|
||||
|
||||
nav {
|
||||
height: 146px;
|
||||
float: left;
|
||||
padding-left: .75rem;
|
||||
padding-top: 2em;
|
||||
}
|
||||
|
||||
nav>a {
|
||||
display: block;
|
||||
}
|
||||
|
||||
section {
|
||||
display: block;
|
||||
}
|
||||
|
||||
pre {
|
||||
background: none;
|
||||
color: #B0D944;
|
||||
font-family: overpass-mono;
|
||||
border: none;
|
||||
height: 146px;
|
||||
user-select: none;
|
||||
position: relative;
|
||||
right: 5em;
|
||||
top: 0.33em;
|
||||
float: right;
|
||||
}
|
||||
|
||||
header {
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 170px;
|
||||
z-index: 10;
|
||||
background: rgba(21, 21, 23, 0.9);
|
||||
padding-top: 20px;
|
||||
width: 100%;
|
||||
transition: background-color .4s, color .4s;
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
#headin {
|
||||
transform: scale(0.92);
|
||||
width: 72rem;
|
||||
margin: 0 auto;
|
||||
margin-top: .75rem;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
h1 {
|
||||
padding: .5rem;
|
||||
border-right: 1px solid #B0D944;
|
||||
border-bottom: 1px solid #B0D944;
|
||||
display: inline-block;
|
||||
padding-right: 0.75em;
|
||||
padding-left: 0.5em;
|
||||
font-weight: bold;
|
||||
font-size: 1.6rem;
|
||||
margin-block-start: 0.67em;
|
||||
margin-block-end: 0.67em;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
outline: 1px solid #323532;
|
||||
margin-inline-start: 0;
|
||||
margin-inline-end: 0;
|
||||
display: block;
|
||||
margin-block-start: 1rem;
|
||||
margin-block-end: 0;
|
||||
padding: 1px;
|
||||
font-size: .825rem
|
||||
}
|
||||
|
||||
blockquote::before {
|
||||
content: "ℹ";
|
||||
float: left;
|
||||
font-size: 1.625rem;
|
||||
margin-left: 1rem;
|
||||
margin-right: 0.625rem;
|
||||
}
|
||||
|
||||
.col {
|
||||
max-width: 63rem;
|
||||
margin: 0 auto;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
#back,
|
||||
#mode {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
position: absolute;
|
||||
right: 0.4rem;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
opacity: .65;
|
||||
}
|
||||
|
||||
#back {
|
||||
top: 10px;
|
||||
}
|
||||
|
||||
#mode {
|
||||
top: 60px;
|
||||
}
|
||||
|
||||
#bar {
|
||||
backdrop-filter: blur(64px);
|
||||
-webkit-app-region: drag;
|
||||
padding: 0;
|
||||
border-top-left-radius: 4px;
|
||||
border-top-right-radius: 4px;
|
||||
color: #B0D944;
|
||||
white-space: nowrap;
|
||||
box-sizing: border-box;
|
||||
position: fixed;
|
||||
z-index: 100;
|
||||
width: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
#bar:hover {
|
||||
outline: #B0D944;
|
||||
}
|
||||
|
||||
pear-ctrl {
|
||||
margin-top: 9px;
|
||||
}
|
||||
|
||||
pear-ctrl[data-platform="darwin"] {
|
||||
margin-top: 16px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
system-status,
|
||||
docs-viewer {
|
||||
position: relative;
|
||||
top: 156px
|
||||
}
|
||||
|
||||
developer-tooling {
|
||||
position: relative;
|
||||
top: 92px
|
||||
}
|
||||
|
||||
a:visited,
|
||||
a:active,
|
||||
a {
|
||||
color: #B0D944;
|
||||
outline: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
app-router a:visited,
|
||||
app-router a:active,
|
||||
app-router a {
|
||||
color: #B0D944;
|
||||
}
|
||||
|
||||
app-router[data-load="system-status"] a[href='/'],
|
||||
app-router[data-load="system-status"] a:active[href='/'],
|
||||
app-router[data-load="system-status"] a:visited[href='/'],
|
||||
app-router[data-load="docs-viewer"] a[href='/documentation'],
|
||||
app-router[data-load="docs-viewer"] a:active[href='/documentation'],
|
||||
app-router[data-load="docs-viewer"] a:visited[href='/documentation'],
|
||||
app-router[data-load="developer-tooling"] a[href='/devtools'],
|
||||
app-router[data-load="developer-tooling"] a:active[href='/devtools'],
|
||||
app-router[data-load="developer-tooling"] a:visited[href='/devtools'] {
|
||||
color: rgb(78, 250, 92);
|
||||
}
|
||||
</style>
|
||||
<div id="bar"><pear-ctrl></pear-ctrl></div>
|
||||
<app-router docs-viewer="/documentation" developer-tooling="/devtools" system-status="/" data-load="system-status">
|
||||
<header>
|
||||
<div id="headin">
|
||||
<svg id="logo" width="102" height="146" viewBox="0 0 102 146" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_8912_10861)">
|
||||
<path d="M47.4056 0.838379H54.5943V15.0361H47.4056V0.838379Z" fill="#B0D944" />
|
||||
<path d="M43.8113 19.5305V22.406H36.6226V26.0004H65.3774V22.406H58.1887V16.8347H51V19.5305H43.8113Z" fill="#B0D944" />
|
||||
<path d="M72.5662 27.7974H51V30.4931H29.4339V36.963H72.5662V27.7974Z" fill="#B0D944" />
|
||||
<path d="M79.7548 38.7593H51V41.455H22.2451V47.9249H79.7548V38.7593Z" fill="#B0D944" />
|
||||
<path d="M79.7548 49.7219H51V52.4177H22.2451V58.8875H79.7548V49.7219Z" fill="#B0D944" />
|
||||
<path d="M86.9436 60.6846H51V63.3803H15.0565V69.8502H86.9436V60.6846Z" fill="#B0D944" />
|
||||
<path d="M86.9436 71.6481H51V74.3438H15.0565V80.8137H86.9436V71.6481Z" fill="#B0D944" />
|
||||
<path d="M94.1323 82.61H51V85.3058H7.86774V91.7756H94.1323V82.61Z" fill="#B0D944" />
|
||||
<path d="M101.321 93.5726H51V96.2684H0.679016V102.738H101.321V93.5726Z" fill="#B0D944" />
|
||||
<path d="M101.321 104.536H51V107.232H0.679016V113.702H101.321V104.536Z" fill="#B0D944" />
|
||||
<path d="M101.321 115.499H51V118.195H0.679016V124.664H101.321V115.499Z" fill="#B0D944" />
|
||||
<path d="M86.9436 126.461H51V129.156H15.0565V135.626H86.9436V126.461Z" fill="#B0D944" />
|
||||
<path d="M72.5662 137.424H51V140.12H29.4339V144.613H72.5662V137.424Z" fill="#B0D944" />
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_8912_10861">
|
||||
<rect width="100.642" height="145.571" fill="white" transform="translate(0.679016 0.214233)" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
<img id="mode" src="/assets/light.svg" onclick="document.documentElement.classList.toggle('light');">
|
||||
<img id="back" src="/assets/arrow.svg" onclick="history.back()">
|
||||
<nav>
|
||||
<a href="/">System Status</a>
|
||||
<a href="/documentation">Documentation</a>
|
||||
<a href="/devtools">Developer Tooling</a>
|
||||
</nav>
|
||||
<pre>
|
||||
__ \ _ \ _` | __|
|
||||
| | __/ ( | |
|
||||
.__/ \___| \__,_| _|
|
||||
_|
|
||||
</pre>
|
||||
</div>
|
||||
</header>
|
||||
<section>
|
||||
<div class="col"><system-status></system-status></div>
|
||||
</section>
|
||||
<script type='module' src='./lib/system-status.js'></script>
|
||||
<section>
|
||||
<div class="col"><docs-viewer></docs-viewer></div>
|
||||
</section>
|
||||
<script type='module' src='./lib/docs-viewer.js'></script>
|
||||
<section>
|
||||
<div class="col"><developer-tooling></developer-tooling></div>
|
||||
</section>
|
||||
<script type="module">
|
||||
customElements.define('pear-version', class extends HTMLElement {
|
||||
constructor () {
|
||||
super()
|
||||
this.template = document.createElement('template')
|
||||
this.template.innerHTML = `
|
||||
<style>
|
||||
:host > div {
|
||||
position: fixed;
|
||||
top: 96vh;
|
||||
font-size: .6rem;
|
||||
background: #1a1a1a;
|
||||
color: white;
|
||||
opacity: 1;
|
||||
padding: 8px;
|
||||
padding-top: 0px;
|
||||
padding-bottom: 0px;
|
||||
border-radius: .35rem;
|
||||
border: 1px solid #3a4816;
|
||||
transition: top 0.3s;
|
||||
display: block;
|
||||
}
|
||||
|
||||
:host > div:hover {
|
||||
top: 92vh;
|
||||
transition: top 0.3s;
|
||||
}
|
||||
</style>
|
||||
<div>
|
||||
<p id="platform-version"></p>
|
||||
<p id="app-version"></p>
|
||||
</div>
|
||||
`
|
||||
this.root = this.attachShadow({ mode: 'open' })
|
||||
|
||||
Pear.versions().then(({ app, platform }) => {
|
||||
this.root.appendChild(this.template.content.cloneNode(true))
|
||||
this.root.querySelector('#platform-version').innerText = `Pear Runtime ${platform.fork}.${platform.length}.${platform.key}`
|
||||
this.root.querySelector('#app-version').innerText = `Pear Desktop ${app.fork}.${app.length}.${app.key}`
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
<pear-version></pear-version>
|
||||
<script>
|
||||
process.env.WS_NO_BUFFER_UTIL = false // ws buffer-util opt-out
|
||||
</script>
|
||||
<script type='module' src='./lib/devtools.js'></script>
|
||||
</app-router>
|
||||
<script type='module' src='./lib/app-router.js'></script>
|
||||
</body>
|
||||
@@ -1,85 +0,0 @@
|
||||
/* eslint-env browser */
|
||||
customElements.define('app-router', class AppRouter extends HTMLElement {
|
||||
constructor () {
|
||||
super()
|
||||
this.routes = {}
|
||||
this.page = null
|
||||
this.anchor = null
|
||||
}
|
||||
|
||||
unload () {
|
||||
for (const element of Object.values(this.routes)) element?.unload && element.unload()
|
||||
}
|
||||
|
||||
async load (pathname = '/', opts = {}) {
|
||||
if (this.page === pathname && this.anchor === opts.anchor) return
|
||||
this.page = pathname
|
||||
this.anchor = opts.anchor
|
||||
for (const [route, element] of Object.entries(this.routes)) {
|
||||
if (pathname.startsWith(route)) {
|
||||
const page = pathname.slice(route.length) || '/'
|
||||
this.unload()
|
||||
document.documentElement.scrollTop = 0
|
||||
this.dataset.load = element.tagName.toLowerCase()
|
||||
await element.load(page, opts)
|
||||
|
||||
const isDocumentationPage = pathname.startsWith('/documentation')
|
||||
const anchor = opts.anchor
|
||||
const shouldShowSpecificSection = anchor && isDocumentationPage
|
||||
if (shouldShowSpecificSection) {
|
||||
const element = this.routes['/documentation'].shadowRoot.getElementById(anchor)
|
||||
element.scrollIntoView()
|
||||
const elementY = Math.floor(element.getBoundingClientRect().y)
|
||||
const pearHeaderHeight = 170
|
||||
const extraScroll = 80
|
||||
const isUnderPearHeader = elementY < pearHeaderHeight + extraScroll
|
||||
if (isUnderPearHeader) {
|
||||
window.scrollBy(0, -1 * (pearHeaderHeight + extraScroll - elementY))
|
||||
}
|
||||
}
|
||||
|
||||
if (!opts.back) history.pushState({ pathname, anchor: opts.anchor }, null, pathname)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
link (evt) {
|
||||
if (evt.target?.tagName !== 'A') return
|
||||
evt.preventDefault()
|
||||
if (evt.target.origin !== location.origin) return window.open(evt.target.href)
|
||||
const { tagName } = evt.target.getRootNode().host || {}
|
||||
const route = tagName ? this.getAttribute(tagName) : ''
|
||||
if (evt.target.pathname.startsWith(route)) {
|
||||
const anchor = evt.target.href.split('#')[1]
|
||||
this.load(evt.target.pathname, { anchor }).catch(console.error)
|
||||
} else {
|
||||
this.load(route + evt.target.pathname).catch(console.error)
|
||||
}
|
||||
}
|
||||
|
||||
connectedCallback () {
|
||||
for (const { name, value } of Array.from(this.attributes)) {
|
||||
if (name.startsWith('data-')) continue
|
||||
this.routes[value] = this.querySelector(name)
|
||||
this.routes[value].router = this
|
||||
}
|
||||
|
||||
this.addEventListener('click', (evt) => this.link(evt))
|
||||
|
||||
window.addEventListener('popstate', (evt) => {
|
||||
this.load(evt.state?.pathname, { back: true, anchor: evt.state?.anchor }).catch(console.error)
|
||||
})
|
||||
|
||||
window.addEventListener('load', () => {
|
||||
const page = '/' + (Pear.config.linkData || '')
|
||||
console.log('load', page)
|
||||
this.load(page).catch(console.error)
|
||||
Pear.wakeups(({ data }) => {
|
||||
Pear.Window.self.focus({ steal: true }).catch(console.error)
|
||||
const page = '/' + (data || '')
|
||||
this.load(page).catch(console.error)
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
429
lib/devtools.js
429
lib/devtools.js
@@ -1,429 +0,0 @@
|
||||
/* eslint-env browser */
|
||||
import http from 'http'
|
||||
import { WebSocketServer } from 'ws'
|
||||
import { Session } from 'pear-inspect'
|
||||
import b4a from 'b4a'
|
||||
import { spawn } from 'child_process'
|
||||
|
||||
customElements.define('developer-tooling', class extends HTMLElement {
|
||||
router = null
|
||||
port = 9342
|
||||
|
||||
constructor () {
|
||||
super()
|
||||
this.template = document.createElement('template')
|
||||
this.template.innerHTML = `
|
||||
<div>
|
||||
<style>
|
||||
:host > div {
|
||||
font-size: .9em;
|
||||
margin-left: -4px; /* scrollbar offset compensate */
|
||||
}
|
||||
|
||||
#add-key-input {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#add-key-error,
|
||||
#change-port-error {
|
||||
color: red;
|
||||
}
|
||||
|
||||
#server-message:hover #change-port-show {
|
||||
display: inline-block;
|
||||
}
|
||||
#change-port-show {
|
||||
display: none;
|
||||
}
|
||||
#change-port-input {
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
/* hide up/down arrow for input type=number */
|
||||
input::-webkit-outer-spin-button,
|
||||
input::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.panels-wrapper {
|
||||
display: flex;
|
||||
gap: 30px;
|
||||
}
|
||||
.panel-left {
|
||||
flex: 1;
|
||||
}
|
||||
.panel-right {
|
||||
width: 400px;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.app {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0.25rem;
|
||||
padding-top: 0.1rem;
|
||||
padding-bottom: 0.15rem;
|
||||
}
|
||||
.app .title {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.app:hover .copy,
|
||||
.app:hover .open-in-chrome,
|
||||
.app:hover .remove {
|
||||
display: block;
|
||||
}
|
||||
.app .copy,
|
||||
.app .open-in-chrome {
|
||||
margin-right: 10px;
|
||||
}
|
||||
.app .copy,
|
||||
.app .open-in-chrome,
|
||||
.app .remove {
|
||||
display: none;
|
||||
}
|
||||
.button {
|
||||
cursor: pointer;
|
||||
background: #3a4816;
|
||||
color: #efeaea;
|
||||
padding: 0 0.25rem;
|
||||
font-family: 'overpass-mono';
|
||||
border-radius: 1px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
#server-message {
|
||||
font-size: 0.8rem;
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
h2 { margin: 0 }
|
||||
p {
|
||||
margin-block-start: 0.75em;
|
||||
margin-block-end: 0.75em;
|
||||
}
|
||||
|
||||
input {
|
||||
all: unset;
|
||||
border: 1px ridge #B0D944;
|
||||
background: #000;
|
||||
color: #B0D944;
|
||||
padding: .45rem;
|
||||
font-family: monospace;
|
||||
font-size: 1rem;
|
||||
line-height: 1rem;
|
||||
}
|
||||
code {
|
||||
background: #3a4816;
|
||||
color: #efeaea;
|
||||
padding: 0.25rem;
|
||||
padding-top: 0.1rem;
|
||||
padding-bottom: 0.15rem;
|
||||
font-family: 'overpass-mono';
|
||||
border-radius: 1px;
|
||||
font-size: .9em;
|
||||
}
|
||||
pre > code { display: block; line-height: 1.025rem; padding-left: 1em; background: #181e19 }
|
||||
h1, h2, h3, h4, h5, h6 { font-weight: bold; }
|
||||
h1 { font-size: 1.6rem; }
|
||||
h2 { font-size: 1.4rem; }
|
||||
h3 { font-size: 1.2rem; }
|
||||
h4 { font-size: 1rem; }
|
||||
h5 { font-size: .8rem; }
|
||||
h6 { font-size: .7rem; }
|
||||
h1 { padding: .5rem; border-right: 1px solid #B0D944; border-bottom: 1px solid #B0D944; display: inline-block; padding-right: 0.75em; padding-left: 0.5em; }
|
||||
</style>
|
||||
|
||||
<div>
|
||||
<h1>Developer Tooling</h1>
|
||||
<div class="panels-wrapper">
|
||||
<div class="panel-left">
|
||||
<h2>Remotely inspect Pear applications.</h2>
|
||||
<p> Some application setup is required to enable remote debugging </p>
|
||||
<p> Install the <code>pear-inspect</code> module into the application </p>
|
||||
<pre><code>npm install pear-inspect</code></pre>
|
||||
<p> Add the following code to the application, before any other code: </p>
|
||||
|
||||
<pre><code>if (Pear.config.dev) {
|
||||
const { Inspector } = await import('pear-inspect')
|
||||
const inspector = await new Inspector()
|
||||
const key = await inspector.enable()
|
||||
console.log('Debug with pear://runtime/devtools/'
|
||||
+ key.toString('hex'))
|
||||
}</code></pre>
|
||||
|
||||
<p>When the application is opened in development mode the inspection key will be logged.</p>
|
||||
<p>Paste the logged key into the input to add it. Then open in Chrome or copy the URL to a compatible inspect tool to view the remote target.</p>
|
||||
</div>
|
||||
<div class="panel-right">
|
||||
<div>
|
||||
<form id="add-key-form">
|
||||
<input id="add-key-input" type="text" placeholder="Paste Pear Inspector Key Here"/>
|
||||
<p id="add-key-error"></p>
|
||||
</form>
|
||||
</div>
|
||||
<h2>Apps</h2>
|
||||
<h3 id="no-apps">No apps added. Add an inspect key to start debugging.</h3>
|
||||
<div id="apps"></div>
|
||||
<div id="server-message" class="hidden">
|
||||
Pear DevTools connection running on
|
||||
<div>
|
||||
<span id="server-location">localhost</span>
|
||||
<span id="change-port-show" class="button">
|
||||
Change port
|
||||
</span>
|
||||
<div>
|
||||
<form id="change-port-form" class="hidden">
|
||||
<input
|
||||
id="change-port-input"
|
||||
type="number"
|
||||
placeholder="New port"
|
||||
min="1024"
|
||||
max="65535"
|
||||
/>
|
||||
</form>
|
||||
<p id="change-port-error"></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
this.root = this.attachShadow({ mode: 'open' })
|
||||
this.root.appendChild(this.template.content.cloneNode(true))
|
||||
|
||||
this.addKeyFormElem = this.root.querySelector('#add-key-form')
|
||||
this.addKeyInputElem = this.root.querySelector('#add-key-input')
|
||||
this.addKeyErrorElem = this.root.querySelector('#add-key-error')
|
||||
this.changePortFormElem = this.root.querySelector('#change-port-form')
|
||||
this.changePortInputElem = this.root.querySelector('#change-port-input')
|
||||
this.changePortErrorElem = this.root.querySelector('#change-port-error')
|
||||
this.changePortShowElem = this.root.querySelector('#change-port-show')
|
||||
this.appsElem = this.root.querySelector('#apps')
|
||||
this.noAppsElem = this.root.querySelector('#no-apps')
|
||||
this.changePortElem = this.root.querySelector('#change-port')
|
||||
this.apps = new Map()
|
||||
|
||||
this.addKeyInputElem.addEventListener('keydown', e => {
|
||||
this.addKeyErrorElem.textContent = ''
|
||||
})
|
||||
this.addKeyFormElem.addEventListener('submit', e => {
|
||||
e.preventDefault()
|
||||
const inspectorKey = this.addKeyInputElem.value
|
||||
this.addApp(inspectorKey)
|
||||
})
|
||||
|
||||
this.changePortInputElem.addEventListener('keydown', e => {
|
||||
const isEscape = e.key === 'Escape'
|
||||
this.changePortErrorElem.textContent = ''
|
||||
if (isEscape) {
|
||||
this.changePortFormElem.classList.add('hidden')
|
||||
this.changePortInputElem.value = ''
|
||||
}
|
||||
})
|
||||
this.changePortShowElem.addEventListener('click', () => {
|
||||
this.changePortFormElem.classList.remove('hidden')
|
||||
})
|
||||
this.changePortFormElem.addEventListener('submit', e => {
|
||||
e.preventDefault()
|
||||
this.port = Number(this.changePortInputElem.value)
|
||||
this.initServer()
|
||||
})
|
||||
|
||||
const shouldLoadApp = Pear.config.linkData?.startsWith('devtools/')
|
||||
if (shouldLoadApp) {
|
||||
this.addApp(Pear.config.linkData)
|
||||
}
|
||||
|
||||
this.initServer()
|
||||
}
|
||||
|
||||
render () {
|
||||
this.appsElem.replaceChildren(...[...this.apps].map(([sessionId, app]) => {
|
||||
const div = document.createElement('div')
|
||||
div.innerHTML = `
|
||||
<div class="app">
|
||||
<div class="title">${app.title} (${app.url})</div>
|
||||
<div class="button copy">Copy URL</div>
|
||||
<div class="button open-in-chrome">Open in Chrome</div>
|
||||
<div class="button remove">✕</div>
|
||||
</div>
|
||||
`
|
||||
div.querySelector('.copy').addEventListener('click', () => {
|
||||
navigator.clipboard.writeText(`devtools://devtools/bundled/js_app.html?experiments=true&v8only=true&ws=127.0.0.1:${this.port}/${sessionId}`)
|
||||
})
|
||||
div.querySelector('.open-in-chrome').addEventListener('click', () => {
|
||||
openChrome(`devtools://devtools/bundled/js_app.html?experiments=true&v8only=true&ws=127.0.0.1:${this.port}/${sessionId}`)
|
||||
})
|
||||
div.querySelector('.remove').addEventListener('click', () => {
|
||||
this.apps.delete(sessionId)
|
||||
this.render()
|
||||
})
|
||||
|
||||
return div
|
||||
}))
|
||||
|
||||
if (this.apps.size > 0) {
|
||||
this.noAppsElem.classList.add('hidden')
|
||||
} else {
|
||||
this.noAppsElem.classList.remove('hidden')
|
||||
}
|
||||
|
||||
if (this.port) {
|
||||
this.root.querySelector('#server-location').textContent = `http://localhost:${this.port}`
|
||||
this.root.querySelector('#server-message').classList.remove('hidden')
|
||||
}
|
||||
}
|
||||
|
||||
addApp (inspectorKey) {
|
||||
const isUrl = inspectorKey.includes('devtools/')
|
||||
if (isUrl) inspectorKey = inspectorKey.split('devtools/').pop()
|
||||
const isIncorrectLength = inspectorKey.length !== 64
|
||||
if (isIncorrectLength) {
|
||||
this.addKeyErrorElem.textContent = 'Key needs to be 64 characters long'
|
||||
return
|
||||
}
|
||||
|
||||
const sessionId = generateUuid()
|
||||
const inspectorSession = new Session({ inspectorKey: b4a.from(inspectorKey, 'hex') })
|
||||
const app = {
|
||||
inspectorKey,
|
||||
title: '',
|
||||
url: '',
|
||||
inspectorSession
|
||||
}
|
||||
|
||||
inspectorSession.on('close', () => {
|
||||
this.apps.delete(sessionId)
|
||||
this.render()
|
||||
})
|
||||
inspectorSession.on('info', ({ filename }) => {
|
||||
app.url = filename
|
||||
app.title = filename.split('/').pop()
|
||||
this.apps.set(sessionId, app)
|
||||
this.render()
|
||||
})
|
||||
|
||||
this.addKeyInputElem.value = ''
|
||||
this.addKeyErrorElem.textContent = ''
|
||||
this.render()
|
||||
}
|
||||
|
||||
initServer () {
|
||||
this.devtoolsHttpServer?.close()
|
||||
|
||||
this.devtoolsHttpServer = http.createServer()
|
||||
const devToolsWsServer = new WebSocketServer({ noServer: true })
|
||||
|
||||
this.devtoolsHttpServer.listen(this.port, () => {
|
||||
console.log(`[devtoolsHttpServer] running on port ${this.port}`)
|
||||
this.render()
|
||||
this.changePortFormElem.classList.add('hidden')
|
||||
})
|
||||
this.devtoolsHttpServer.on('error', err => {
|
||||
this.changePortErrorElem.textContent = err?.message
|
||||
})
|
||||
this.devtoolsHttpServer.on('request', (req, res) => {
|
||||
if (req.url !== '/json/list') {
|
||||
res.writeHead(404)
|
||||
res.end()
|
||||
return
|
||||
}
|
||||
|
||||
const targets = [...this.apps].map(([sessionId, app]) => ({
|
||||
description: 'node.js instance', // `Pear app: ${app.name}`,
|
||||
devtoolsFrontendUrl: `devtools://devtools/bundled/js_app.html?experiments=true&v8only=true&ws=127.0.0.1:${this.port}/${sessionId}`,
|
||||
devtoolsFrontendUrlCompat: `devtools://devtools/bundled/inspector.html?experiments=true&v8only=true&ws=127.0.0.1:${this.port}/${sessionId}`,
|
||||
faviconUrl: 'https://nodejs.org/static/images/favicons/favicon.ico',
|
||||
id: sessionId,
|
||||
title: app.title,
|
||||
type: 'node',
|
||||
url: `file://${app.url}`,
|
||||
webSocketDebuggerUrl: `ws://127.0.0.1:${this.port}/${sessionId}`
|
||||
}))
|
||||
|
||||
res.writeHead(200, {
|
||||
'Content-Type': 'application/json; charset=UTF-8',
|
||||
'Cache-Control': 'no-cache',
|
||||
'Content-Length': JSON.stringify(targets).length
|
||||
})
|
||||
res.end(JSON.stringify(targets))
|
||||
})
|
||||
this.devtoolsHttpServer.on('upgrade', (request, socket, head) => {
|
||||
console.log(`[devtoolsHttpServer] UPGRADE. url=${request.url}`)
|
||||
const sessionId = request.url.substr(1)
|
||||
const sessionIdExists = this.apps.has(sessionId)
|
||||
if (!sessionIdExists) return socket.destroy()
|
||||
|
||||
devToolsWsServer.handleUpgrade(request, socket, head, ws => devToolsWsServer.emit('connection', ws, request))
|
||||
})
|
||||
|
||||
devToolsWsServer.on('connection', (devtoolsSocket, request) => {
|
||||
const sessionId = request.url.substr(1)
|
||||
const app = this.apps.get(sessionId)
|
||||
if (!app) return devtoolsSocket.destroy()
|
||||
|
||||
const { inspectorSession } = app
|
||||
|
||||
const onMessage = msg => {
|
||||
const { pearInspectMethod } = msg
|
||||
const isACDPMessage = !pearInspectMethod
|
||||
|
||||
if (isACDPMessage) return devtoolsSocket.send(JSON.stringify(msg))
|
||||
}
|
||||
inspectorSession.connect()
|
||||
inspectorSession.on('message', onMessage)
|
||||
devtoolsSocket.on('message', msg => inspectorSession.post(JSON.parse(msg)))
|
||||
devtoolsSocket.on('close', () => {
|
||||
inspectorSession.disconnect()
|
||||
inspectorSession.off('message', onMessage)
|
||||
app.connected = false
|
||||
this.render()
|
||||
})
|
||||
|
||||
app.connected = true
|
||||
this.render()
|
||||
})
|
||||
}
|
||||
|
||||
load () {
|
||||
this.style.display = ''
|
||||
}
|
||||
|
||||
unload () {
|
||||
this.style.display = 'none'
|
||||
}
|
||||
})
|
||||
|
||||
// Can't use `uuid` module for some reason as it results in a throw with `crypto` when importing
|
||||
function generateUuid () {
|
||||
let result, i, j
|
||||
result = ''
|
||||
for (j = 0; j < 32; j++) {
|
||||
if (j === 8 || j === 12 || j === 16 || j === 20) {
|
||||
result = result + '-'
|
||||
}
|
||||
i = Math.floor(Math.random() * 16).toString(16)
|
||||
result = result + i
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
function openChrome (url) {
|
||||
const params = {
|
||||
darwin: ['open', '-a', 'Google Chrome', url],
|
||||
linux: ['google-chrome', url],
|
||||
win32: ['start', 'chrome', url]
|
||||
}[process.platform]
|
||||
|
||||
if (!params) throw new Error('Cannot open Chrome')
|
||||
|
||||
const [command, ...args] = params
|
||||
spawn(command, args)
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -1,234 +0,0 @@
|
||||
/* eslint-env browser */
|
||||
import os from 'os'
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import { spawn } from 'child_process'
|
||||
const { config } = Pear
|
||||
|
||||
const BIN = path.join(config.pearDir, 'bin')
|
||||
const isWin = process.platform === 'win32'
|
||||
|
||||
customElements.define('pear-welcome', class extends HTMLElement {
|
||||
constructor () {
|
||||
super()
|
||||
this.template = document.createElement('template')
|
||||
this.template.innerHTML = `
|
||||
<style>
|
||||
blockquote { outline: 1px solid #323532; margin-inline-start: 0; margin-inline-end: 0; display: block; margin-block-start: 1rem; margin-block-end: 0; padding: 1px; font-size: .825rem; margin-top: -1rem; margin-bottom: 1rem; border-radius: 2px; }
|
||||
blockquote::before { content: "✔"; float: left; font-size: 1.625rem; margin-left: 1rem; margin-right: 0.625rem; }
|
||||
video { float: right; outline: 1px solid #323532; border-radius: 2px; }
|
||||
#welcome { float: left; width: calc(100% - 420px); }
|
||||
code {
|
||||
background: #3a4816;
|
||||
color: #efeaea;
|
||||
padding: 0.25rem;
|
||||
padding-top: 0.1rem;
|
||||
padding-bottom: 0.15rem;
|
||||
font-family: 'overpass-mono';
|
||||
border-radius: 1px;
|
||||
}
|
||||
</style>
|
||||
<blockquote>
|
||||
<p> Pear is in the system PATH and ready to go .</p>
|
||||
</blockquote>
|
||||
<div id="welcome">
|
||||
<h2> Welcome... </h2>
|
||||
<p> ...to the Internet of Peers. <p>
|
||||
<p> Pear provides the <code>pear</code> Command-line Interface as the primary interface for developing, sharing & maintaining unstoppable peer-to-peer applications and systems. </p>
|
||||
<p> To get started, open a terminal, type <code>pear</code> and hit return. </p>
|
||||
</div>
|
||||
<video width="380" height="390" autoplay muted style="background:#000">
|
||||
<source src="./assets/usage.mp4" type="video/mp4">
|
||||
</video>
|
||||
`
|
||||
this.root = this.attachShadow({ mode: 'open' })
|
||||
|
||||
this.root.appendChild(this.template.content.cloneNode(true))
|
||||
}
|
||||
})
|
||||
|
||||
customElements.define('system-status', class extends HTMLElement {
|
||||
router = null
|
||||
|
||||
connectedCallback () {
|
||||
this.root.addEventListener('click', (evt) => {
|
||||
this.router.link(evt)
|
||||
})
|
||||
}
|
||||
|
||||
load () {
|
||||
this.style.display = ''
|
||||
}
|
||||
|
||||
unload () {
|
||||
this.style.display = 'none'
|
||||
}
|
||||
|
||||
constructor () {
|
||||
super()
|
||||
this.zsh = false
|
||||
this.bash = false
|
||||
this.statement = `export PATH="${BIN}":$PATH`
|
||||
this.stmtrx = new RegExp(`^export PATH="${BIN}":\\$PATH$`, 'm')
|
||||
this.shellProfiles = null
|
||||
this.root = this.attachShadow({ mode: 'open' })
|
||||
this.update = null
|
||||
this.#render()
|
||||
}
|
||||
|
||||
#render () {
|
||||
this.shadowRoot.innerHTML = `
|
||||
<div id="panel">
|
||||
<style>
|
||||
#panel { user-select: none; }
|
||||
blockquote { outline: 1px solid #323532; margin-inline-start: 0; margin-inline-end: 0; display: block; margin-block-start: 1rem; margin-block-end: 0; padding: 1px; font-size: .825rem; border-radius: 2px; }
|
||||
blockquote::before { content: "ℹ"; float: left; font-size: 1.625rem; margin-left: 1rem; margin-right: 0.625rem; }
|
||||
button { background: #151517; color: #B0D944; border: 1px solid; padding: .575em .65em; cursor: pointer; margin-top: 2rem; font-size: 1.20rem; }
|
||||
#tip { text-indent: 4px; margin-top: -.25rem }
|
||||
code {
|
||||
background: #3a4816;
|
||||
color: #efeaea;
|
||||
padding: 0.25rem;
|
||||
padding-top: 0.1rem;
|
||||
padding-bottom: 0.15rem;
|
||||
font-family: 'overpass-mono';
|
||||
border-radius: 1px;
|
||||
user-select: text;
|
||||
}
|
||||
#tip > p { margin-top: 6px; margin-bottom: 6px; padding: 0}
|
||||
#tip {
|
||||
margin-top: 3rem;
|
||||
}
|
||||
#update-button {
|
||||
position: fixed;
|
||||
left: 893px;
|
||||
top: 170px;
|
||||
width: 171px;
|
||||
}
|
||||
h1 {
|
||||
padding: 0.5rem;
|
||||
display: inline-block;
|
||||
padding-right: 0.75em;
|
||||
font-weight: bold;
|
||||
font-size: 2.46rem;
|
||||
margin-left: -0.7rem;
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 1.25rem;
|
||||
}
|
||||
</style>
|
||||
<h1>System Status</h1>
|
||||
<button id="update-button"> Update Available </button>
|
||||
${
|
||||
this.#installed()
|
||||
? '<pear-welcome></pear-welcome>'
|
||||
: `
|
||||
<blockquote>
|
||||
<p>Pear setup is nearly complete.</p>
|
||||
</blockquote>
|
||||
<p>To finish installing Pear Runtime set your system path to</p>
|
||||
<p><code>${BIN}</code></p>
|
||||
<p>${!isWin ? ' or click the button.' : ''}</p>
|
||||
${!isWin ? '<p><button id="setup-button"> Automatic Setup Completion </button><p>' : ''}
|
||||
`
|
||||
}
|
||||
</div>
|
||||
`
|
||||
|
||||
this.updateButton = this.shadowRoot.querySelector('#update-button')
|
||||
this.updateButton.style.display = 'none'
|
||||
|
||||
this.updateButton.addEventListener('mouseenter', (e) => {
|
||||
e.target.innerText = 'Click to Restart'
|
||||
})
|
||||
this.updateButton.addEventListener('mouseout', (e) => {
|
||||
e.target.innerText = 'Update Available'
|
||||
})
|
||||
|
||||
this.shadowRoot.querySelector('#setup-button')?.addEventListener('click', () => {
|
||||
this.#install().then(() => this.#render()).catch((err) => this.#error(err))
|
||||
})
|
||||
|
||||
Pear.updates((update) => {
|
||||
this.update = update
|
||||
this.updateButton.style.display = ''
|
||||
|
||||
if (this.updateButtonListener) {
|
||||
this.updateButton.removeEventListener('click', this.updateButtonListener)
|
||||
}
|
||||
|
||||
this.updateButtonListener = () => Pear.restart({ platform: this.update.app === false }).catch(console.error)
|
||||
this.updateButton.addEventListener('click', this.updateButtonListener)
|
||||
})
|
||||
}
|
||||
|
||||
#error (err) {
|
||||
console.error(err)
|
||||
}
|
||||
|
||||
#installed () {
|
||||
if (isWin === false) {
|
||||
let hasPear = false
|
||||
this.shellProfiles = {}
|
||||
for (const file of ['.zshrc', '.zshenv', '.zshprofile', '.zlogin', '.profile', '.bashrc']) {
|
||||
const filepath = path.join(os.homedir(), file)
|
||||
let contents = null
|
||||
try { contents = fs.readFileSync(filepath, { encoding: 'utf-8' }) } catch {}
|
||||
if (contents !== null) {
|
||||
this.shellProfiles[file] = { filepath, hasPear: this.stmtrx.test(contents) }
|
||||
hasPear = hasPear || this.shellProfiles[file].hasPear
|
||||
}
|
||||
}
|
||||
if (hasPear) process.env.PATH = `${BIN}:${process.env.PATH}`
|
||||
}
|
||||
|
||||
this.paths = process.env.PATH.split(path.delimiter)
|
||||
|
||||
return this.paths.some((bin) => {
|
||||
return bin === BIN && fs.existsSync(path.join(BIN, isWin ? 'pear.cmd' : 'pear'))
|
||||
})
|
||||
}
|
||||
|
||||
#install () {
|
||||
const runtime = path.join('..', 'current', 'by-arch', process.platform + '-' + process.arch, 'bin', 'pear-runtime')
|
||||
fs.mkdirSync(BIN, { recursive: true })
|
||||
if (isWin) {
|
||||
const ps1tmp = path.join(BIN, Math.floor(Math.random() * 1000) + '.pear')
|
||||
fs.writeFileSync(ps1tmp, `function pear { & "${runtime}" }; Export-ModuleMember -Function pear`)
|
||||
fs.renameSync(ps1tmp, path.join(BIN, 'pear.ps1'))
|
||||
const cmdtmp = path.join(BIN, Math.floor(Math.random() * 1000) + '.pear')
|
||||
fs.writeFileSync(cmdtmp, `@echo off\n"${runtime}" %*`)
|
||||
fs.renameSync(cmdtmp, path.join(BIN, 'pear.cmd'))
|
||||
return new Promise((resolve, reject) => {
|
||||
spawn('powershell', ['-Command', `[System.Environment]::SetEnvironmentVariables("PATH", "${BIN};${process.env.PATH}", "User")`]).on('exit', (code) => {
|
||||
if (code !== 0) {
|
||||
reject(new Error('Failed to set PATH'))
|
||||
return
|
||||
}
|
||||
process.env.PATH = `${BIN};${process.env.PATH}`
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
}
|
||||
const comment = '# Added by Pear Runtime, configures system with Pear CLI\n'
|
||||
const profiles = Object.values(this.shellProfiles)
|
||||
if (profiles.length > 0) {
|
||||
for (const { filepath, hasPear } of profiles) {
|
||||
if (hasPear === false) fs.writeFileSync(filepath, '\n' + comment + this.statement + '\n', { flag: 'a' })
|
||||
}
|
||||
} else {
|
||||
const bash = this.paths.some((bin) => fs.existsSync(path.join(bin, 'bash')))
|
||||
const zsh = this.paths.some((bin) => fs.existsSync(path.join(bin, 'zsh')))
|
||||
fs.writeFileSync(path.join(os.homedir(), bash ? '.bashrc' : '.profile'), this.statement + '\n', { flag: 'a' })
|
||||
if (zsh) fs.writeFileSync(path.join(os.homedir(), '.zshrc'), '\n' + comment + this.statement + '\n', { flag: 'a' })
|
||||
}
|
||||
|
||||
const pear = path.join(BIN, 'pear')
|
||||
const tmp = path.join(BIN, Math.floor(Math.random() * 1000) + '.pear')
|
||||
fs.symlinkSync(runtime, tmp)
|
||||
fs.renameSync(tmp, pear)
|
||||
fs.chmodSync(pear, 0o755)
|
||||
|
||||
process.env.PATH = `${BIN}:${process.env.PATH}`
|
||||
return Promise.resolve()
|
||||
}
|
||||
})
|
||||
33
package.json
33
package.json
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"name": "pear-desktop",
|
||||
"version": "1.0.0",
|
||||
"main": "index.html",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"test": "standard --fix",
|
||||
"check-urls": "node test/urls.tests.cjs"
|
||||
},
|
||||
"pear": {
|
||||
"gui": {
|
||||
"backgroundColor": "#151517",
|
||||
"height": 780,
|
||||
"width": 1120
|
||||
}
|
||||
},
|
||||
"standard": {
|
||||
"globals": [
|
||||
"Pear"
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"b4a": "^1.6.4",
|
||||
"bare-path": "^2.1.0",
|
||||
"pear-inspect": "^1.0.3",
|
||||
"redhat-overpass-font": "^1.0.0",
|
||||
"ws": "^8.16.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"brittle": "^3.4.0",
|
||||
"standard": "^17.1.0"
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
const { readdir, stat, readFile } = require('fs/promises')
|
||||
const test = require('brittle')
|
||||
const path = require('path')
|
||||
const https = require('https')
|
||||
|
||||
test('check that all urls can be reached', async (t) => {
|
||||
const docs = await readMarkdownFiles()
|
||||
|
||||
let urls = await Promise.all(docs.map(async (doc) => {
|
||||
const content = (await readFile(doc)).toString()
|
||||
const urlRegex = /(?<url>https?:\/\/[^\s)"'`]+)/gi
|
||||
return content.match(urlRegex)
|
||||
}))
|
||||
|
||||
urls = urls.flat().filter(u => u !== null).filter(u => !u.startsWith('http://localhost'))
|
||||
|
||||
const cache = new Map()
|
||||
const responses = await Promise.all(urls.map(async url => {
|
||||
if (cache.get(url)) return cache.get(url)
|
||||
const result = await checkUrl(url.trim())
|
||||
cache.set(url, result)
|
||||
return { result, url }
|
||||
}))
|
||||
|
||||
for (const response of responses) {
|
||||
t.ok(response.result, `${response.url} should return 200 code`)
|
||||
}
|
||||
})
|
||||
|
||||
async function readMarkdownFiles (folderPath = path.join(__dirname, '..')) {
|
||||
let result = []
|
||||
const files = await readdir(folderPath)
|
||||
|
||||
for (const file of files) {
|
||||
const filePath = path.join(folderPath, file)
|
||||
const stats = await stat(filePath)
|
||||
|
||||
if (!filePath.includes('node_modules') && stats.isDirectory()) {
|
||||
result = result.concat(await readMarkdownFiles(filePath))
|
||||
} else if (path.extname(filePath) === '.md') {
|
||||
result.push(filePath)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
function checkUrl (url) {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
https.get(url, (res) => {
|
||||
if (res.statusCode >= 200 && res.statusCode < 300) {
|
||||
resolve(true)
|
||||
} else {
|
||||
resolve(false)
|
||||
}
|
||||
}).on('error', () => {
|
||||
resolve(false)
|
||||
})
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
resolve(false)
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user