1st commit of dsnt changes
This commit is contained in:
parent
053a1c8ed2
commit
ad30d14a66
21 changed files with 2902 additions and 72 deletions
19
package-lock.json
generated
19
package-lock.json
generated
|
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "nostri.chat",
|
"name": "dnst",
|
||||||
"version": "0.1.5",
|
"version": "0.3.14159",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "nostri.chat",
|
"name": "dsnt",
|
||||||
"version": "0.1.5",
|
"version": "0.3.14159",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nostr-connect/connect": "^0.2.3",
|
"@nostr-connect/connect": "^0.2.3",
|
||||||
"@nostr-dev-kit/ndk": "^0.3.32",
|
"@nostr-dev-kit/ndk": "^0.3.32",
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
"@sveltejs/adapter-node": "^1.1.7",
|
"@sveltejs/adapter-node": "^1.1.7",
|
||||||
"@tailwindcss/forms": "^0.5.3",
|
"@tailwindcss/forms": "^0.5.3",
|
||||||
"@tailwindcss/typography": "^0.5.9",
|
"@tailwindcss/typography": "^0.5.9",
|
||||||
|
"dayjs": "^1.11.9",
|
||||||
"emoji-regex": "^10.2.1",
|
"emoji-regex": "^10.2.1",
|
||||||
"eventemitter3": "^5.0.0",
|
"eventemitter3": "^5.0.0",
|
||||||
"light-bolt11-decoder": "^3.0.0",
|
"light-bolt11-decoder": "^3.0.0",
|
||||||
|
|
@ -2440,6 +2441,11 @@
|
||||||
"node": ">= 12"
|
"node": ">= 12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/dayjs": {
|
||||||
|
"version": "1.11.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.9.tgz",
|
||||||
|
"integrity": "sha512-QvzAURSbQ0pKdIye2txOzNaHmxtUBXerpY0FJsFXUMKbIZeFm5ht1LS/jFsrncjnmtv8HsG0W2g6c0zUjZWmpA=="
|
||||||
|
},
|
||||||
"node_modules/debug": {
|
"node_modules/debug": {
|
||||||
"version": "4.3.4",
|
"version": "4.3.4",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||||
|
|
@ -10273,6 +10279,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
|
||||||
"integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A=="
|
"integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A=="
|
||||||
},
|
},
|
||||||
|
"dayjs": {
|
||||||
|
"version": "1.11.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.9.tgz",
|
||||||
|
"integrity": "sha512-QvzAURSbQ0pKdIye2txOzNaHmxtUBXerpY0FJsFXUMKbIZeFm5ht1LS/jFsrncjnmtv8HsG0W2g6c0zUjZWmpA=="
|
||||||
|
},
|
||||||
"debug": {
|
"debug": {
|
||||||
"version": "4.3.4",
|
"version": "4.3.4",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"name": "nostri.chat",
|
"name": "dsnt",
|
||||||
"version": "0.3.14159",
|
"version": "0.3.14159",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite dev",
|
"dev": "vite dev",
|
||||||
|
|
@ -34,6 +34,7 @@
|
||||||
"@sveltejs/adapter-node": "^1.1.7",
|
"@sveltejs/adapter-node": "^1.1.7",
|
||||||
"@tailwindcss/forms": "^0.5.3",
|
"@tailwindcss/forms": "^0.5.3",
|
||||||
"@tailwindcss/typography": "^0.5.9",
|
"@tailwindcss/typography": "^0.5.9",
|
||||||
|
"dayjs": "^1.11.9",
|
||||||
"emoji-regex": "^10.2.1",
|
"emoji-regex": "^10.2.1",
|
||||||
"eventemitter3": "^5.0.0",
|
"eventemitter3": "^5.0.0",
|
||||||
"light-bolt11-decoder": "^3.0.0",
|
"light-bolt11-decoder": "^3.0.0",
|
||||||
|
|
|
||||||
34
src/Brand.svelte
Normal file
34
src/Brand.svelte
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
<p class="logo">
|
||||||
|
<span>D</span><span class="letter--dim">i</span><span>s</span><span class="letter--dim">se</span><span>nt</span>
|
||||||
|
</p>
|
||||||
|
<p class="motto">The web’s comment section.</p>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
|
||||||
|
p {
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
font-family: 'Koulen', sans-serif;
|
||||||
|
letter-spacing: -.1em;
|
||||||
|
color: var(--c-bright);
|
||||||
|
font-size: 82px;
|
||||||
|
margin: 0;
|
||||||
|
line-height: .8;
|
||||||
|
}
|
||||||
|
.logo span{
|
||||||
|
z-index: 1;
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
.logo span.letter--dim{
|
||||||
|
color: #cc9680;
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.motto{
|
||||||
|
color: var(--c-2);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -239,7 +239,7 @@
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!name) { name = `[${pubkey.slice(0, 6)}]`; }
|
if (!name) { name = `Anonymous [${pubkey.slice(0, 6)}]`; }
|
||||||
|
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
@ -321,6 +321,8 @@
|
||||||
{#if event.deleted}
|
{#if event.deleted}
|
||||||
👆 deleted
|
👆 deleted
|
||||||
{/if}
|
{/if}
|
||||||
|
{:else}
|
||||||
|
<p>no comments</p>
|
||||||
{/each}
|
{/each}
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -328,7 +330,7 @@
|
||||||
|
|
||||||
|
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<div class="
|
<!-- <div class="
|
||||||
border-y border-y-slate-200
|
border-y border-y-slate-200
|
||||||
-mx-4 my-2 bg-slate-100 text-black text-sm
|
-mx-4 my-2 bg-slate-100 text-black text-sm
|
||||||
px-4 py-2
|
px-4 py-2
|
||||||
|
|
@ -343,7 +345,7 @@
|
||||||
<b>Public notes:</b>
|
<b>Public notes:</b>
|
||||||
your followers see your messages on their timeline
|
your followers see your messages on their timeline
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div> -->
|
||||||
|
|
||||||
<div class="flex flex-row gap-2 -mx-1">
|
<div class="flex flex-row gap-2 -mx-1">
|
||||||
<textarea
|
<textarea
|
||||||
|
|
@ -357,7 +359,7 @@
|
||||||
rounded-xl
|
rounded-xl
|
||||||
text-gray-600
|
text-gray-600
|
||||||
border
|
border
|
||||||
" placeholder="Say hello!"
|
" placeholder="leave a comment"
|
||||||
rows=1
|
rows=1
|
||||||
on:keydown={inputKeyDown}
|
on:keydown={inputKeyDown}
|
||||||
></textarea>
|
></textarea>
|
||||||
|
|
|
||||||
480
src/ConnectedWidget2.svelte
Normal file
480
src/ConnectedWidget2.svelte
Normal file
|
|
@ -0,0 +1,480 @@
|
||||||
|
<script>
|
||||||
|
import {
|
||||||
|
chatAdapter,
|
||||||
|
chatData,
|
||||||
|
selectedMessage,
|
||||||
|
zapsPerMessage,
|
||||||
|
} from "./lib/store";
|
||||||
|
import { onMount } from "svelte";
|
||||||
|
import NostrNote from "./NostrNote2.svelte";
|
||||||
|
import * as animateScroll from "svelte-scrollto";
|
||||||
|
|
||||||
|
let events = [];
|
||||||
|
let responseEvents = [];
|
||||||
|
let responses = {};
|
||||||
|
let profiles = {};
|
||||||
|
|
||||||
|
export let websiteOwnerPubkey;
|
||||||
|
export let chatConfiguration;
|
||||||
|
let prevChatConfiguration;
|
||||||
|
|
||||||
|
$: {
|
||||||
|
if (chatConfiguration !== prevChatConfiguration && $chatAdapter) {
|
||||||
|
$chatAdapter.setChatConfiguration(
|
||||||
|
chatConfiguration.chatType,
|
||||||
|
chatConfiguration.chatTags,
|
||||||
|
chatConfiguration.chatReferenceTags,
|
||||||
|
chatConfiguration.chatId
|
||||||
|
);
|
||||||
|
events = [];
|
||||||
|
responses = {};
|
||||||
|
rootNoteId = null;
|
||||||
|
localStorage.removeItem("rootNoteId");
|
||||||
|
|
||||||
|
// rootNoteId = localStorage.getItem('rootNoteId');
|
||||||
|
// if (rootNoteId) {
|
||||||
|
// $chatAdapter.subscribeToEventAndResponses(rootNoteId);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
prevChatConfiguration = chatConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getEventById(eventId) {
|
||||||
|
let event = events.find((e) => e.id === eventId);
|
||||||
|
event = event || responseEvents.find((e) => e.id === eventId);
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function sendMessage() {
|
||||||
|
const input = document.getElementById("message-input");
|
||||||
|
const message = input.value;
|
||||||
|
input.value = "";
|
||||||
|
let extraParams = { tags: [], tagPubKeys: [] };
|
||||||
|
|
||||||
|
// if this is the rootLevel we want to tag the owner of the site's pubkey
|
||||||
|
if (!rootNoteId && websiteOwnerPubkey) {
|
||||||
|
extraParams.tagPubKeys = [websiteOwnerPubkey];
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we are responding to an event, we want to tag the event and the pubkey
|
||||||
|
if ($selectedMessage) {
|
||||||
|
extraParams.tags.push(["e", $selectedMessage, "wss://nos.lol", "root"]);
|
||||||
|
extraParams.tagPubKeys.push(getEventById($selectedMessage).pubkey);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (rootNoteId) {
|
||||||
|
// // mark it as a response to the most recent event
|
||||||
|
// const mostRecentEvent = events[events.length - 1];
|
||||||
|
// // go through all the tags and add them to the new message
|
||||||
|
// if (mostRecentEvent) {
|
||||||
|
// mostRecentEvent.tags.forEach(tag => {
|
||||||
|
// if (tag[0] === 'e') {
|
||||||
|
// extraParams.tags.push(tag);
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// extraParams.tags.push(['e', mostRecentEvent.id]);
|
||||||
|
// extraParams.tags.push(['p', mostRecentEvent.pubkey]);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
const noteId = await $chatAdapter.send(message, extraParams);
|
||||||
|
|
||||||
|
if (!rootNoteId) {
|
||||||
|
rootNoteId = noteId;
|
||||||
|
localStorage.setItem("rootNoteId", rootNoteId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function inputKeyDown(event) {
|
||||||
|
if (event.key === "Enter") {
|
||||||
|
sendMessage();
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function messageReceived(message) {
|
||||||
|
const messageLastEventTag = message.tags
|
||||||
|
.filter((tag) => tag[0] === "e")
|
||||||
|
.pop();
|
||||||
|
let isThread;
|
||||||
|
|
||||||
|
if (chatConfiguration.chatType === "GLOBAL") {
|
||||||
|
isThread = message.tags.filter((tag) => tag[0] === "e").length >= 1;
|
||||||
|
} else if (chatConfiguration.chatType === "GROUP") {
|
||||||
|
isThread =
|
||||||
|
message.tags.filter(
|
||||||
|
(tag) => tag[0] === "e" && tag[1] !== chatConfiguration.chatId
|
||||||
|
).length >= 1;
|
||||||
|
} else {
|
||||||
|
const pubkeysTagged = message.tags
|
||||||
|
.filter((tag) => tag[0] === "p")
|
||||||
|
.map((tag) => tag[1]);
|
||||||
|
isThread = new Set(pubkeysTagged).size >= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!responses[message.id]) {
|
||||||
|
responses[message.id] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isThread) {
|
||||||
|
// get the last "e" tag, which is tagging the immediate parent
|
||||||
|
const lastETag = message.tags.filter((tag) => tag[0] === "e").pop();
|
||||||
|
if (lastETag && lastETag[1]) {
|
||||||
|
// if there is one, add it to the response
|
||||||
|
if (!responses[lastETag[1]]) {
|
||||||
|
responses[lastETag[1]] = [];
|
||||||
|
}
|
||||||
|
responses[lastETag[1]].push(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
responseEvents.push(message);
|
||||||
|
responseEvents = responseEvents;
|
||||||
|
} else {
|
||||||
|
// insert message so that it's chronologically ordered by created_at
|
||||||
|
let index = 0;
|
||||||
|
while (
|
||||||
|
index < events.length &&
|
||||||
|
events[index].created_at < message.created_at
|
||||||
|
) {
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
events.splice(index, 0, message);
|
||||||
|
events = events;
|
||||||
|
}
|
||||||
|
|
||||||
|
responses = responses;
|
||||||
|
|
||||||
|
scrollDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
function scrollDown() {
|
||||||
|
animateScroll.scrollToBottom({
|
||||||
|
container: document.getElementById("messages-container"),
|
||||||
|
offset: 999999, // hack, oh well, browsers suck
|
||||||
|
duration: 50,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function zapReceived(zap) {
|
||||||
|
const event = events.find((event) => event.id === zap.zappedEvent);
|
||||||
|
if (!event) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$zapsPerMessage[event.id]) $zapsPerMessage[event.id] = [];
|
||||||
|
$zapsPerMessage[event.id].push(zap);
|
||||||
|
}
|
||||||
|
|
||||||
|
function reactionReceived(reaction) {
|
||||||
|
const event = events.find((event) => event.id === reaction.id);
|
||||||
|
if (!event) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
event.reactions = event.reactions || [];
|
||||||
|
event.reactions.push(reaction);
|
||||||
|
events = events;
|
||||||
|
}
|
||||||
|
|
||||||
|
let rootNoteId;
|
||||||
|
let channelMetadata = {};
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
$chatAdapter.on("message", messageReceived);
|
||||||
|
|
||||||
|
$chatAdapter.on("connectivity", (e) => {
|
||||||
|
connectivityStatus = e;
|
||||||
|
});
|
||||||
|
|
||||||
|
$chatAdapter.on("reaction", reactionReceived);
|
||||||
|
$chatAdapter.on("zap", zapReceived);
|
||||||
|
$chatAdapter.on("deleted", (deletedEvents) => {
|
||||||
|
deletedEvents.forEach((deletedEventId) => {
|
||||||
|
const index = events.findIndex((event) => event.id === deletedEventId);
|
||||||
|
if (index !== -1) {
|
||||||
|
events[index].deleted = true;
|
||||||
|
events = events;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$chatAdapter.on("profile", ({ pubkey, profile }) => {
|
||||||
|
let profiles = $chatData.profiles;
|
||||||
|
profiles[pubkey] = profile;
|
||||||
|
|
||||||
|
chatData.set({ profiles, ...$chatData });
|
||||||
|
});
|
||||||
|
|
||||||
|
$chatAdapter.on("channelMetadata", (event) => {
|
||||||
|
channelMetadata = JSON.parse(event.content);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
let connectivityStatus = {};
|
||||||
|
let connectedRelays = 0;
|
||||||
|
let totalRelays = 0;
|
||||||
|
|
||||||
|
$: {
|
||||||
|
connectedRelays = Object.values(connectivityStatus).filter(
|
||||||
|
(status) => status === "connected"
|
||||||
|
).length;
|
||||||
|
totalRelays = Object.values(connectivityStatus).length;
|
||||||
|
|
||||||
|
if ($chatAdapter?.pubkey && !profiles[$chatAdapter.pubkey]) {
|
||||||
|
$chatAdapter.reqProfile($chatAdapter.pubkey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let connectedChatId;
|
||||||
|
|
||||||
|
$: if (connectedChatId !== $chatAdapter?.chatId) {
|
||||||
|
connectedChatId = $chatAdapter?.chatId;
|
||||||
|
channelMetadata = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
$: profiles = $chatData.profiles;
|
||||||
|
|
||||||
|
function selectParent() {
|
||||||
|
if (chatConfiguration.chatType === "GROUP") {
|
||||||
|
$selectedMessage = null;
|
||||||
|
} else {
|
||||||
|
// get the last tagged event in the tags array of the current $selectedMessage
|
||||||
|
const lastETag = getEventById($selectedMessage)
|
||||||
|
.tags.filter((tag) => tag[0] === "e")
|
||||||
|
.pop();
|
||||||
|
const lastETagId = lastETag && lastETag[1];
|
||||||
|
|
||||||
|
$selectedMessage = lastETagId;
|
||||||
|
}
|
||||||
|
|
||||||
|
scrollDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
let ownName;
|
||||||
|
$: ownName = $chatAdapter?.pubkey ? pubkeyName($chatAdapter.pubkey) : "";
|
||||||
|
|
||||||
|
$: profiles = $chatData.profiles;
|
||||||
|
|
||||||
|
$: profilePicture =
|
||||||
|
(profiles[$chatAdapter.pubkey] && profiles[$chatAdapter.pubkey].picture) ||
|
||||||
|
`https://robohash.org/${$chatAdapter.pubkey.slice(0, 1)}.png?set=set1`;
|
||||||
|
|
||||||
|
function pubkeyName(pubkey) {
|
||||||
|
let name;
|
||||||
|
|
||||||
|
if (profiles[$chatAdapter.pubkey]) {
|
||||||
|
let self = profiles[$chatAdapter.pubkey];
|
||||||
|
|
||||||
|
// https://xkcd.com/927/
|
||||||
|
name = self.display_name || self.displayName || self.name || self.nip05;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!name) {
|
||||||
|
name = `Anonymous [${pubkey.slice(0, 6)}]`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- TOP -->
|
||||||
|
<div class="toolbar">
|
||||||
|
{#if $chatAdapter?.pubkey}
|
||||||
|
<a class="toolbar__avatar" href="https://iris.to/{$chatAdapter.pubkey}">
|
||||||
|
<p class="">
|
||||||
|
{ownName}
|
||||||
|
</p>
|
||||||
|
<img src={profilePicture} alt="{ownName}'s avatar" />
|
||||||
|
</a>
|
||||||
|
{/if}
|
||||||
|
<div class="toolbar__stats">
|
||||||
|
{#if events}
|
||||||
|
<p class="stats__count">
|
||||||
|
{events.length}
|
||||||
|
</p>
|
||||||
|
{/if}
|
||||||
|
<!-- <Relays bind:relays on:update={handleUpdate} /> -->
|
||||||
|
{#if totalRelays}
|
||||||
|
<div class="stats__relays">
|
||||||
|
{connectedRelays}/{totalRelays} relays
|
||||||
|
<div class="relay-dots">
|
||||||
|
{#each Array(totalRelays) as _, i}
|
||||||
|
<span
|
||||||
|
class="relay {connectedRelays > i ? 'relay--active' : ''}"
|
||||||
|
/>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- BOTTOM -->
|
||||||
|
<div id="messages-container" class="content content--scrolling">
|
||||||
|
<div class="events">
|
||||||
|
{#if $selectedMessage}
|
||||||
|
<NostrNote
|
||||||
|
event={getEventById($selectedMessage)}
|
||||||
|
{responses}
|
||||||
|
{websiteOwnerPubkey}
|
||||||
|
/>
|
||||||
|
{:else}
|
||||||
|
{#each events as event}
|
||||||
|
<NostrNote {event} {responses} {websiteOwnerPubkey} />
|
||||||
|
{#if event.deleted}
|
||||||
|
👆 deleted
|
||||||
|
{/if}
|
||||||
|
{:else}
|
||||||
|
<p>no comments</p>
|
||||||
|
{/each}
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if channelMetadata.name}
|
||||||
|
<div class="">
|
||||||
|
{#if channelMetadata.picture}
|
||||||
|
<img src={channelMetadata.picture} class="" />
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<div class="">
|
||||||
|
<div class="">{channelMetadata.name}</div>
|
||||||
|
{#if channelMetadata.about}
|
||||||
|
<div class="">{channelMetadata.about}</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if $selectedMessage}
|
||||||
|
{#if !getEventById($selectedMessage)}
|
||||||
|
<h1>Couldn't find event with ID {$selectedMessage}</h1>
|
||||||
|
{:else}
|
||||||
|
<div class="">
|
||||||
|
<a href="#" on:click|preventDefault={selectParent}>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke-width="1.5"
|
||||||
|
stroke="currentColor"
|
||||||
|
class=""
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
d="M19.5 12h-15m0 0l6.75 6.75M4.5 12l6.75-6.75"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<!-- <div class="flex flex-col ml-2">
|
||||||
|
<span class="text-lg text-black overflow-hidden whitespace-nowrap text-ellipsis">
|
||||||
|
{getEventById($selectedMessage).content}
|
||||||
|
</span>
|
||||||
|
</div> -->
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- MESSAGE INPUT -->
|
||||||
|
|
||||||
|
<div class="message-input">
|
||||||
|
<textarea
|
||||||
|
type="text"
|
||||||
|
id="message-input"
|
||||||
|
class=""
|
||||||
|
placeholder="leave a comment"
|
||||||
|
rows="1"
|
||||||
|
on:keydown={inputKeyDown}
|
||||||
|
/>
|
||||||
|
<button type="button" class="" on:click|preventDefault={sendMessage}>
|
||||||
|
<!-- Heroicon name: outline/plus -->
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class="w-6 h-6 rotate-90"
|
||||||
|
fill="currentColor"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
><path
|
||||||
|
d="M10.894 2.553a1 1 0 00-1.788 0l-7 14a1 1 0 001.169 1.409l5-1.429A1 1 0 009 15.571V11a1 1 0 112 0v4.571a1 1 0 00.725.962l5 1.428a1 1 0 001.17-1.408l-7-14z"
|
||||||
|
/></svg
|
||||||
|
>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
|
||||||
|
.content {
|
||||||
|
flex-grow: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-input {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-input textarea {
|
||||||
|
height: auto;
|
||||||
|
padding: 1rem;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-input button {
|
||||||
|
padding: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.events {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbar__avatar{
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 1rem;
|
||||||
|
color: var(--c-bright);
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbar__avatar img{
|
||||||
|
width: 2rem;
|
||||||
|
border-radius: 2rem;
|
||||||
|
outline: 1px solid var(--c-lines);
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbar__stats {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
flex-grow: 1;
|
||||||
|
align-items: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats__count{
|
||||||
|
font-family: 'Barlow Condensed', sans-serif;
|
||||||
|
font-size: 55px;
|
||||||
|
font-weight: 100;
|
||||||
|
line-height: .8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.relay-dots {
|
||||||
|
display: flex;
|
||||||
|
gap:.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.relay{
|
||||||
|
width: 13px;
|
||||||
|
height: 13px;
|
||||||
|
border-radius: 13px;
|
||||||
|
border: 1px solid var(--c-lines);
|
||||||
|
}
|
||||||
|
|
||||||
|
.relay--active{
|
||||||
|
border-color: var(--c-bright);
|
||||||
|
background-color: var(--c-marker);
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -106,94 +106,52 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<h1 class="font-bold text-xl mb-3">
|
<h1 class="">
|
||||||
How would you like to connect?
|
How would you like to connect?
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
{#if publicKey}
|
{#if publicKey}
|
||||||
<p class="text-gray-400 mb-3 font-bold">
|
<p class="">
|
||||||
Nostr Connect is a WIP, not fully implemented yet!
|
Nostr Connect is a WIP, not fully implemented yet!
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p class="text-gray-400 mb-3">
|
<p class="">
|
||||||
You are currently connected with the following public key:
|
You are currently connected with the following public key:
|
||||||
<span>{publicKey}</span>
|
<span>{publicKey}</span>
|
||||||
</p>
|
</p>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if nip46URI}
|
{#if nip46URI}
|
||||||
<p class="text-gray-600 mb-3">
|
<p class="">
|
||||||
Scan this with your Nostr Connect (click to copy to clipboard)
|
Scan this with your Nostr Connect (click to copy to clipboard)
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div class="bg-white w-full p-3"
|
<div class=""
|
||||||
on:click|preventDefault={Nip46Copy}>
|
on:click|preventDefault={Nip46Copy}>
|
||||||
<!-- <QR text={nip46URI} /> -->
|
<!-- <QR text={nip46URI} /> -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button class="
|
<button class="" on:click|preventDefault={() => { nip46URI = null; }}>
|
||||||
bg-purple-900
|
|
||||||
hover:bg-purple-700
|
|
||||||
w-full
|
|
||||||
p-2
|
|
||||||
rounded-xl
|
|
||||||
text-center
|
|
||||||
font-regular
|
|
||||||
text-white
|
|
||||||
" on:click|preventDefault={() => { nip46URI = null; }}>
|
|
||||||
Cancel
|
Cancel
|
||||||
</button>
|
</button>
|
||||||
{:else if !publicKey}
|
{:else if !publicKey}
|
||||||
<div class="flex flex-col gap-1">
|
<div class="">
|
||||||
{#if hasNostrNip07}
|
{#if hasNostrNip07}
|
||||||
<button class="
|
<button class="" on:click|preventDefault={useNip07}>
|
||||||
bg-purple-900
|
|
||||||
hover:bg-purple-700
|
|
||||||
w-full
|
|
||||||
p-4
|
|
||||||
rounded-xl
|
|
||||||
text-center
|
|
||||||
font-regular
|
|
||||||
text-gray-200
|
|
||||||
" on:click|preventDefault={useNip07}>
|
|
||||||
Browser Extension (NIP-07)
|
Browser Extension (NIP-07)
|
||||||
</button>
|
</button>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
|
||||||
<button class="
|
<button class="" on:click|preventDefault={useNip46}>
|
||||||
bg-purple-900
|
|
||||||
hover:bg-purple-700
|
|
||||||
w-full
|
|
||||||
p-4
|
|
||||||
rounded-xl
|
|
||||||
text-center
|
|
||||||
font-regular
|
|
||||||
text-gray-200
|
|
||||||
" on:click|preventDefault={useNip46}>
|
|
||||||
Nostr Connect (NIP-46)
|
Nostr Connect (NIP-46)
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button class="
|
<button class="" on:click|preventDefault={useDiscardableKeys}>
|
||||||
bg-purple-900
|
|
||||||
hover:bg-purple-700
|
|
||||||
w-full
|
|
||||||
p-4
|
|
||||||
rounded-xl
|
|
||||||
text-center
|
|
||||||
font-regular
|
|
||||||
text-gray-200
|
|
||||||
" on:click|preventDefault={useDiscardableKeys}>
|
|
||||||
Anonymous
|
Anonymous
|
||||||
<span class="text-xs text-gray-300">
|
<span class="">
|
||||||
(Ephemeral Keys)
|
(Ephemeral Keys)
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<style>
|
|
||||||
@tailwind base;
|
|
||||||
@tailwind components;
|
|
||||||
@tailwind utilities;
|
|
||||||
</style>
|
|
||||||
160
src/MetaData.svelte
Normal file
160
src/MetaData.svelte
Normal file
|
|
@ -0,0 +1,160 @@
|
||||||
|
<script>
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
|
||||||
|
export let url;
|
||||||
|
|
||||||
|
let title = null;
|
||||||
|
let description = null;
|
||||||
|
let thumbnail = null;
|
||||||
|
let publishDate = null;
|
||||||
|
let isLoading = true;
|
||||||
|
let isError = false;
|
||||||
|
let urlAbbr;
|
||||||
|
|
||||||
|
let imageLoaded = false;
|
||||||
|
let imageError = false;
|
||||||
|
|
||||||
|
function onImageLoad() {
|
||||||
|
imageLoaded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onImageError() {
|
||||||
|
imageError = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$: {
|
||||||
|
const cleanedUrl = typeof url === 'string' ? url.replace(/^https?:\/\//, '') : '';
|
||||||
|
urlAbbr = cleanedUrl.length > 20 ? cleanedUrl.substring(0, 20) + '...' : cleanedUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
$: fetchMetaData(url);
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
fetchMetaData(url);
|
||||||
|
});
|
||||||
|
|
||||||
|
async function fetchMetaData(url){
|
||||||
|
isError = false;
|
||||||
|
try {
|
||||||
|
const res = await fetch(url);
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new Error("Couldn't fetch URL");
|
||||||
|
}
|
||||||
|
const text = await res.text();
|
||||||
|
const parser = new DOMParser();
|
||||||
|
const doc = parser.parseFromString(text, 'text/html');
|
||||||
|
|
||||||
|
// Check multiple possible meta tags for each property
|
||||||
|
const titleTags = ['title', 'og:title', 'twitter:title'];
|
||||||
|
const descriptionTags = ['description', 'og:description', 'twitter:description'];
|
||||||
|
const thumbnailTags = ['image', 'og:image', 'twitter:image'];
|
||||||
|
const publishDateTags = ['article:published_time', 'published_time', 'date'];
|
||||||
|
|
||||||
|
title = getMetaContent(doc, titleTags);
|
||||||
|
description = getMetaContent(doc, descriptionTags);
|
||||||
|
thumbnail = getMetaContent(doc, thumbnailTags);
|
||||||
|
publishDate = getMetaContent(doc, publishDateTags);
|
||||||
|
} catch (error) {
|
||||||
|
isError = true;
|
||||||
|
console.log('Error fetching URL:', error);
|
||||||
|
} finally {
|
||||||
|
isLoading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMetaContent(doc, tagNames) {
|
||||||
|
for (const tagName of tagNames) {
|
||||||
|
const element = doc.querySelector(`meta[name="${tagName}"], meta[property="${tagName}"]`);
|
||||||
|
if (element && element.content) {
|
||||||
|
return element.content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
{#if isLoading}
|
||||||
|
<div class="metadata__text">
|
||||||
|
<h1>{urlAbbr}</h1>
|
||||||
|
<p>Loading...</p>
|
||||||
|
</div>
|
||||||
|
{:else if isError}
|
||||||
|
<div class="metadata__text">
|
||||||
|
<h1>{urlAbbr}</h1>
|
||||||
|
<p>There was a problem displaying info from this website.</p>
|
||||||
|
</div>
|
||||||
|
{:else if !title && !description && !thumbnail && !publishDate}
|
||||||
|
<div class="metadata__text">
|
||||||
|
<h1>{urlAbbr}</h1>
|
||||||
|
<p>No data found.</p>
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<div class="metadata__text">
|
||||||
|
{#if title}
|
||||||
|
<h1>{title}</h1>
|
||||||
|
{/if}
|
||||||
|
<a href="{url}" target="_blank" rel="noreferrer">{urlAbbr}</a>
|
||||||
|
{#if description}
|
||||||
|
<p>{description}</p>
|
||||||
|
{/if}
|
||||||
|
{#if publishDate}
|
||||||
|
<p>Published on: {new Date(publishDate).toLocaleDateString()}</p>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
<div class="metadata__thumbnail">
|
||||||
|
{#if thumbnail}
|
||||||
|
<img src="{thumbnail}" alt="Thumbnail for {title}" on:load={onImageLoad} on:error={onImageError} />
|
||||||
|
{imageLoaded}
|
||||||
|
{imageError}
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
|
||||||
|
<style>
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
/* font-size: 2.5rem; */
|
||||||
|
color: var(--c-3);
|
||||||
|
font-size: 1.5rem;
|
||||||
|
color: var(--c-3);
|
||||||
|
font-weight: 500;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
a{
|
||||||
|
color: var(--c-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
a:visited{
|
||||||
|
color: var(--c-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover{
|
||||||
|
color: var(--c-bright);
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
color: var(--c-3);
|
||||||
|
max-width: 60ch;
|
||||||
|
overflow: hidden;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
}
|
||||||
|
|
||||||
|
.metadata__text {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.metadata__thumbnail {
|
||||||
|
flex-grow:0;
|
||||||
|
width: fit-content;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
img{
|
||||||
|
max-height: 100%;
|
||||||
|
outline: 1px solid rgba(255, 255, 255, .5);
|
||||||
|
border-radius: .5rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -113,7 +113,7 @@
|
||||||
'w-full rounded-full bg-white drop-shadow-xl justify-between border-2 border-gray-200' :
|
'w-full rounded-full bg-white drop-shadow-xl justify-between border-2 border-gray-200' :
|
||||||
' rounded-full w-8 h-8 justify-center'
|
' rounded-full w-8 h-8 justify-center'
|
||||||
}
|
}
|
||||||
flex items-center absolute ml-5 mt-10 z-10">
|
flex items-center ml-5 mt-10 z-10">
|
||||||
{#if zappingIt}
|
{#if zappingIt}
|
||||||
{#if mobilePR}
|
{#if mobilePR}
|
||||||
<div class="flex flex-col gap-3 w-full">
|
<div class="flex flex-col gap-3 w-full">
|
||||||
|
|
|
||||||
325
src/NostrNote2.svelte
Normal file
325
src/NostrNote2.svelte
Normal file
|
|
@ -0,0 +1,325 @@
|
||||||
|
<script>
|
||||||
|
import { afterUpdate, onMount } from "svelte";
|
||||||
|
import { selectedMessage, zappingMessage, zapsPerMessage } from "./lib/store";
|
||||||
|
import { chatData, chatAdapter } from "./lib/store";
|
||||||
|
import { nip19 } from "nostr-tools";
|
||||||
|
import ZapAmountButton from "./ZapAmountButton.svelte";
|
||||||
|
// import { prettifyContent } from '$lib/utils';
|
||||||
|
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
import relativeTime from 'dayjs/plugin/relativeTime';
|
||||||
|
|
||||||
|
dayjs.extend(relativeTime);
|
||||||
|
|
||||||
|
export let event;
|
||||||
|
export let responses;
|
||||||
|
export let websiteOwnerPubkey;
|
||||||
|
|
||||||
|
let profiles = {};
|
||||||
|
let profilePicture;
|
||||||
|
let npub;
|
||||||
|
let zappingIt;
|
||||||
|
let hovering;
|
||||||
|
let mobilePR;
|
||||||
|
|
||||||
|
let relativeTimeFromNow;
|
||||||
|
|
||||||
|
let zappedAmount = 0;
|
||||||
|
|
||||||
|
function selectMessage() {
|
||||||
|
if ($selectedMessage === event.id) {
|
||||||
|
$selectedMessage = null;
|
||||||
|
} else {
|
||||||
|
$selectedMessage = event.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// delay-fetch responses
|
||||||
|
onMount(() => {
|
||||||
|
$chatAdapter.delayedSubscribe(
|
||||||
|
{ kinds: [1, 42, 9735], "#e": [event.id] },
|
||||||
|
"responses",
|
||||||
|
500
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const byWebsiteOwner = !!websiteOwnerPubkey === event.pubkey;
|
||||||
|
|
||||||
|
$: profiles = $chatData.profiles;
|
||||||
|
$: displayName =
|
||||||
|
(profiles[event.pubkey] && profiles[event.pubkey].display_name) ||
|
||||||
|
`[${event.pubkey.slice(0, 6)}]`;
|
||||||
|
// $: nip05 = profiles[event.pubkey] && profiles[event.pubkey].nip05;
|
||||||
|
$: zappingIt = $zappingMessage === event.id;
|
||||||
|
$: {
|
||||||
|
try {
|
||||||
|
npub = nip19.npubEncode(event.pubkey);
|
||||||
|
} catch (e) {
|
||||||
|
npub = event.pubkey;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$chatAdapter.on("zap", () => {
|
||||||
|
zappedAmount =
|
||||||
|
$zapsPerMessage[event.id]?.reduce((acc, zap) => acc + zap.amount, 0) || 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
$: {
|
||||||
|
zappedAmount =
|
||||||
|
$zapsPerMessage[event.id]?.reduce((acc, zap) => acc + zap.amount, 0) || 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
afterUpdate(() => {
|
||||||
|
zappedAmount =
|
||||||
|
$zapsPerMessage[event.id]?.reduce((acc, zap) => acc + zap.amount, 0) || 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
$: profilePicture =
|
||||||
|
(profiles[event.pubkey] && profiles[event.pubkey].picture) ||
|
||||||
|
`https://robohash.org/${event.pubkey.slice(0, 1)}.png?set=set1`;
|
||||||
|
|
||||||
|
// const repliedIds = event.tags.filter(e => e[0] === 'e').map(e => e[1]);
|
||||||
|
|
||||||
|
let timestamp = new Date(event.created_at * 1000);
|
||||||
|
|
||||||
|
$: {
|
||||||
|
const now = dayjs();
|
||||||
|
const then = dayjs(timestamp);
|
||||||
|
const diffInSeconds = now.diff(then, 'second');
|
||||||
|
const diffInMinutes = now.diff(then, 'minute');
|
||||||
|
const diffInHours = now.diff(then, 'hour');
|
||||||
|
const diffInDays = now.diff(then, 'day');
|
||||||
|
const diffInMonths = now.diff(then, 'month');
|
||||||
|
const diffInYears = now.diff(then, 'year');
|
||||||
|
|
||||||
|
if (diffInSeconds < 10) {
|
||||||
|
relativeTimeFromNow = "Now";
|
||||||
|
} else if (diffInSeconds < 60) {
|
||||||
|
relativeTimeFromNow = `${diffInSeconds}s`;
|
||||||
|
} else if (diffInMinutes < 60) {
|
||||||
|
relativeTimeFromNow = `${diffInMinutes}m`;
|
||||||
|
} else if (diffInHours < 24) {
|
||||||
|
relativeTimeFromNow = `${diffInHours}h`;
|
||||||
|
} else if (diffInDays < 365) {
|
||||||
|
relativeTimeFromNow = then.format('MMM D');
|
||||||
|
} else {
|
||||||
|
if (diffInYears >= 1 && diffInMonths >= 6) {
|
||||||
|
relativeTimeFromNow = then.format('MMM D, YYYY');
|
||||||
|
} else {
|
||||||
|
relativeTimeFromNow = then.format('MMM D');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<article class="event">
|
||||||
|
<div class="event__content">
|
||||||
|
<!-- AVATAR-->
|
||||||
|
<div class="event__avatar">
|
||||||
|
<a href={`nostr:${npub}`}
|
||||||
|
><img
|
||||||
|
src={profilePicture}
|
||||||
|
alt="{displayName}'s avatar"
|
||||||
|
/></a
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="zap-btn {zappedAmount > 0
|
||||||
|
? 'zap-btn--zapped'
|
||||||
|
: ''}"
|
||||||
|
on:click|preventDefault={() =>
|
||||||
|
($zappingMessage = $zappingMessage === event.id ? null : event.id)}
|
||||||
|
>
|
||||||
|
{#if zappedAmount > 0}
|
||||||
|
<p>
|
||||||
|
⚡️
|
||||||
|
<span>
|
||||||
|
{zappedAmount / 1000}
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
{:else}
|
||||||
|
⚡️
|
||||||
|
{/if}
|
||||||
|
</button>
|
||||||
|
<!-- <div
|
||||||
|
class="
|
||||||
|
{zappingIt
|
||||||
|
? 'w-full rounded-full bg-white drop-shadow-xl justify-between border-2 border-white/50'
|
||||||
|
: ' rounded-full w-8 h-8 justify-center'}
|
||||||
|
flex items-center ml-5 mt-10 z-10"
|
||||||
|
>
|
||||||
|
{#if zappingIt}
|
||||||
|
{#if mobilePR}
|
||||||
|
<div class="">
|
||||||
|
<a
|
||||||
|
href={`lightning:${mobilePR}`}
|
||||||
|
class=""
|
||||||
|
>Open in wallet</a
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class=""
|
||||||
|
on:click={() => {
|
||||||
|
$zappingMessage = null;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
<ZapAmountButton icon="👍" amount={500} {event} bind:mobilePR />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<ZapAmountButton
|
||||||
|
icon="🤙"
|
||||||
|
amount={2500}
|
||||||
|
amountDisplay={"2.5k"}
|
||||||
|
{event}
|
||||||
|
bind:mobilePR
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div >
|
||||||
|
<ZapAmountButton
|
||||||
|
icon="🙌"
|
||||||
|
amount={5000}
|
||||||
|
amountDisplay={"5k"}
|
||||||
|
{event}
|
||||||
|
bind:mobilePR
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<ZapAmountButton
|
||||||
|
icon="🧡"
|
||||||
|
amount={10000}
|
||||||
|
amountDisplay={"10k"}
|
||||||
|
{event}
|
||||||
|
bind:mobilePR
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<ZapAmountButton
|
||||||
|
icon="🤯"
|
||||||
|
amount={100000}
|
||||||
|
amountDisplay={"100k"}
|
||||||
|
{event}
|
||||||
|
bind:mobilePR
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<ZapAmountButton
|
||||||
|
icon="😎"
|
||||||
|
amount={1000000}
|
||||||
|
amountDisplay={"1M"}
|
||||||
|
{event}
|
||||||
|
bind:mobilePR
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{/if}
|
||||||
|
</div> -->
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- TEXT-->
|
||||||
|
<div class="event__text">
|
||||||
|
<header>
|
||||||
|
<h1>
|
||||||
|
{displayName}
|
||||||
|
</h1>
|
||||||
|
<span title="{timestamp.toLocaleString()}">
|
||||||
|
{relativeTimeFromNow}
|
||||||
|
</span>
|
||||||
|
</header>
|
||||||
|
<p class="event__message">
|
||||||
|
{event.content}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if responses[event.id].length > 0}
|
||||||
|
<div class="event__responses">
|
||||||
|
{#each responses[event.id] as response}
|
||||||
|
<svelte:self {websiteOwnerPubkey} event={response} {responses} />
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
|
||||||
|
.event{
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 1rem;
|
||||||
|
border-bottom: 1px solid var(--c-lines);
|
||||||
|
color: var(--c-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.event__content{
|
||||||
|
display: flex;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.event__avatar {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 1rem;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.event__avatar img {
|
||||||
|
aspect-ratio: 1;
|
||||||
|
width: 40px;
|
||||||
|
border-radius: 40px;
|
||||||
|
outline: 1px solid var(--c-lines);
|
||||||
|
}
|
||||||
|
|
||||||
|
.event__text {
|
||||||
|
flex-grow: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: .5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
header{
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
header h1 {
|
||||||
|
font-weight: 900;
|
||||||
|
font-size: 1rem;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
p{
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
header span{
|
||||||
|
margin-right: .5rem;
|
||||||
|
color: var(--c-bright);
|
||||||
|
}
|
||||||
|
|
||||||
|
.zap-btn {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
border-radius: 50px;
|
||||||
|
background-color: orange;
|
||||||
|
color: white;
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.event__responses{
|
||||||
|
border-left: 1px solid var(--c-lines);
|
||||||
|
margin-left: calc((40px - 1rem) / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.event__message {
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
line-height: 1.4em;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
47
src/Relays.svelte
Normal file
47
src/Relays.svelte
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
<script>
|
||||||
|
import { createEventDispatcher } from 'svelte';
|
||||||
|
|
||||||
|
// Accept relays as a prop
|
||||||
|
export let relays = [];
|
||||||
|
|
||||||
|
// Create a local copy
|
||||||
|
let localRelays = [...relays];
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher();
|
||||||
|
|
||||||
|
let showRelays = false;
|
||||||
|
let newRelay = '';
|
||||||
|
|
||||||
|
function toggleRelays() {
|
||||||
|
showRelays = !showRelays;
|
||||||
|
}
|
||||||
|
|
||||||
|
function addRelay() {
|
||||||
|
if (newRelay) {
|
||||||
|
localRelays.push(newRelay);
|
||||||
|
newRelay = '';
|
||||||
|
dispatch('update', localRelays);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeRelay(index) {
|
||||||
|
localRelays.splice(index, 1);
|
||||||
|
dispatch('update', localRelays);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<button on:click={toggleRelays}>Relays <span>{relays.length}</span></button>
|
||||||
|
|
||||||
|
{#if showRelays}
|
||||||
|
<ul>
|
||||||
|
{#each relays as relay, index}
|
||||||
|
<li>
|
||||||
|
{relay} <button on:click={() => removeRelay(index)}>Remove</button>
|
||||||
|
</li>
|
||||||
|
{/each}
|
||||||
|
<li>
|
||||||
|
<input type="text" bind:value={newRelay} placeholder="Add new relay..." />
|
||||||
|
<button on:click={addRelay}>Add</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
{/if}
|
||||||
124
src/app.css
Normal file
124
src/app.css
Normal file
|
|
@ -0,0 +1,124 @@
|
||||||
|
@import url("https://fonts.googleapis.com/css2?family=Work+Sans:wght@300;400;500;600;700&display=swap");
|
||||||
|
@import url("https://fonts.googleapis.com/css2?family=Barlow+Condensed:wght@100;300;400;500;600;700&display=swap");
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Koulen&display=swap');
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--c-lines: rgba(255, 255, 255, 0.3);
|
||||||
|
--c-2: #cc9680;
|
||||||
|
--c-3: rgba(255, 255, 255, 0.7);
|
||||||
|
--c-bright: white;
|
||||||
|
--c-marker: orange;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
main,
|
||||||
|
section,
|
||||||
|
div {
|
||||||
|
border: dotted orange .5px;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
main {
|
||||||
|
font-family: Work Sans, sans-serif;
|
||||||
|
display: flex;
|
||||||
|
height: 100vh;
|
||||||
|
overflow: hidden;
|
||||||
|
background: linear-gradient(
|
||||||
|
to bottom,
|
||||||
|
#381100 0%,
|
||||||
|
#a13000 99.98%,
|
||||||
|
#b93700 99.99%,
|
||||||
|
#ff4c00 100%
|
||||||
|
),
|
||||||
|
linear-gradient(to left, rgba(0, 0, 0, 0.2) 0%, rgba(0, 0, 0, 0.3) 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel {
|
||||||
|
flex-grow: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel .toolbar {
|
||||||
|
background-color: rgb(56, 17, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar {
|
||||||
|
flex-shrink: 0;
|
||||||
|
width: 566px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background-color: rgb(56, 17, 0, 0.2);
|
||||||
|
border-left: 1px solid var(--c-lines);
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbar {
|
||||||
|
display: flex;
|
||||||
|
flex-shrink: 0;
|
||||||
|
gap: 0.5rem;
|
||||||
|
padding: 1rem;
|
||||||
|
height: 10rem;
|
||||||
|
border-bottom: 1px solid var(--c-lines);
|
||||||
|
color: var(--c-bright);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar .toolbar {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
outline: 1px solid var(--c-lines);
|
||||||
|
color: var(--c-lines);
|
||||||
|
padding: 1em 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Webkit browsers like Chrome, Safari */
|
||||||
|
.content--scrolling::-webkit-scrollbar {
|
||||||
|
width: 1rem; /* Set the width */
|
||||||
|
}
|
||||||
|
|
||||||
|
.content--scrolling::-webkit-scrollbar-track {
|
||||||
|
background: transparent; /* Make the track transparent */
|
||||||
|
}
|
||||||
|
|
||||||
|
.content--scrolling::-webkit-scrollbar-thumb {
|
||||||
|
background: var(--c-lines); /* Set the thumb color */
|
||||||
|
}
|
||||||
|
|
||||||
|
.content--scrolling::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: var(--c-bright); /* Change the thumb color when hovered */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Internet Explorer */
|
||||||
|
.content--scrolling {
|
||||||
|
position: relative;
|
||||||
|
scrollbar-face-color: var(--c-lines);
|
||||||
|
scrollbar-track-color: transparent;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content--scrolling::before {
|
||||||
|
content: "";
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
width: 1px;
|
||||||
|
height: 100%;
|
||||||
|
background-color: var(--c-lines);
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
padding: 0.5em 1em;
|
||||||
|
background-color: transparent;
|
||||||
|
border: 1px solid var(--c-lines);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
color: var(--c-bright);
|
||||||
|
}
|
||||||
|
|
@ -6,10 +6,7 @@
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
%sveltekit.head%
|
%sveltekit.head%
|
||||||
</head>
|
</head>
|
||||||
<!-- bg-white dark:bg-black -->
|
<body>
|
||||||
<body class="
|
|
||||||
|
|
||||||
">
|
|
||||||
<div>%sveltekit.body%</div>
|
<div>%sveltekit.body%</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,49 @@
|
||||||
import { writable } from 'svelte/store';
|
import { writable } from 'svelte/store';
|
||||||
|
import { browser } from '$app/environment';
|
||||||
|
|
||||||
|
// Your other stores
|
||||||
export const chatAdapter = writable(null);
|
export const chatAdapter = writable(null);
|
||||||
export const chatData = writable({ events: [], profiles: {}});
|
export const chatData = writable({ events: [], profiles: {}});
|
||||||
export const selectedMessage = writable(null);
|
export const selectedMessage = writable(null);
|
||||||
export const zappingMessage = writable(null);
|
export const zappingMessage = writable(null);
|
||||||
export const zapsPerMessage = writable({});
|
export const zapsPerMessage = writable({});
|
||||||
|
|
||||||
|
// Default values
|
||||||
|
const defaultRelays = [
|
||||||
|
"wss://relay.f7z.io",
|
||||||
|
"wss://nos.lol",
|
||||||
|
"wss://relay.nostr.band",
|
||||||
|
"wss://nostr-pub.wellorder.net",
|
||||||
|
"wss://relay.damus.io",
|
||||||
|
"wss://relay.f7z.io",
|
||||||
|
"wss://eden.nostr.land",
|
||||||
|
"wss://offchain.pub",
|
||||||
|
"wss://soloco.nl"
|
||||||
|
];
|
||||||
|
|
||||||
|
// Read initial state from sessionStorage if available, otherwise use default values
|
||||||
|
const initialUrl = browser && sessionStorage.getItem('url') !== null
|
||||||
|
? sessionStorage.getItem('url')
|
||||||
|
: null;
|
||||||
|
|
||||||
|
const initialRelays = browser && sessionStorage.getItem('relays') !== null
|
||||||
|
? JSON.parse(sessionStorage.getItem('relays'))
|
||||||
|
: defaultRelays;
|
||||||
|
|
||||||
|
// Create the writable stores
|
||||||
|
export const url = writable(initialUrl);
|
||||||
|
export const relays = writable(initialRelays);
|
||||||
|
|
||||||
|
// Function to synchronize the URL with sessionStorage
|
||||||
|
url.subscribe((currentUrl) => {
|
||||||
|
if (browser) {
|
||||||
|
sessionStorage.setItem("url", currentUrl);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Function to synchronize the relays with sessionStorage
|
||||||
|
relays.subscribe((currentRelays) => {
|
||||||
|
if (browser) {
|
||||||
|
sessionStorage.setItem("relays", JSON.stringify(currentRelays));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
|
||||||
635
src/milligram.css
Normal file
635
src/milligram.css
Normal file
|
|
@ -0,0 +1,635 @@
|
||||||
|
/*!
|
||||||
|
* Milligram v1.4.1
|
||||||
|
* https://milligram.io
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020 CJ Patoilo
|
||||||
|
* Licensed under the MIT license
|
||||||
|
*/
|
||||||
|
|
||||||
|
*,
|
||||||
|
*:after,
|
||||||
|
*:before {
|
||||||
|
box-sizing: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-size: 62.5%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
color: #606c76;
|
||||||
|
font-family: 'Roboto', 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif;
|
||||||
|
font-size: 1.6em;
|
||||||
|
font-weight: 300;
|
||||||
|
letter-spacing: .01em;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote {
|
||||||
|
border-left: 0.3rem solid #d1d1d1;
|
||||||
|
margin-left: 0;
|
||||||
|
margin-right: 0;
|
||||||
|
padding: 1rem 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote *:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button,
|
||||||
|
button,
|
||||||
|
input[type='button'],
|
||||||
|
input[type='reset'],
|
||||||
|
input[type='submit'] {
|
||||||
|
background-color: #9b4dca;
|
||||||
|
border: 0.1rem solid #9b4dca;
|
||||||
|
border-radius: .4rem;
|
||||||
|
color: #fff;
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
font-weight: 700;
|
||||||
|
height: 3.8rem;
|
||||||
|
letter-spacing: .1rem;
|
||||||
|
line-height: 3.8rem;
|
||||||
|
padding: 0 3.0rem;
|
||||||
|
text-align: center;
|
||||||
|
text-decoration: none;
|
||||||
|
text-transform: uppercase;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button:focus, .button:hover,
|
||||||
|
button:focus,
|
||||||
|
button:hover,
|
||||||
|
input[type='button']:focus,
|
||||||
|
input[type='button']:hover,
|
||||||
|
input[type='reset']:focus,
|
||||||
|
input[type='reset']:hover,
|
||||||
|
input[type='submit']:focus,
|
||||||
|
input[type='submit']:hover {
|
||||||
|
background-color: #606c76;
|
||||||
|
border-color: #606c76;
|
||||||
|
color: #fff;
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button[disabled],
|
||||||
|
button[disabled],
|
||||||
|
input[type='button'][disabled],
|
||||||
|
input[type='reset'][disabled],
|
||||||
|
input[type='submit'][disabled] {
|
||||||
|
cursor: default;
|
||||||
|
opacity: .5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button[disabled]:focus, .button[disabled]:hover,
|
||||||
|
button[disabled]:focus,
|
||||||
|
button[disabled]:hover,
|
||||||
|
input[type='button'][disabled]:focus,
|
||||||
|
input[type='button'][disabled]:hover,
|
||||||
|
input[type='reset'][disabled]:focus,
|
||||||
|
input[type='reset'][disabled]:hover,
|
||||||
|
input[type='submit'][disabled]:focus,
|
||||||
|
input[type='submit'][disabled]:hover {
|
||||||
|
background-color: #9b4dca;
|
||||||
|
border-color: #9b4dca;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button.button-outline,
|
||||||
|
button.button-outline,
|
||||||
|
input[type='button'].button-outline,
|
||||||
|
input[type='reset'].button-outline,
|
||||||
|
input[type='submit'].button-outline {
|
||||||
|
background-color: transparent;
|
||||||
|
color: #9b4dca;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button.button-outline:focus, .button.button-outline:hover,
|
||||||
|
button.button-outline:focus,
|
||||||
|
button.button-outline:hover,
|
||||||
|
input[type='button'].button-outline:focus,
|
||||||
|
input[type='button'].button-outline:hover,
|
||||||
|
input[type='reset'].button-outline:focus,
|
||||||
|
input[type='reset'].button-outline:hover,
|
||||||
|
input[type='submit'].button-outline:focus,
|
||||||
|
input[type='submit'].button-outline:hover {
|
||||||
|
background-color: transparent;
|
||||||
|
border-color: #606c76;
|
||||||
|
color: #606c76;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button.button-outline[disabled]:focus, .button.button-outline[disabled]:hover,
|
||||||
|
button.button-outline[disabled]:focus,
|
||||||
|
button.button-outline[disabled]:hover,
|
||||||
|
input[type='button'].button-outline[disabled]:focus,
|
||||||
|
input[type='button'].button-outline[disabled]:hover,
|
||||||
|
input[type='reset'].button-outline[disabled]:focus,
|
||||||
|
input[type='reset'].button-outline[disabled]:hover,
|
||||||
|
input[type='submit'].button-outline[disabled]:focus,
|
||||||
|
input[type='submit'].button-outline[disabled]:hover {
|
||||||
|
border-color: inherit;
|
||||||
|
color: #9b4dca;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button.button-clear,
|
||||||
|
button.button-clear,
|
||||||
|
input[type='button'].button-clear,
|
||||||
|
input[type='reset'].button-clear,
|
||||||
|
input[type='submit'].button-clear {
|
||||||
|
background-color: transparent;
|
||||||
|
border-color: transparent;
|
||||||
|
color: #9b4dca;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button.button-clear:focus, .button.button-clear:hover,
|
||||||
|
button.button-clear:focus,
|
||||||
|
button.button-clear:hover,
|
||||||
|
input[type='button'].button-clear:focus,
|
||||||
|
input[type='button'].button-clear:hover,
|
||||||
|
input[type='reset'].button-clear:focus,
|
||||||
|
input[type='reset'].button-clear:hover,
|
||||||
|
input[type='submit'].button-clear:focus,
|
||||||
|
input[type='submit'].button-clear:hover {
|
||||||
|
background-color: transparent;
|
||||||
|
border-color: transparent;
|
||||||
|
color: #606c76;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button.button-clear[disabled]:focus, .button.button-clear[disabled]:hover,
|
||||||
|
button.button-clear[disabled]:focus,
|
||||||
|
button.button-clear[disabled]:hover,
|
||||||
|
input[type='button'].button-clear[disabled]:focus,
|
||||||
|
input[type='button'].button-clear[disabled]:hover,
|
||||||
|
input[type='reset'].button-clear[disabled]:focus,
|
||||||
|
input[type='reset'].button-clear[disabled]:hover,
|
||||||
|
input[type='submit'].button-clear[disabled]:focus,
|
||||||
|
input[type='submit'].button-clear[disabled]:hover {
|
||||||
|
color: #9b4dca;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
background: #f4f5f6;
|
||||||
|
border-radius: .4rem;
|
||||||
|
font-size: 86%;
|
||||||
|
margin: 0 .2rem;
|
||||||
|
padding: .2rem .5rem;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
background: #f4f5f6;
|
||||||
|
border-left: 0.3rem solid #9b4dca;
|
||||||
|
overflow-y: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre > code {
|
||||||
|
border-radius: 0;
|
||||||
|
display: block;
|
||||||
|
padding: 1rem 1.5rem;
|
||||||
|
white-space: pre;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
border: 0;
|
||||||
|
border-top: 0.1rem solid #f4f5f6;
|
||||||
|
margin: 3.0rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type='color'],
|
||||||
|
input[type='date'],
|
||||||
|
input[type='datetime'],
|
||||||
|
input[type='datetime-local'],
|
||||||
|
input[type='email'],
|
||||||
|
input[type='month'],
|
||||||
|
input[type='number'],
|
||||||
|
input[type='password'],
|
||||||
|
input[type='search'],
|
||||||
|
input[type='tel'],
|
||||||
|
input[type='text'],
|
||||||
|
input[type='url'],
|
||||||
|
input[type='week'],
|
||||||
|
input:not([type]),
|
||||||
|
textarea,
|
||||||
|
select {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
background-color: transparent;
|
||||||
|
border: 0.1rem solid #d1d1d1;
|
||||||
|
border-radius: .4rem;
|
||||||
|
box-shadow: none;
|
||||||
|
box-sizing: inherit;
|
||||||
|
height: 3.8rem;
|
||||||
|
padding: .6rem 1.0rem .7rem;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type='color']:focus,
|
||||||
|
input[type='date']:focus,
|
||||||
|
input[type='datetime']:focus,
|
||||||
|
input[type='datetime-local']:focus,
|
||||||
|
input[type='email']:focus,
|
||||||
|
input[type='month']:focus,
|
||||||
|
input[type='number']:focus,
|
||||||
|
input[type='password']:focus,
|
||||||
|
input[type='search']:focus,
|
||||||
|
input[type='tel']:focus,
|
||||||
|
input[type='text']:focus,
|
||||||
|
input[type='url']:focus,
|
||||||
|
input[type='week']:focus,
|
||||||
|
input:not([type]):focus,
|
||||||
|
textarea:focus,
|
||||||
|
select:focus {
|
||||||
|
border-color: #9b4dca;
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 8" width="30"><path fill="%23d1d1d1" d="M0,0l6,8l6-8"/></svg>') center right no-repeat;
|
||||||
|
padding-right: 3.0rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
select:focus {
|
||||||
|
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 8" width="30"><path fill="%239b4dca" d="M0,0l6,8l6-8"/></svg>');
|
||||||
|
}
|
||||||
|
|
||||||
|
select[multiple] {
|
||||||
|
background: none;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
min-height: 6.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
label,
|
||||||
|
legend {
|
||||||
|
display: block;
|
||||||
|
font-size: 1.6rem;
|
||||||
|
font-weight: 700;
|
||||||
|
margin-bottom: .5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldset {
|
||||||
|
border-width: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type='checkbox'],
|
||||||
|
input[type='radio'] {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-inline {
|
||||||
|
display: inline-block;
|
||||||
|
font-weight: normal;
|
||||||
|
margin-left: .5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
margin: 0 auto;
|
||||||
|
max-width: 112.0rem;
|
||||||
|
padding: 0 2.0rem;
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row.row-no-padding {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row.row-no-padding > .column {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row.row-wrap {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row.row-top {
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row.row-bottom {
|
||||||
|
align-items: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row.row-center {
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row.row-stretch {
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row.row-baseline {
|
||||||
|
align-items: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row .column {
|
||||||
|
display: block;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
margin-left: 0;
|
||||||
|
max-width: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row .column.column-offset-10 {
|
||||||
|
margin-left: 10%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row .column.column-offset-20 {
|
||||||
|
margin-left: 20%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row .column.column-offset-25 {
|
||||||
|
margin-left: 25%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row .column.column-offset-33, .row .column.column-offset-34 {
|
||||||
|
margin-left: 33.3333%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row .column.column-offset-40 {
|
||||||
|
margin-left: 40%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row .column.column-offset-50 {
|
||||||
|
margin-left: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row .column.column-offset-60 {
|
||||||
|
margin-left: 60%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row .column.column-offset-66, .row .column.column-offset-67 {
|
||||||
|
margin-left: 66.6666%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row .column.column-offset-75 {
|
||||||
|
margin-left: 75%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row .column.column-offset-80 {
|
||||||
|
margin-left: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row .column.column-offset-90 {
|
||||||
|
margin-left: 90%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row .column.column-10 {
|
||||||
|
flex: 0 0 10%;
|
||||||
|
max-width: 10%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row .column.column-20 {
|
||||||
|
flex: 0 0 20%;
|
||||||
|
max-width: 20%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row .column.column-25 {
|
||||||
|
flex: 0 0 25%;
|
||||||
|
max-width: 25%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row .column.column-33, .row .column.column-34 {
|
||||||
|
flex: 0 0 33.3333%;
|
||||||
|
max-width: 33.3333%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row .column.column-40 {
|
||||||
|
flex: 0 0 40%;
|
||||||
|
max-width: 40%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row .column.column-50 {
|
||||||
|
flex: 0 0 50%;
|
||||||
|
max-width: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row .column.column-60 {
|
||||||
|
flex: 0 0 60%;
|
||||||
|
max-width: 60%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row .column.column-66, .row .column.column-67 {
|
||||||
|
flex: 0 0 66.6666%;
|
||||||
|
max-width: 66.6666%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row .column.column-75 {
|
||||||
|
flex: 0 0 75%;
|
||||||
|
max-width: 75%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row .column.column-80 {
|
||||||
|
flex: 0 0 80%;
|
||||||
|
max-width: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row .column.column-90 {
|
||||||
|
flex: 0 0 90%;
|
||||||
|
max-width: 90%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row .column .column-top {
|
||||||
|
align-self: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row .column .column-bottom {
|
||||||
|
align-self: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row .column .column-center {
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 40rem) {
|
||||||
|
.row {
|
||||||
|
flex-direction: row;
|
||||||
|
margin-left: -1.0rem;
|
||||||
|
width: calc(100% + 2.0rem);
|
||||||
|
}
|
||||||
|
.row .column {
|
||||||
|
margin-bottom: inherit;
|
||||||
|
padding: 0 1.0rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #9b4dca;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:focus, a:hover {
|
||||||
|
color: #606c76;
|
||||||
|
}
|
||||||
|
|
||||||
|
dl,
|
||||||
|
ol,
|
||||||
|
ul {
|
||||||
|
list-style: none;
|
||||||
|
margin-top: 0;
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
dl dl,
|
||||||
|
dl ol,
|
||||||
|
dl ul,
|
||||||
|
ol dl,
|
||||||
|
ol ol,
|
||||||
|
ol ul,
|
||||||
|
ul dl,
|
||||||
|
ul ol,
|
||||||
|
ul ul {
|
||||||
|
font-size: 90%;
|
||||||
|
margin: 1.5rem 0 1.5rem 3.0rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
ol {
|
||||||
|
list-style: decimal inside;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
list-style: circle inside;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button,
|
||||||
|
button,
|
||||||
|
dd,
|
||||||
|
dt,
|
||||||
|
li {
|
||||||
|
margin-bottom: 1.0rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldset,
|
||||||
|
input,
|
||||||
|
select,
|
||||||
|
textarea {
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote,
|
||||||
|
dl,
|
||||||
|
figure,
|
||||||
|
form,
|
||||||
|
ol,
|
||||||
|
p,
|
||||||
|
pre,
|
||||||
|
table,
|
||||||
|
ul {
|
||||||
|
margin-bottom: 2.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
border-spacing: 0;
|
||||||
|
display: block;
|
||||||
|
overflow-x: auto;
|
||||||
|
text-align: left;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
td,
|
||||||
|
th {
|
||||||
|
border-bottom: 0.1rem solid #e1e1e1;
|
||||||
|
padding: 1.2rem 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
td:first-child,
|
||||||
|
th:first-child {
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
td:last-child,
|
||||||
|
th:last-child {
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 40rem) {
|
||||||
|
table {
|
||||||
|
display: table;
|
||||||
|
overflow-x: initial;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b,
|
||||||
|
strong {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
h5,
|
||||||
|
h6 {
|
||||||
|
font-weight: 300;
|
||||||
|
letter-spacing: -.1rem;
|
||||||
|
margin-bottom: 2.0rem;
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 4.6rem;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 3.6rem;
|
||||||
|
line-height: 1.25;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: 2.8rem;
|
||||||
|
line-height: 1.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
font-size: 2.2rem;
|
||||||
|
letter-spacing: -.08rem;
|
||||||
|
line-height: 1.35;
|
||||||
|
}
|
||||||
|
|
||||||
|
h5 {
|
||||||
|
font-size: 1.8rem;
|
||||||
|
letter-spacing: -.05rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
h6 {
|
||||||
|
font-size: 1.6rem;
|
||||||
|
letter-spacing: 0;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clearfix:after {
|
||||||
|
clear: both;
|
||||||
|
content: ' ';
|
||||||
|
display: table;
|
||||||
|
}
|
||||||
|
|
||||||
|
.float-left {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.float-right {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*# sourceMappingURL=milligram.css.map */
|
||||||
351
src/normalize.css
vendored
Normal file
351
src/normalize.css
vendored
Normal file
|
|
@ -0,0 +1,351 @@
|
||||||
|
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
|
||||||
|
|
||||||
|
/* Document
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the line height in all browsers.
|
||||||
|
* 2. Prevent adjustments of font size after orientation changes in iOS.
|
||||||
|
*/
|
||||||
|
|
||||||
|
html {
|
||||||
|
line-height: 1.15; /* 1 */
|
||||||
|
-webkit-text-size-adjust: 100%; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sections
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the margin in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render the `main` element consistently in IE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
main {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the font size and margin on `h1` elements within `section` and
|
||||||
|
* `article` contexts in Chrome, Firefox, and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 2em;
|
||||||
|
margin: 0.67em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Grouping content
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Add the correct box sizing in Firefox.
|
||||||
|
* 2. Show the overflow in Edge and IE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
hr {
|
||||||
|
box-sizing: content-box; /* 1 */
|
||||||
|
height: 0; /* 1 */
|
||||||
|
overflow: visible; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||||
|
* 2. Correct the odd `em` font sizing in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
pre {
|
||||||
|
font-family: monospace, monospace; /* 1 */
|
||||||
|
font-size: 1em; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Text-level semantics
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the gray background on active links in IE 10.
|
||||||
|
*/
|
||||||
|
|
||||||
|
a {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Remove the bottom border in Chrome 57-
|
||||||
|
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
abbr[title] {
|
||||||
|
border-bottom: none; /* 1 */
|
||||||
|
text-decoration: underline; /* 2 */
|
||||||
|
text-decoration: underline dotted; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct font weight in Chrome, Edge, and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
b,
|
||||||
|
strong {
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||||
|
* 2. Correct the odd `em` font sizing in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
code,
|
||||||
|
kbd,
|
||||||
|
samp {
|
||||||
|
font-family: monospace, monospace; /* 1 */
|
||||||
|
font-size: 1em; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct font size in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
small {
|
||||||
|
font-size: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prevent `sub` and `sup` elements from affecting the line height in
|
||||||
|
* all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
sub,
|
||||||
|
sup {
|
||||||
|
font-size: 75%;
|
||||||
|
line-height: 0;
|
||||||
|
position: relative;
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub {
|
||||||
|
bottom: -0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
sup {
|
||||||
|
top: -0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Embedded content
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the border on images inside links in IE 10.
|
||||||
|
*/
|
||||||
|
|
||||||
|
img {
|
||||||
|
border-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Forms
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Change the font styles in all browsers.
|
||||||
|
* 2. Remove the margin in Firefox and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
input,
|
||||||
|
optgroup,
|
||||||
|
select,
|
||||||
|
textarea {
|
||||||
|
font-family: inherit; /* 1 */
|
||||||
|
font-size: 100%; /* 1 */
|
||||||
|
line-height: 1.15; /* 1 */
|
||||||
|
margin: 0; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the overflow in IE.
|
||||||
|
* 1. Show the overflow in Edge.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
input { /* 1 */
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the inheritance of text transform in Edge, Firefox, and IE.
|
||||||
|
* 1. Remove the inheritance of text transform in Firefox.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
select { /* 1 */
|
||||||
|
text-transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the inability to style clickable types in iOS and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
[type="button"],
|
||||||
|
[type="reset"],
|
||||||
|
[type="submit"] {
|
||||||
|
-webkit-appearance: button;
|
||||||
|
appearance: button;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the inner border and padding in Firefox.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button::-moz-focus-inner,
|
||||||
|
[type="button"]::-moz-focus-inner,
|
||||||
|
[type="reset"]::-moz-focus-inner,
|
||||||
|
[type="submit"]::-moz-focus-inner {
|
||||||
|
border-style: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restore the focus styles unset by the previous rule.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button:-moz-focusring,
|
||||||
|
[type="button"]:-moz-focusring,
|
||||||
|
[type="reset"]:-moz-focusring,
|
||||||
|
[type="submit"]:-moz-focusring {
|
||||||
|
outline: 1px dotted ButtonText;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the padding in Firefox.
|
||||||
|
*/
|
||||||
|
|
||||||
|
fieldset {
|
||||||
|
padding: 0.35em 0.75em 0.625em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the text wrapping in Edge and IE.
|
||||||
|
* 2. Correct the color inheritance from `fieldset` elements in IE.
|
||||||
|
* 3. Remove the padding so developers are not caught out when they zero out
|
||||||
|
* `fieldset` elements in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
legend {
|
||||||
|
box-sizing: border-box; /* 1 */
|
||||||
|
color: inherit; /* 2 */
|
||||||
|
display: table; /* 1 */
|
||||||
|
max-width: 100%; /* 1 */
|
||||||
|
padding: 0; /* 3 */
|
||||||
|
white-space: normal; /* 1 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct vertical alignment in Chrome, Firefox, and Opera.
|
||||||
|
*/
|
||||||
|
|
||||||
|
progress {
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the default vertical scrollbar in IE 10+.
|
||||||
|
*/
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Add the correct box sizing in IE 10.
|
||||||
|
* 2. Remove the padding in IE 10.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[type="checkbox"],
|
||||||
|
[type="radio"] {
|
||||||
|
box-sizing: border-box; /* 1 */
|
||||||
|
padding: 0; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the cursor style of increment and decrement buttons in Chrome.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[type="number"]::-webkit-inner-spin-button,
|
||||||
|
[type="number"]::-webkit-outer-spin-button {
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the odd appearance in Chrome and Safari.
|
||||||
|
* 2. Correct the outline style in Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[type="search"] {
|
||||||
|
-webkit-appearance: textfield; /* 1 */
|
||||||
|
appearance: textfield;
|
||||||
|
outline-offset: -2px; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the inner padding in Chrome and Safari on macOS.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[type="search"]::-webkit-search-decoration {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the inability to style clickable types in iOS and Safari.
|
||||||
|
* 2. Change font properties to `inherit` in Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
::-webkit-file-upload-button {
|
||||||
|
-webkit-appearance: button; /* 1 */
|
||||||
|
font: inherit; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Interactive
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add the correct display in Edge, IE 10+, and Firefox.
|
||||||
|
*/
|
||||||
|
|
||||||
|
details {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add the correct display in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
summary {
|
||||||
|
display: list-item;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Misc
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct display in IE 10+.
|
||||||
|
*/
|
||||||
|
|
||||||
|
template {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct display in IE 10.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[hidden] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
7
src/routes/+layout.svelte
Normal file
7
src/routes/+layout.svelte
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
<script>
|
||||||
|
//import '../normalize.css'
|
||||||
|
//import '../milligram.css'
|
||||||
|
import '../app.css'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<slot />
|
||||||
|
|
@ -281,6 +281,50 @@
|
||||||
>
|
>
|
||||||
<span class="opacity-50 font-normal">https://</span>psbt.io
|
<span class="opacity-50 font-normal">https://</span>psbt.io
|
||||||
</button>
|
</button>
|
||||||
|
<button type="button" class="
|
||||||
|
inline-flex items-center rounded-r-md border px-4 py-2 text-md font-medium
|
||||||
|
{currentTopic === 'https://a.com' ?
|
||||||
|
'text-white bg-orange-700 border-orange-900'
|
||||||
|
:
|
||||||
|
'border-gray-300 bg-white text-gray-700'}
|
||||||
|
:ring-indigo-500"
|
||||||
|
on:click={()=>{ chatType='GLOBAL'; chatTags=[]; chatReferenceTags=['https://a.com'] }}
|
||||||
|
>
|
||||||
|
<span class="opacity-50 font-normal">a 🌎️</span>
|
||||||
|
</button>
|
||||||
|
<button type="button" class="
|
||||||
|
inline-flex items-center rounded-r-md border px-4 py-2 text-md font-medium
|
||||||
|
{currentTopic === 'https://a.com' ?
|
||||||
|
'text-white bg-orange-700 border-orange-900'
|
||||||
|
:
|
||||||
|
'border-gray-300 bg-white text-gray-700'}
|
||||||
|
:ring-indigo-500"
|
||||||
|
on:click={()=>{ chatType='GROUP'; chatTags=[]; chatReferenceTags=['https://a.com'] }}
|
||||||
|
>
|
||||||
|
<span class="opacity-50 font-normal">a 👥</span>
|
||||||
|
</button>
|
||||||
|
<button type="button" class="
|
||||||
|
inline-flex items-center rounded-r-md border px-4 py-2 text-md font-medium
|
||||||
|
{currentTopic === 'https://b.com' ?
|
||||||
|
'text-white bg-orange-700 border-orange-900'
|
||||||
|
:
|
||||||
|
'border-gray-300 bg-white text-gray-700'}
|
||||||
|
:ring-indigo-500"
|
||||||
|
on:click={()=>{ chatType='GLOBAL'; chatTags=[]; chatReferenceTags=['https://b.com'] }}
|
||||||
|
>
|
||||||
|
<span class="opacity-50 font-normal">b 🌎️</span>
|
||||||
|
</button>
|
||||||
|
<button type="button" class="
|
||||||
|
inline-flex items-center rounded-r-md border px-4 py-2 text-md font-medium
|
||||||
|
{currentTopic === 'https://b.com' ?
|
||||||
|
'text-white bg-orange-700 border-orange-900'
|
||||||
|
:
|
||||||
|
'border-gray-300 bg-white text-gray-700'}
|
||||||
|
:ring-indigo-500"
|
||||||
|
on:click={()=>{ chatType='GROUP'; chatTags=[]; chatReferenceTags=['https://b.com'] }}
|
||||||
|
>
|
||||||
|
<span class="opacity-50 font-normal">b 👥</span>
|
||||||
|
</button>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
254
src/routes/draft1/+page.svelte
Normal file
254
src/routes/draft1/+page.svelte
Normal file
|
|
@ -0,0 +1,254 @@
|
||||||
|
<script>
|
||||||
|
|
||||||
|
import { afterUpdate } from 'svelte';
|
||||||
|
|
||||||
|
import 'websocket-polyfill';
|
||||||
|
import Container from '../../Container.svelte';
|
||||||
|
import KeyPrompt from '../../KeyPrompt.svelte';
|
||||||
|
import ConnectedWidget from '../../ConnectedWidget.svelte';
|
||||||
|
import Widget from '../../Widget.svelte';
|
||||||
|
import { chatAdapter } from '../../lib/store';
|
||||||
|
import Relays from '../../Relays.svelte';
|
||||||
|
|
||||||
|
let chatStarted;
|
||||||
|
let chatType = 'GROUP';
|
||||||
|
let websiteOwnerPubkey = 'fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52';
|
||||||
|
let chatTags = [];
|
||||||
|
//let chatId = '9cef2eead5d91df42eba09be363f1272107e911685126ea5e261ac2d93299478';
|
||||||
|
let chatReferenceTags = [];
|
||||||
|
let relays = [
|
||||||
|
'wss://relay.f7z.io',
|
||||||
|
// 'wss://nos.lol',
|
||||||
|
// 'wss://relay.nostr.band',
|
||||||
|
// 'wss://nostr-pub.wellorder.net',
|
||||||
|
// 'wss://relay.damus.io',
|
||||||
|
// 'wss://relay.f7z.io'
|
||||||
|
];
|
||||||
|
|
||||||
|
$: currentTopic = [...chatTags, ...chatReferenceTags][0]
|
||||||
|
|
||||||
|
$: chatId = url && stringToHex(url)
|
||||||
|
|
||||||
|
function handleUpdate(event) {
|
||||||
|
relays = event.detail;
|
||||||
|
refreshWidget();
|
||||||
|
}
|
||||||
|
|
||||||
|
function stringToHex(str) {
|
||||||
|
let result = '';
|
||||||
|
|
||||||
|
for (let i = 0; i < str.length; i++) {
|
||||||
|
// Convert each character to its char code
|
||||||
|
const charCode = str.charCodeAt(i);
|
||||||
|
|
||||||
|
// Convert the char code to its hex representation
|
||||||
|
result += charCode.toString(16).padStart(2, '0');
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
$: chatStarted = !!$chatAdapter
|
||||||
|
|
||||||
|
function currentTopic(topic) {
|
||||||
|
return [...chatTags, ...chatReferenceTags].includes(topic)
|
||||||
|
}
|
||||||
|
|
||||||
|
$: if (chatStarted) {
|
||||||
|
afterUpdate(() => {
|
||||||
|
if (searchElement) searchElement.focus();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let searchElement;
|
||||||
|
|
||||||
|
let inputUrl = "";
|
||||||
|
let url = null;
|
||||||
|
|
||||||
|
const isValidUrl = (str) => {
|
||||||
|
const pattern = new RegExp(
|
||||||
|
"^(https?:\\/\\/)?" + // protocol
|
||||||
|
"((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|" + // domain name
|
||||||
|
"((\\d{1,3}\\.){3}\\d{1,3}))" + // OR ip (v4) address
|
||||||
|
"(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*" + // port and path
|
||||||
|
"(\\?[;&a-z\\d%_.~+=-]*)?" + // query string
|
||||||
|
"(\\#[-a-z\\d_]*)?$",
|
||||||
|
"i"
|
||||||
|
); // fragment locator
|
||||||
|
return !!pattern.test(str);
|
||||||
|
};
|
||||||
|
|
||||||
|
const startsWithProtocol = (str) => {
|
||||||
|
return (str.startsWith("http://") || str.startsWith("https://"));
|
||||||
|
};
|
||||||
|
|
||||||
|
function handleEnterKey(event) {
|
||||||
|
if (event.keyCode === 13 && isValidUrl(inputUrl)) {
|
||||||
|
showComments(inputUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function showComments(str) {
|
||||||
|
if (!str.startsWith("http://") && !str.startsWith("https://")) {
|
||||||
|
url = "https://" + str;
|
||||||
|
} else {
|
||||||
|
url = str;
|
||||||
|
}
|
||||||
|
inputUrl = "";
|
||||||
|
chatType='GROUP';
|
||||||
|
chatTags=[];
|
||||||
|
chatReferenceTags=[url];
|
||||||
|
}
|
||||||
|
|
||||||
|
function handlePaste(event) {
|
||||||
|
showComments(event);
|
||||||
|
inputUrl = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
let refreshKey = 1;
|
||||||
|
|
||||||
|
function refreshWidget() {
|
||||||
|
refreshKey++;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<title>Nostri.chat / A NOSTR chat widget you control</title>
|
||||||
|
<meta property="og:url" content="https://nostri.chat/">
|
||||||
|
<meta name="description" content="A chat widget you own, powered by nostr" />
|
||||||
|
<meta property="og:description" content="A chat widget you own, powered by nostr" />
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
|
||||||
|
<section id="hero">
|
||||||
|
<h1 class="
|
||||||
|
text-6xl
|
||||||
|
font-black
|
||||||
|
my-2
|
||||||
|
">Dissent</h1>
|
||||||
|
|
||||||
|
<h2 class="
|
||||||
|
text-2xl lg:text-4xl
|
||||||
|
text-bold
|
||||||
|
">The comments section of the internet.</h2>
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
{chatType}
|
||||||
|
{chatId}
|
||||||
|
{chatTags}
|
||||||
|
{chatReferenceTags}
|
||||||
|
</pre>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{#if !chatStarted}
|
||||||
|
<section>
|
||||||
|
<KeyPrompt {websiteOwnerPubkey} chatConfiguration={{
|
||||||
|
chatType,
|
||||||
|
chatId,
|
||||||
|
chatTags,
|
||||||
|
chatReferenceTags,
|
||||||
|
}} {relays} />
|
||||||
|
</section>
|
||||||
|
{:else}
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<div class="max-w-prose text-2xl text-gray-200 tracking-wide leading-9">
|
||||||
|
<div class="search-container p-2 w-full resize-none rounded-xl text-gray-600 border back bg-white">
|
||||||
|
{#if !startsWithProtocol(inputUrl)}
|
||||||
|
<span>https://</span>
|
||||||
|
{/if}
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
bind:value={inputUrl}
|
||||||
|
bind:this="{searchElement}"
|
||||||
|
placeholder="paste or enter a URL"
|
||||||
|
on:keyup={handleEnterKey}
|
||||||
|
on:paste={handlePaste(inputUrl)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{#if isValidUrl(inputUrl)}
|
||||||
|
<div class="p-2">
|
||||||
|
<button class="bg-purple-900 hover:bg-purple-700 w-full p-4 rounded-xl text-center font-regular text-gray-200" on:click={showComments(inputUrl)}>Show Comments</button>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="justify-center">
|
||||||
|
{#if url}
|
||||||
|
<h3 class="text-2xl lg:text-3xl text-bold url">{url}</h3>
|
||||||
|
<div class="shadow-2xl bg-gray-100/90 backdrop-blur-md mb-5 w-96 max-w-screen-sm text-black rounded-3xl px-4 py-5 overflow-auto flex flex-col justify-end ">
|
||||||
|
{#each [refreshKey] as key (key)}
|
||||||
|
<ConnectedWidget {websiteOwnerPubkey} chatConfiguration={{
|
||||||
|
chatType,
|
||||||
|
chatId,
|
||||||
|
chatTags,
|
||||||
|
chatReferenceTags,
|
||||||
|
}} {relays} />
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</section>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<Relays bind:relays on:update={handleUpdate} />
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="flex flex-col justify-start items-start gap-[25px] px-[23px] py-[17px] bg-gradient-to-br from-[#f10e0e] to-[#0c84e0]"
|
||||||
|
>
|
||||||
|
<div class="flex flex-col justify-start items-start flex-grow-0 flex-shrink-0 relative">
|
||||||
|
<p class="flex-grow-0 flex-shrink-0 text-4xl font-bold text-left text-[#eee]">Heading</p>
|
||||||
|
<p class="flex-grow-0 flex-shrink-0 text-2xl text-left text-[#eee]">sub heading</p>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col justify-start items-start flex-grow-0 flex-shrink-0 relative gap-2">
|
||||||
|
<p class="flex-grow-0 flex-shrink-0 text-xs text-left text-white">Label</p>
|
||||||
|
<div class="flex justify-start items-center flex-grow-0 flex-shrink-0 gap-4">
|
||||||
|
<div
|
||||||
|
class="flex justify-start items-center flex-grow-0 flex-shrink-0 w-[203px] relative overflow-hidden gap-2.5 p-2.5 rounded-[20px] bg-white"
|
||||||
|
>
|
||||||
|
<p class="flex-grow-0 flex-shrink-0 text-xs text-left text-black">search by typing</p>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="flex flex-col justify-center items-center flex-grow-0 flex-shrink-0 w-[42px] h-[42px] overflow-hidden gap-2.5 px-px py-[3px] rounded-[30px] bg-[#03d403]"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* div { border: solid red 1px; } */
|
||||||
|
|
||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
|
||||||
|
main{
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
section{
|
||||||
|
width: 90vw;
|
||||||
|
max-width: 600px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-container {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-container input {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3.url{
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
235
src/routes/draft2/+page.svelte
Normal file
235
src/routes/draft2/+page.svelte
Normal file
|
|
@ -0,0 +1,235 @@
|
||||||
|
<script>
|
||||||
|
import { onMount, afterUpdate } from "svelte";
|
||||||
|
|
||||||
|
import "websocket-polyfill";
|
||||||
|
import KeyPrompt from "../../KeyPrompt.svelte";
|
||||||
|
import ConnectedWidget from "../../ConnectedWidget2.svelte";
|
||||||
|
//import Relays from "../../Relays.svelte";
|
||||||
|
import MetaData from "../../MetaData.svelte";
|
||||||
|
import Brand from "../../Brand.svelte";
|
||||||
|
|
||||||
|
import { chatAdapter, url, relays } from '../../lib/store';
|
||||||
|
|
||||||
|
let chatStarted;
|
||||||
|
let chatType = "GROUP";
|
||||||
|
let websiteOwnerPubkey =
|
||||||
|
"fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52";
|
||||||
|
let chatTags = [];
|
||||||
|
//let chatId = '9cef2eead5d91df42eba09be363f1272107e911685126ea5e261ac2d93299478';
|
||||||
|
let chatReferenceTags = [];
|
||||||
|
|
||||||
|
$: currentTopic = [...chatTags, ...chatReferenceTags][0];
|
||||||
|
|
||||||
|
$: chatId = $url && stringToHex($url);
|
||||||
|
|
||||||
|
function handleUpdate(event) {
|
||||||
|
relays = event.detail;
|
||||||
|
refreshWidget();
|
||||||
|
}
|
||||||
|
|
||||||
|
function stringToHex(str) {
|
||||||
|
let result = "";
|
||||||
|
|
||||||
|
for (let i = 0; i < str.length; i++) {
|
||||||
|
// Convert each character to its char code
|
||||||
|
const charCode = str.charCodeAt(i);
|
||||||
|
|
||||||
|
// Convert the char code to its hex representation
|
||||||
|
result += charCode.toString(16).padStart(2, "0");
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
$: chatStarted = !!$chatAdapter;
|
||||||
|
|
||||||
|
function currentTopic(topic) {
|
||||||
|
return [...chatTags, ...chatReferenceTags].includes(topic);
|
||||||
|
}
|
||||||
|
|
||||||
|
$: if (chatStarted) {
|
||||||
|
afterUpdate(() => {
|
||||||
|
if (searchElement) searchElement.focus();
|
||||||
|
});
|
||||||
|
refreshWidget();
|
||||||
|
}
|
||||||
|
|
||||||
|
let searchElement;
|
||||||
|
|
||||||
|
let inputUrl = "";
|
||||||
|
|
||||||
|
const isValidUrl = (str) => {
|
||||||
|
const pattern = new RegExp(
|
||||||
|
"^(https?:\\/\\/)?" + // protocol
|
||||||
|
"((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|" + // domain name
|
||||||
|
"((\\d{1,3}\\.){3}\\d{1,3}))" + // OR ip (v4) address
|
||||||
|
"(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*" + // port and path
|
||||||
|
"(\\?[;&a-z\\d%_.~+=-]*)?" + // query string
|
||||||
|
"(\\#[-a-z\\d_]*)?$",
|
||||||
|
"i"
|
||||||
|
); // fragment locator
|
||||||
|
return !!pattern.test(str);
|
||||||
|
};
|
||||||
|
|
||||||
|
const startsWithProtocol = (str) => {
|
||||||
|
return str.startsWith("http://") || str.startsWith("https://");
|
||||||
|
};
|
||||||
|
|
||||||
|
function handleEnterKey(event) {
|
||||||
|
if (event.keyCode === 13 && isValidUrl(inputUrl)) {
|
||||||
|
showComments(inputUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function showComments(str) {
|
||||||
|
if (!str.startsWith("http://") && !str.startsWith("https://")) {
|
||||||
|
$url = "https://" + str;
|
||||||
|
} else {
|
||||||
|
$url = str;
|
||||||
|
}
|
||||||
|
inputUrl = "";
|
||||||
|
chatType = "GROUP";
|
||||||
|
chatTags = [];
|
||||||
|
chatReferenceTags = [$url];
|
||||||
|
}
|
||||||
|
|
||||||
|
function handlePaste(event) {
|
||||||
|
showComments(event);
|
||||||
|
inputUrl = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
let refreshKey = 1;
|
||||||
|
|
||||||
|
function refreshWidget() {
|
||||||
|
refreshKey++;
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<title>DiSseNT - The web's comment section</title>
|
||||||
|
<meta property="og:url" content="https://dsnt.chat/" />
|
||||||
|
<meta name="description" content="The web's comment section." />
|
||||||
|
<meta
|
||||||
|
property="og:description"
|
||||||
|
content="The web's comment section."
|
||||||
|
/>
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<div class="panel">
|
||||||
|
<section class="toolbar">
|
||||||
|
{#if $url}
|
||||||
|
<MetaData url={$url} />
|
||||||
|
{/if}
|
||||||
|
</section>
|
||||||
|
<section class="content">
|
||||||
|
<div class="search-wrapper">
|
||||||
|
<div class="search">
|
||||||
|
<div class="search__bar">
|
||||||
|
{#if !startsWithProtocol(inputUrl)}
|
||||||
|
<span>https://</span>
|
||||||
|
{/if}
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
bind:value={inputUrl}
|
||||||
|
bind:this={searchElement}
|
||||||
|
placeholder="paste or enter a URL"
|
||||||
|
on:keyup={handleEnterKey}
|
||||||
|
on:paste={handlePaste(inputUrl)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{#if isValidUrl(inputUrl)}
|
||||||
|
<button class="search__btn" on:click={showComments(inputUrl)}>show comments</button>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="brand">
|
||||||
|
<Brand />
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- RIGHT -->
|
||||||
|
<div class="sidebar">
|
||||||
|
{#if !chatStarted}
|
||||||
|
<section>
|
||||||
|
<KeyPrompt
|
||||||
|
{websiteOwnerPubkey}
|
||||||
|
chatConfiguration={{
|
||||||
|
chatType,
|
||||||
|
chatId,
|
||||||
|
chatTags,
|
||||||
|
chatReferenceTags,
|
||||||
|
}}
|
||||||
|
relays={$relays}
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
{/if}
|
||||||
|
{#if chatStarted && $url}
|
||||||
|
{#each [refreshKey] as key (key)}
|
||||||
|
<ConnectedWidget
|
||||||
|
{websiteOwnerPubkey}
|
||||||
|
chatConfiguration={{
|
||||||
|
chatType,
|
||||||
|
chatId,
|
||||||
|
chatTags,
|
||||||
|
chatReferenceTags,
|
||||||
|
}}
|
||||||
|
relays={$relays}
|
||||||
|
/>
|
||||||
|
{/each}
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 1rem;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.brand {
|
||||||
|
flex-grow: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-wrapper {
|
||||||
|
flex-grow: 1;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search__bar {
|
||||||
|
display: flex;
|
||||||
|
border-bottom: 1px solid var(--c-lines);
|
||||||
|
font-size: 2rem;
|
||||||
|
align-items: center;
|
||||||
|
padding: .5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search__bar span{
|
||||||
|
color: rgba(255, 255, 255, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.search__bar input {
|
||||||
|
color: rgba(255, 255, 255);
|
||||||
|
transition: all .3s ease;
|
||||||
|
background-color: transparent;
|
||||||
|
flex-grow: 1;
|
||||||
|
font-size: inherit;
|
||||||
|
border: none;
|
||||||
|
font-weight: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search__bar input::placeholder {
|
||||||
|
color: rgba(255, 255, 255, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.search__bar input:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
123
src/routes/figma/+page.svelte
Normal file
123
src/routes/figma/+page.svelte
Normal file
|
|
@ -0,0 +1,123 @@
|
||||||
|
<div
|
||||||
|
class="w-[1440px] h-[1024px] relative overflow-hidden"
|
||||||
|
style="background: linear-gradient(to bottom, #381100 0%, #a13000 99.98%, #b93700 99.99%, #ff4c00 100%);"
|
||||||
|
>
|
||||||
|
<div class="w-[566px] h-[1024px] absolute left-[874px] top-0 overflow-hidden">
|
||||||
|
<div
|
||||||
|
class="flex flex-col justify-start items-start w-[566px] h-[822px] absolute left-0 top-[202px] overflow-hidden bg-[#381100]/20 border-t-0 border-r-0 border-b-0 border-l border-white/50"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="flex flex-col justify-start items-start self-stretch flex-grow-0 flex-shrink-0 overflow-hidden gap-2.5 p-[30px] border-t-0 border-r-0 border-b border-l-0 border-white/50"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="flex justify-start items-start self-stretch flex-grow-0 flex-shrink-0 gap-[26px]"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="flex flex-col justify-start items-center flex-grow-0 flex-shrink-0 relative gap-[9px]"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
class="flex-grow-0 flex-shrink-0 w-[70px] h-[70px] rounded-[118.52px]"
|
||||||
|
src=""
|
||||||
|
/><svg
|
||||||
|
width="40"
|
||||||
|
height="40"
|
||||||
|
viewBox="0 0 40 40"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="flex-grow-0 flex-shrink-0"
|
||||||
|
preserveAspectRatio="xMidYMid meet"
|
||||||
|
>
|
||||||
|
<circle cx="20" cy="20" r="19.5" stroke="#FF8A00"></circle>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col justify-start items-start flex-grow relative gap-2.5">
|
||||||
|
<div
|
||||||
|
class="flex justify-between items-start self-stretch flex-grow-0 flex-shrink-0 relative pr-[15px]"
|
||||||
|
>
|
||||||
|
<p class="flex-grow-0 flex-shrink-0 text-[15px] font-bold text-left text-white">
|
||||||
|
Name
|
||||||
|
</p>
|
||||||
|
<p class="flex-grow-0 flex-shrink-0 text-[15px] font-bold text-left text-white">
|
||||||
|
17h
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<p
|
||||||
|
class="self-stretch flex-grow-0 flex-shrink-0 w-[410px] text-[15px] text-left text-white"
|
||||||
|
>
|
||||||
|
Hahaha. There are so many people on the “system” and do side work for cash and do very
|
||||||
|
very well. Tons of people selling their food cards for cash. Single moms with multiple
|
||||||
|
baby daddies and a cash side business while also on the system. And so much more. It’s
|
||||||
|
so wide spread.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="w-[566px] h-[202px] absolute left-0 top-0 overflow-hidden bg-[#381100]/20 border-t-0 border-r-0 border-b border-l-0 border-white/50"
|
||||||
|
>
|
||||||
|
<p class="absolute left-3.5 top-[122px] text-[55px] font-thin text-left text-white">35</p>
|
||||||
|
<div class="flex justify-start items-start absolute left-[474px] top-[164px] gap-[7px]">
|
||||||
|
<div class="flex-grow-0 flex-shrink-0 w-[13px] h-[13px] rounded-[13px] bg-[#d9d9d9]"></div>
|
||||||
|
<div class="flex-grow-0 flex-shrink-0 w-[13px] h-[13px] rounded-[13px] bg-[#d9d9d9]"></div>
|
||||||
|
<div class="flex-grow-0 flex-shrink-0 w-[13px] h-[13px] rounded-[13px] bg-[#d9d9d9]"></div>
|
||||||
|
<div class="flex-grow-0 flex-shrink-0 w-[13px] h-[13px] rounded-[13px] bg-[#d9d9d9]"></div>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-end items-center w-52 absolute left-[339px] top-[17px] gap-2.5">
|
||||||
|
<p class="flex-grow-0 flex-shrink-0 text-[15px] font-bold text-left text-white">Name</p>
|
||||||
|
<img
|
||||||
|
class="flex-grow-0 flex-shrink-0 w-[30px] h-[30px] rounded-[118.52px]"
|
||||||
|
src=""
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="flex flex-col justify-start items-start w-[874px] h-[1024px] absolute left-0 top-0 overflow-hidden border-t-0 border-r-0 border-b-0 border-l border-white/50"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="flex flex-col justify-start items-start flex-grow-0 flex-shrink-0 h-[202px] w-[874px] relative overflow-hidden gap-2.5 p-4 bg-[#381100]/20 border-t-0 border-r-0 border-b border-l-0 border-white/50"
|
||||||
|
>
|
||||||
|
<p class="flex-grow-0 flex-shrink-0 text-3xl text-left text-white">
|
||||||
|
FileGator - Simple Self-Hosted File Server
|
||||||
|
</p>
|
||||||
|
<p class="flex-grow-0 flex-shrink-0 text-[15px] text-left text-white/50">
|
||||||
|
https://noted.lol/filegator/
|
||||||
|
</p>
|
||||||
|
<p class="flex-grow-0 flex-shrink-0 w-[510px] text-[15px] text-left text-white">
|
||||||
|
FileGator is your personal file butler, serving up a self-hosted buffet of file management
|
||||||
|
goodness where you're the chef, and your files are the main course – all with the added
|
||||||
|
bonus of keeping your data more locked down than a treasure chest in a dragon's den.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="flex-grow-0 flex-shrink-0 w-[874px] h-[822px] relative">
|
||||||
|
<p class="absolute left-9 top-[657px] text-[81.52728271484375px] text-left">
|
||||||
|
<span class="text-[81.52728271484375px] text-left text-white">D</span
|
||||||
|
><span class="text-[81.52728271484375px] text-left text-white/50">i</span
|
||||||
|
><span class="text-[81.52728271484375px] text-left text-white">s</span
|
||||||
|
><span class="text-[81.52728271484375px] text-left text-white/50">se</span
|
||||||
|
><span class="text-[81.52728271484375px] text-left text-white">nt</span>
|
||||||
|
</p>
|
||||||
|
<p class="absolute left-9 top-[766px] text-[18.14524269104004px] text-left text-white">
|
||||||
|
The web’s comment section.
|
||||||
|
</p>
|
||||||
|
<div
|
||||||
|
class="flex justify-start items-center w-[659px] h-[86px] absolute left-[55px] top-[211px] gap-2.5 px-5 py-[21px] border-t-0 border-r-0 border-b border-l-0 border-white/50"
|
||||||
|
>
|
||||||
|
<p class="flex-grow-0 flex-shrink-0 text-4xl font-extralight text-left text-white/50">
|
||||||
|
type or paste a web address
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="w-[86px] h-[86px] absolute left-[713px] top-[210px] bg-black/20"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* div { border: solid skyblue 1px; } */
|
||||||
|
|
||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
@ -10,9 +10,9 @@ let chatReferenceTags = script.getAttribute('data-chat-reference-tags');
|
||||||
let relays = script.getAttribute('data-relays');
|
let relays = script.getAttribute('data-relays');
|
||||||
script.parentNode.insertBefore(div, script);
|
script.parentNode.insertBefore(div, script);
|
||||||
|
|
||||||
if (!relays) {
|
// if (!relays) {
|
||||||
relays = 'wss://relay.f7z.io,wss://nos.lol,wss://relay.nostr.info,wss://nostr-pub.wellorder.net,wss://relay.current.fyi,wss://relay.nostr.band'
|
// relays = 'wss://relay.f7z.io,wss://nos.lol,wss://relay.nostr.info,wss://nostr-pub.wellorder.net,wss://relay.current.fyi,wss://relay.nostr.band'
|
||||||
}
|
// }
|
||||||
|
|
||||||
relays = relays.split(',');
|
relays = relays.split(',');
|
||||||
chatTags = chatTags ? chatTags.split(',') : [];
|
chatTags = chatTags ? chatTags.split(',') : [];
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue