Skip to content

Commit a5abe48

Browse files
updates
1 parent 967227d commit a5abe48

16 files changed

Lines changed: 383 additions & 95 deletions

File tree

TODO.md

Lines changed: 25 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -11,35 +11,28 @@
1111
- add reaction from actions doesnt work
1212

1313
# Planned
14-
1. context menu
15-
1. user
16-
- block user
17-
- add/remove friend
18-
2. message list
19-
1. individual message
20-
- edited label
21-
2. virtual scrolling
22-
- downward virtual scrolling
23-
3. user list
24-
1. real time status (8.1) and RPC
25-
4. user popup
26-
1. dms shortcut
27-
5. settings
28-
1. general
29-
- toggles
30-
- edited messages
31-
- methods
32-
- idle connection
33-
- load attachment
34-
- show nicknames
35-
- blocked user messages
36-
2. themes
37-
- background wallpaper setting
38-
3. JS plugins
39-
6. message compose box
40-
1. emoji picker
41-
- sticker search
42-
- show custom emojis from all connected servers
43-
7. DMs server
44-
1. ui functionality instead of bot
45-
2. better dms server icon
14+
- message signing
15+
16+
- ping sound
17+
- status indicator dots
18+
- better emoji shortcode parsing
19+
- attachment right click
20+
- right click gif info
21+
- block user
22+
- add/remove friend
23+
- edited label
24+
- downward virtual scrolling
25+
- real time status (8.1) and RPC
26+
- dms shortcut in profile popout
27+
- toggles
28+
- edited messages
29+
- methods
30+
- idle connection
31+
- load attachment
32+
- show nicknames
33+
- blocked user messages
34+
- background wallpaper setting
35+
- JS plugins
36+
- sticker search
37+
- ui functionality instead of bot
38+
- better dms server icon

package-lock.json

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
"dependencies": {
1212
"emoji-picker-element": "^1.29.1",
1313
"emoji-picker-element-data": "^1.8.0",
14+
"highlight.js": "^11.12.0",
1415
"lucide-solid": "^1.20.0",
1516
"peerjs": "^1.5.5",
1617
"rotur-sdk": "^1.3.4",

public/themes/basic.css

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
:root {
2+
--bg-one: #181818;
3+
--bg-two: #242424;
4+
--bg-three: #2d2929;
5+
--bg-four: #101010;
6+
--hl-one: #2878aa;
7+
--hl-two: #3d94c4;
8+
--hl-three: #12577f;
9+
--hl-four: #55acd8;
10+
--border: 0px 0px 0 1px rgb(255 255 255 / 10%);
11+
--fg-one: #ffffff;
12+
--fg-two: #e8e8e8;
13+
--fg-dim: #ffffffb7;
14+
--fg-three: rgb(210 115 212);
15+
}
16+
17+
& .first_bar {
18+
background-color: var(--bg-one);
19+
& .channel_list {
20+
& .channel_item.x,
21+
& .channel_thread_item {
22+
background-color: var(--bg-one);
23+
&.channel_item--active {
24+
background-color: var(--hl-two);
25+
}
26+
}
27+
}
28+
}
29+
30+
.server_info > .header {
31+
& > .dropdown {
32+
background-color: var(--bg-one) !important;
33+
box-shadow: 0 -2px 1em 1em var(--bg-one) !important;
34+
}
35+
}

public/themes/index.json

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,23 @@
4242
{
4343
"id": "oxygen",
4444
"name": "Oxygen light",
45-
"author": "darkdot & stormy",
45+
"author": "darkdot",
4646
"version": "1.0.0",
4747
"description": "A semi realistic light theme inspired by KDE oxygen",
4848
"file": "oxygen.css",
4949
"preview": "#f0f0f0",
5050
"type": "theme"
5151
},
52+
{
53+
"id": "basic",
54+
"name": "Basic dark",
55+
"author": "darkdot",
56+
"version": "1.0.0",
57+
"description": "a very basic looking dark theme.",
58+
"file": "basic.css",
59+
"preview": "#1E1E1E",
60+
"type": "theme"
61+
},
5262
{
5363
"id": "aqua",
5464
"name": "Aqua",

public/themes/oxygen.css

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
--bg-three: #D7CFCF;
55
--bg-four: #ffffff;
66
--hl-one: #47A9E8;
7-
--hl-two: #47A9E8;
7+
--hl-two: #65bef7;
88
--hl-three: #1678b8;
9-
--hl-four: #47A9E8;
9+
--hl-four: #7acbff;
1010
--border: 0px 0px 0 1px rgb(255 255 255 / 20%);
1111
--fg-one: #000000;
1212
--fg-two: #000000;

src/components/compose/MessageComposer.jsx

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,34 @@ export default function MessageComposer(props) {
413413
}
414414
}
415415

416+
// Edit last message with up arrow when textarea is empty
417+
if (
418+
e.key === "ArrowUp" &&
419+
textarea.value.trim() === "" &&
420+
!mention.hasSuggestions() &&
421+
!channelAc.hasSuggestions()
422+
) {
423+
e.preventDefault();
424+
425+
const messages = tempState.virtMsgList?.messages?.() ?? [];
426+
const currentUsername = tempState?.conn?.me()?.username;
427+
428+
// Find the last message that belongs to the current user
429+
const lastOwnMessage = messages.findLast(
430+
(m) => m.user === currentUsername
431+
);
432+
433+
if (lastOwnMessage && !state.editing?.id) {
434+
setState("editing", {
435+
id: lastOwnMessage.id,
436+
user: lastOwnMessage.user,
437+
content: lastOwnMessage.content,
438+
});
439+
tempState.virtMsgList?.scrollToMessage?.(lastOwnMessage.id);
440+
}
441+
return;
442+
}
443+
416444
if (e.key === "Enter" && !e.shiftKey) {
417445
e.preventDefault();
418446

src/components/messages/ParseMarkdown.jsx

Lines changed: 69 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
1-
import { For, createResource, Show } from "solid-js";
1+
import { For, createResource, createSignal, Show } from "solid-js";
22
import { setState, state, tempState } from "../../App";
33
import { setPreview } from "../../App";
44
import { openPopout } from "../rightSidebar/memberList/popout.jsx";
55
import { twemojiUrl } from "./twemoji.js";
6-
const markdownCache = new Map();
7-
86
import { switchToChannel } from "../../App";
97
import { BeamEmbed } from "./embeds/BeamEmbed.jsx";
108
import { getEmbedProvider } from "./embeds/registry.jsx";
9+
import hljs from 'highlight.js';
10+
import 'highlight.js/styles/atom-one-dark.css';
11+
12+
const markdownCache = new Map();
13+
import { parseMarkdown as tokenizeMarkdown } from "./markdown_tokenizer.js";
1114

1215
function getParsedMarkdown(id, content) {
1316
if (markdownCache.has(id)) {
@@ -71,35 +74,6 @@ function ServerInviteEmbed(props) {
7174
);
7275
}
7376

74-
function readMaskedLink(text, start) {
75-
if (text[start] !== "[") return null;
76-
77-
let i = start + 1;
78-
while (i < text.length && text[i] !== "]") i++;
79-
if (text[i] !== "]" || text[i + 1] !== "(") return null;
80-
81-
const label = text.slice(start + 1, i);
82-
83-
i += 2;
84-
85-
let depth = 1;
86-
const urlStart = i;
87-
88-
while (i < text.length && depth > 0) {
89-
if (text[i] === "(") depth++;
90-
else if (text[i] === ")") depth--;
91-
i++;
92-
}
93-
94-
if (depth !== 0) return null;
95-
96-
return {
97-
label,
98-
url: text.slice(urlStart, i - 1),
99-
end: i,
100-
};
101-
}
102-
10377
export function Embed(props) {
10478
const embed = props.embed;
10579
if (embed.type == "image") return;
@@ -140,7 +114,6 @@ export function Embed(props) {
140114

141115
{embed.title && <div class="embed_title">{embed.title}</div>}
142116

143-
{/* FIXED: Use renderTokens instead of parseMarkdown to avoid circular loop */}
144117
{embed.description && (
145118
<div class="embed_description">
146119
{renderTokens(tokenizeMarkdown(embed.description))}
@@ -194,7 +167,6 @@ function EmbeddedLink(props) {
194167
const type = data().type;
195168

196169
if (type.startsWith("image/")) {
197-
console.log("d");
198170
return (
199171
<img
200172
src={props.url}
@@ -235,14 +207,60 @@ function EmbeddedLink(props) {
235207
</Show>
236208
);
237209
}
238-
import { parseMarkdown as tokenizeMarkdown } from "./markdown_tokenizer.js";
239210

240211
let keyCounter = 0;
241212

242213
function getKey() {
243214
return `key_${keyCounter++}`;
244215
}
245216

217+
function syntaxHighlight(code, language) {
218+
if (!language) {
219+
return code;
220+
}
221+
222+
try {
223+
return hljs.highlight(code, { language }).value;
224+
} catch (e) {
225+
return hljs.highlightAuto(code).value;
226+
}
227+
}
228+
229+
function CodeBlockDisplay(props) {
230+
const [copied, setCopied] = createSignal(false);
231+
232+
const handleCopy = () => {
233+
navigator.clipboard.writeText(props.code);
234+
setCopied(true);
235+
setTimeout(() => setCopied(false), 2000);
236+
};
237+
238+
const highlightedCode = syntaxHighlight(props.code, props.language);
239+
240+
return (
241+
<div class="code_block_container">
242+
<div class="code_block_header">
243+
<span class="code_block_language">
244+
{props.language || 'text'}
245+
</span>
246+
<button
247+
class="code_block_copy"
248+
onClick={handleCopy}
249+
title="Copy code"
250+
>
251+
{copied() ? 'Copied' : 'Copy'}
252+
</button>
253+
</div>
254+
<pre class="code_block_content">
255+
<code
256+
class={`hljs lang-${props.language || 'text'}`}
257+
innerHTML={highlightedCode}
258+
/>
259+
</pre>
260+
</div>
261+
);
262+
}
263+
246264
function renderToken(token, depth = 0) {
247265
if (!token) return null;
248266

@@ -257,6 +275,13 @@ function renderToken(token, depth = 0) {
257275
case "text":
258276
return token.value;
259277

278+
case "italic":
279+
return (
280+
<i key={key}>
281+
{token.children?.map((t) => renderToken(t, depth + 1))}
282+
</i>
283+
);
284+
260285
case "bold":
261286
return (
262287
<strong key={key}>
@@ -281,9 +306,11 @@ function renderToken(token, depth = 0) {
281306

282307
case "codeBlock":
283308
return (
284-
<pre key={key} class="code_block">
285-
<code class={`lang-${token.language}`}>{token.code}</code>
286-
</pre>
309+
<CodeBlockDisplay
310+
key={key}
311+
code={token.code}
312+
language={token.language}
313+
/>
287314
);
288315

289316
case "link":
@@ -304,6 +331,7 @@ function renderToken(token, depth = 0) {
304331
}
305332
return <EmbeddedLink key={key} url={token.url} />;
306333
}
334+
307335
case "emoji":
308336
return (
309337
<img
@@ -329,8 +357,10 @@ function renderToken(token, depth = 0) {
329357
/>
330358
);
331359
}
360+
332361
case "newline":
333362
return <br key={key} />;
363+
334364
case "sticker":
335365
return (
336366
<img
@@ -340,6 +370,7 @@ function renderToken(token, depth = 0) {
340370
alt=""
341371
/>
342372
);
373+
343374
case "timestamp": {
344375
const date = new Date(token.unix * 1000);
345376

0 commit comments

Comments
 (0)