fubi 1.0.0-beta.3 → 1.0.0-beta.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.es.js +21288 -0
- package/dist/index.iife.js +6 -0
- package/package.json +23 -21
- package/.prettierignore +0 -9
- package/.prettierrc +0 -23
- package/.vscode/extensions.json +0 -3
- package/config.toml +0 -3
- package/demo/index.html +0 -64
- package/index.html +0 -19
- package/publish.js +0 -77
- package/src/components/App.svelte +0 -17
- package/src/components/Debug.svelte +0 -39
- package/src/components/Layout.svelte +0 -47
- package/src/components/comment/CommentCard.svelte +0 -49
- package/src/components/comment/CommentFocusElement.svelte +0 -14
- package/src/components/comment/CommentMenu.svelte +0 -18
- package/src/components/comment/CommentPriority.svelte +0 -31
- package/src/components/comment/CommentReplyButton.svelte +0 -35
- package/src/components/comment/CommentUser.svelte +0 -22
- package/src/components/icons/AlertIcon.svelte +0 -7
- package/src/components/icons/CancelIcon.svelte +0 -8
- package/src/components/icons/CheckCircle.svelte +0 -7
- package/src/components/icons/CheckIcon.svelte +0 -7
- package/src/components/icons/CheckSmallIcon.svelte +0 -7
- package/src/components/icons/ChevronLeftIcon.svelte +0 -7
- package/src/components/icons/ChevronRightIcon.svelte +0 -7
- package/src/components/icons/ChevronsUpIcon.svelte +0 -8
- package/src/components/icons/DotsIcon.svelte +0 -9
- package/src/components/icons/PlusIcon.svelte +0 -8
- package/src/components/index.ts +0 -0
- package/src/components/toolbar/ButtonToolbar.svelte +0 -29
- package/src/components/toolbar/Menu.svelte +0 -42
- package/src/components/toolbar/Toolbar.svelte +0 -55
- package/src/components/ui/Button.svelte +0 -33
- package/src/components/ui/ButtonIcon.svelte +0 -31
- package/src/components/ui/ButtonSmall.svelte +0 -12
- package/src/components/ui/ButtonTabbar.svelte +0 -35
- package/src/components/ui/Chip.svelte +0 -28
- package/src/components/ui/Icon.svelte +0 -38
- package/src/components/ui/Input.svelte +0 -9
- package/src/components/ui/List.svelte +0 -18
- package/src/components/ui/ListItem.svelte +0 -87
- package/src/components/ui/ListLoader.svelte +0 -20
- package/src/components/ui/Loader.svelte +0 -39
- package/src/components/ui/Logo.svelte +0 -20
- package/src/components/ui/Msg.svelte +0 -21
- package/src/components/ui/Navbar.svelte +0 -33
- package/src/components/ui/Page.svelte +0 -67
- package/src/components/ui/Tabbar.svelte +0 -60
- package/src/components/ui/Toggle.svelte +0 -8
- package/src/components/ui/Window.svelte +0 -61
- package/src/index.ts +0 -28
- package/src/lib/actions.ts +0 -58
- package/src/lib/haptics.ts +0 -94
- package/src/lib/logger.ts +0 -22
- package/src/lib/routes.ts +0 -37
- package/src/lib/utils.ts +0 -27
- package/src/modules/app.svelte.ts +0 -66
- package/src/modules/auth.svelte.ts +0 -80
- package/src/modules/collection.svelte.ts +0 -79
- package/src/modules/comments.svelte.ts +0 -34
- package/src/modules/dom.svelte.ts +0 -26
- package/src/modules/domains.svelte.ts +0 -91
- package/src/modules/environment.svelte.ts +0 -10
- package/src/modules/events.svelte.ts +0 -23
- package/src/modules/folders.svelte.ts +0 -64
- package/src/modules/home.svelte.ts +0 -29
- package/src/modules/hover.svelte.ts +0 -3
- package/src/modules/index.ts +0 -28
- package/src/modules/keys.svelte.ts +0 -72
- package/src/modules/module.svelte.ts +0 -47
- package/src/modules/navbar.svelte.ts +0 -11
- package/src/modules/pages.svelte.ts +0 -126
- package/src/modules/project.svelte.ts +0 -70
- package/src/modules/router.svelte.ts +0 -99
- package/src/modules/tabbar.svelte.ts +0 -21
- package/src/modules/teams.svelte.ts +0 -0
- package/src/modules/toolbar.svelte.ts +0 -34
- package/src/modules/watch.svelte.ts +0 -8
- package/src/modules/win.svelte.ts +0 -273
- package/src/pages/Comments.svelte +0 -32
- package/src/pages/Error.svelte +0 -25
- package/src/pages/Folders.svelte +0 -53
- package/src/pages/Login.svelte +0 -44
- package/src/pages/Pages.svelte +0 -75
- package/src/pages/Thread.svelte +0 -11
- package/src/styles/global.css +0 -16
- package/src/styles/router.css +0 -79
- package/src/styles/styles.css +0 -159
- package/src/styles/tailwind.css +0 -115
- package/src/styles/variables.css +0 -99
- package/src/test.ts +0 -22
- package/src/types/fubi.ts +0 -0
- package/src/types/msg.ts +0 -5
- package/src/types/pocketbase.ts +0 -7
- package/src/types/user.ts +0 -8
- package/svelte.config.js +0 -11
- package/tsconfig.json +0 -41
- package/vite.config.ts +0 -61
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { Module } from "./module.svelte";
|
|
2
|
-
|
|
3
|
-
export class Tabbar extends Module {
|
|
4
|
-
visible = $state(false);
|
|
5
|
-
|
|
6
|
-
tabActive = $derived(
|
|
7
|
-
(this.modules.router.navigating
|
|
8
|
-
? this.modules.router.direction === "forward"
|
|
9
|
-
? this.modules.router.next
|
|
10
|
-
: this.modules.router.previous
|
|
11
|
-
: this.modules.router.current) === "comments"
|
|
12
|
-
? "comments"
|
|
13
|
-
: (this.modules.router.navigating
|
|
14
|
-
? this.modules.router.direction === "forward"
|
|
15
|
-
? this.modules.router.next
|
|
16
|
-
: this.modules.router.previous
|
|
17
|
-
: this.modules.router.current) === "folders"
|
|
18
|
-
? "folders"
|
|
19
|
-
: "pages",
|
|
20
|
-
);
|
|
21
|
-
}
|
|
File without changes
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import type { App } from "./app.svelte";
|
|
2
|
-
import { hasFocus } from "@utils";
|
|
3
|
-
import { Module } from "./module.svelte";
|
|
4
|
-
|
|
5
|
-
export class Toolbar extends Module {
|
|
6
|
-
wrapper!: HTMLElement;
|
|
7
|
-
hovering = $state(false);
|
|
8
|
-
focusWithin = $state(false);
|
|
9
|
-
|
|
10
|
-
visible = $state(false);
|
|
11
|
-
minimized = $state(true);
|
|
12
|
-
|
|
13
|
-
logo = $state(true);
|
|
14
|
-
|
|
15
|
-
constructor(wrapper: HTMLElement) {
|
|
16
|
-
super();
|
|
17
|
-
this.wrapper = wrapper;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
start() {
|
|
21
|
-
this.modules.keys.on("c", () => {
|
|
22
|
-
if (hasFocus()) return;
|
|
23
|
-
console.log("you pressed c, should enable comment mode");
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
handlePointerEnter() {
|
|
28
|
-
this.hovering = true;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
handlePointerLeave() {
|
|
32
|
-
this.hovering = false;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
@@ -1,273 +0,0 @@
|
|
|
1
|
-
// modules
|
|
2
|
-
import { Spring } from "svelte/motion";
|
|
3
|
-
|
|
4
|
-
import { hasFocus } from "@utils";
|
|
5
|
-
import { Module } from "./module.svelte.js";
|
|
6
|
-
import { haptics } from "@lib/haptics.js";
|
|
7
|
-
|
|
8
|
-
const MIDDLE_TO_TOP_TRESHOLD = 50; // px distance from middle to top to trigger open
|
|
9
|
-
const MIDDLE_TO_BOTTOM_TRESHOLD = 250; // px distance from middle to bottom to trigger close
|
|
10
|
-
|
|
11
|
-
const BREAKPOINT_TRESHOLD = 30;
|
|
12
|
-
|
|
13
|
-
export class Win extends Module {
|
|
14
|
-
el = $state(null as HTMLDivElement | null);
|
|
15
|
-
|
|
16
|
-
declare opened: boolean;
|
|
17
|
-
|
|
18
|
-
contentScroll = $state<number>(0);
|
|
19
|
-
|
|
20
|
-
height = new Spring(0, { stiffness: 0.2, damping: 0.4 });
|
|
21
|
-
|
|
22
|
-
breakpoints = $state({
|
|
23
|
-
bottom: 0,
|
|
24
|
-
middle: this.calculateBreakpoint(),
|
|
25
|
-
// get client height
|
|
26
|
-
top: this.calculateBreakpoint(0.99),
|
|
27
|
-
});
|
|
28
|
-
currentBreakpoint = $state<number>(this.breakpoints.middle);
|
|
29
|
-
nextBreakpoint = $state<number>(this.breakpoints.middle);
|
|
30
|
-
|
|
31
|
-
dragging = $state({
|
|
32
|
-
active: false,
|
|
33
|
-
start: 0,
|
|
34
|
-
current: 0,
|
|
35
|
-
difference: 0,
|
|
36
|
-
startHeight: 0,
|
|
37
|
-
height: 0,
|
|
38
|
-
direction: null as "up" | "down" | null,
|
|
39
|
-
hasMoved: false,
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
constructor() {
|
|
43
|
-
super();
|
|
44
|
-
this.storage<boolean>("opened", "fubi-win-opened", true);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
start() {
|
|
48
|
-
this.modules.keys.on("m", () => {
|
|
49
|
-
if (!hasFocus()) this.toggle();
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
this.modules.keys.on("Escape", () => {
|
|
53
|
-
if (!hasFocus()) this.close();
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
// check if the window is opened
|
|
57
|
-
if (this.opened) {
|
|
58
|
-
this.modules.dom.scroll.disable({ touchOnly: true });
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// Setup touch handling
|
|
62
|
-
this.setupTouchHandling();
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
setupTouchHandling() {
|
|
66
|
-
// Wait for element
|
|
67
|
-
if (!this.el) {
|
|
68
|
-
requestAnimationFrame(() => this.setupTouchHandling());
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
let touchStartY = 0;
|
|
73
|
-
let startScrollTop = 0;
|
|
74
|
-
let deciding = true;
|
|
75
|
-
|
|
76
|
-
// Use native touch events for better control
|
|
77
|
-
this.el.addEventListener(
|
|
78
|
-
"touchstart",
|
|
79
|
-
(e) => {
|
|
80
|
-
if (!this.opened) return;
|
|
81
|
-
|
|
82
|
-
touchStartY = e.touches[0].clientY;
|
|
83
|
-
startScrollTop = this.contentScroll;
|
|
84
|
-
deciding = true;
|
|
85
|
-
},
|
|
86
|
-
{ passive: true },
|
|
87
|
-
);
|
|
88
|
-
|
|
89
|
-
this.el.addEventListener(
|
|
90
|
-
"touchmove",
|
|
91
|
-
(e) => {
|
|
92
|
-
if (!this.opened || !deciding) return;
|
|
93
|
-
|
|
94
|
-
const touchY = e.touches[0].clientY;
|
|
95
|
-
const diff = touchY - touchStartY;
|
|
96
|
-
|
|
97
|
-
// After 5px of movement, decide what to do
|
|
98
|
-
if (Math.abs(diff) > 5) {
|
|
99
|
-
const scrollable = this.el?.querySelector(
|
|
100
|
-
".page-content, .content, [data-scrollable]",
|
|
101
|
-
) as HTMLElement;
|
|
102
|
-
const isAtBottom = scrollable
|
|
103
|
-
? this.contentScroll >= scrollable.scrollHeight - scrollable.clientHeight - 1
|
|
104
|
-
: false;
|
|
105
|
-
|
|
106
|
-
const shouldPreventScroll =
|
|
107
|
-
(this.contentScroll <= 0 && diff > 0) || // At top, dragging down
|
|
108
|
-
(isAtBottom && diff < 0 && this.currentBreakpoint !== this.breakpoints.top); // At bottom, dragging up
|
|
109
|
-
|
|
110
|
-
if (shouldPreventScroll) {
|
|
111
|
-
e.preventDefault(); // This will prevent the scroll
|
|
112
|
-
deciding = false; // Decision made
|
|
113
|
-
} else {
|
|
114
|
-
deciding = false; // Let it scroll
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
},
|
|
118
|
-
{ passive: false },
|
|
119
|
-
);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
open() {
|
|
123
|
-
this.currentBreakpoint = this.breakpoints.middle;
|
|
124
|
-
this.opened = true;
|
|
125
|
-
this.modules.dom.scroll.disable({ touchOnly: true });
|
|
126
|
-
|
|
127
|
-
// focus element
|
|
128
|
-
this.focusPageContent();
|
|
129
|
-
}
|
|
130
|
-
close() {
|
|
131
|
-
this.currentBreakpoint = this.breakpoints.bottom;
|
|
132
|
-
this.opened = false;
|
|
133
|
-
this.modules.dom.scroll.enable();
|
|
134
|
-
}
|
|
135
|
-
toggle() {
|
|
136
|
-
this[this.opened ? "close" : "open"]();
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
getCurrentHeight(max: boolean = false): number {
|
|
140
|
-
if (!this.el) return 0;
|
|
141
|
-
|
|
142
|
-
const style = getComputedStyle(this.el);
|
|
143
|
-
const height = style[max ? "maxHeight" : "height"];
|
|
144
|
-
|
|
145
|
-
if (!height || height === "none") return 0;
|
|
146
|
-
|
|
147
|
-
return parseFloat(height) || 0;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
calculateBreakpoint(fragment: number = 0.55): number {
|
|
151
|
-
const deviceHeight = window.innerHeight;
|
|
152
|
-
const calculated = deviceHeight * fragment;
|
|
153
|
-
return Math.max(200, Math.min(1000, calculated));
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
focusPageContent() {
|
|
157
|
-
const currentPageName = this.modules.router.current;
|
|
158
|
-
const page = this.el?.querySelectorAll(`[data-fubi-page="${currentPageName}"]`);
|
|
159
|
-
const pageContent = page?.[0]?.querySelector(".page-content") as HTMLDivElement | null;
|
|
160
|
-
|
|
161
|
-
if (pageContent) pageContent.focus();
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
drag = {
|
|
165
|
-
start: (e: PointerEvent) => {
|
|
166
|
-
if (!this.modules.environment.touch) return;
|
|
167
|
-
|
|
168
|
-
// DON'T stop propagation here - we don't know if it's a drag yet!
|
|
169
|
-
// Check if the target is an interactive element
|
|
170
|
-
const target = e.target as HTMLElement;
|
|
171
|
-
if (target.closest("button, a, input, label, textarea, select")) {
|
|
172
|
-
return; // Don't start drag on interactive elements
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
this.dragging.hasMoved = false;
|
|
176
|
-
this.dragging.active = true;
|
|
177
|
-
this.dragging.start = e.y;
|
|
178
|
-
this.dragging.startHeight = this.getCurrentHeight();
|
|
179
|
-
this.dragging.height = this.dragging.startHeight;
|
|
180
|
-
},
|
|
181
|
-
|
|
182
|
-
move: (e: PointerEvent) => {
|
|
183
|
-
if (!this.dragging.active || !this.modules.environment.touch) return;
|
|
184
|
-
|
|
185
|
-
this.dragging.current = e.y;
|
|
186
|
-
this.dragging.difference = this.dragging.current - this.dragging.start;
|
|
187
|
-
|
|
188
|
-
// Only process if we're actually dragging (not scrolling)
|
|
189
|
-
// The touch handler above should have prevented scroll if needed
|
|
190
|
-
if (Math.abs(this.dragging.difference) > 2) {
|
|
191
|
-
this.dragging.hasMoved = true;
|
|
192
|
-
|
|
193
|
-
const isAtTop = this.contentScroll <= 0;
|
|
194
|
-
const isDraggingDown = this.dragging.difference > 0;
|
|
195
|
-
|
|
196
|
-
// Only actually drag if we're in a drag scenario
|
|
197
|
-
// (at top dragging down, or at bottom dragging up)
|
|
198
|
-
if (!(isAtTop && isDraggingDown)) {
|
|
199
|
-
// Check if at bottom
|
|
200
|
-
const scrollable = this.el?.querySelector(
|
|
201
|
-
".page-content, .content, [data-scrollable]",
|
|
202
|
-
) as HTMLElement;
|
|
203
|
-
const isAtBottom = scrollable
|
|
204
|
-
? this.contentScroll >= scrollable.scrollHeight - scrollable.clientHeight - 1
|
|
205
|
-
: false;
|
|
206
|
-
const isDraggingUp = this.dragging.difference < 0;
|
|
207
|
-
|
|
208
|
-
if (!(isAtBottom && isDraggingUp && this.currentBreakpoint !== this.breakpoints.top)) {
|
|
209
|
-
// We shouldn't be dragging, bail out
|
|
210
|
-
this.dragging.active = false;
|
|
211
|
-
return;
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
if (this.currentBreakpoint === this.breakpoints.middle) {
|
|
217
|
-
if (
|
|
218
|
-
this.dragging.direction === "up" &&
|
|
219
|
-
this.dragging.height > this.breakpoints.middle + BREAKPOINT_TRESHOLD
|
|
220
|
-
) {
|
|
221
|
-
this.nextBreakpoint = this.breakpoints.top;
|
|
222
|
-
} else if (
|
|
223
|
-
this.dragging.direction === "down" &&
|
|
224
|
-
this.dragging.height < this.breakpoints.middle - BREAKPOINT_TRESHOLD
|
|
225
|
-
) {
|
|
226
|
-
this.nextBreakpoint = this.breakpoints.bottom;
|
|
227
|
-
} else {
|
|
228
|
-
this.nextBreakpoint = this.breakpoints.middle;
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
if (this.currentBreakpoint === this.breakpoints.top) {
|
|
233
|
-
if (
|
|
234
|
-
this.dragging.direction === "down" &&
|
|
235
|
-
this.dragging.height < this.breakpoints.top - MIDDLE_TO_TOP_TRESHOLD
|
|
236
|
-
) {
|
|
237
|
-
this.nextBreakpoint = this.breakpoints.middle;
|
|
238
|
-
} else {
|
|
239
|
-
this.nextBreakpoint = this.breakpoints.top;
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
this.dragging.height = this.dragging.startHeight - this.dragging.difference;
|
|
244
|
-
this.dragging.direction = this.dragging.difference < 0 ? "up" : "down";
|
|
245
|
-
},
|
|
246
|
-
|
|
247
|
-
end: (e: PointerEvent) => {
|
|
248
|
-
// Always prevent event propagation if we were in drag mode
|
|
249
|
-
// This stops the click from reaching the backdrop button
|
|
250
|
-
if (this.dragging.active || this.dragging.hasMoved) {
|
|
251
|
-
e.preventDefault();
|
|
252
|
-
e.stopPropagation();
|
|
253
|
-
e.stopImmediatePropagation();
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
if (!this.dragging.active) return;
|
|
257
|
-
|
|
258
|
-
if (this.dragging.hasMoved) {
|
|
259
|
-
this.currentBreakpoint = this.nextBreakpoint;
|
|
260
|
-
if (this.currentBreakpoint === this.breakpoints.bottom) this.close();
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
// Reset all drag state
|
|
264
|
-
this.dragging.active = false;
|
|
265
|
-
this.dragging.current = 0;
|
|
266
|
-
this.dragging.startHeight = 0;
|
|
267
|
-
this.dragging.height = 0;
|
|
268
|
-
this.dragging.difference = 0;
|
|
269
|
-
this.dragging.direction = null;
|
|
270
|
-
this.dragging.hasMoved = false;
|
|
271
|
-
},
|
|
272
|
-
};
|
|
273
|
-
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import { getModules } from "@modules";
|
|
3
|
-
|
|
4
|
-
// components
|
|
5
|
-
import Page from "@ui/Page.svelte";
|
|
6
|
-
|
|
7
|
-
import Navbar from "@components/ui/Navbar.svelte";
|
|
8
|
-
import CommentCard from "@components/comment/CommentCard.svelte";
|
|
9
|
-
|
|
10
|
-
// icons
|
|
11
|
-
import { SquareLock02Icon } from "@hugeicons-pro/core-solid-rounded";
|
|
12
|
-
import ListLoader from "@components/ui/ListLoader.svelte";
|
|
13
|
-
|
|
14
|
-
const { comments, pages } = getModules();
|
|
15
|
-
</script>
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
<Page name="comments" title={pages?.current?.name}>
|
|
19
|
-
<div class="flex flex-col gap-8">
|
|
20
|
-
{#each comments?.data ?? [] as comment, index }
|
|
21
|
-
<CommentCard {...comment} />
|
|
22
|
-
{:else}
|
|
23
|
-
{#if comments?.loading}
|
|
24
|
-
<ListLoader spinner />
|
|
25
|
-
{:else if comments?.error}
|
|
26
|
-
<p>Error loading</p>
|
|
27
|
-
{:else if comments?.empty}
|
|
28
|
-
<p class="p-4 text-center text-sm text-gray-500">No comments</p>
|
|
29
|
-
{/if}
|
|
30
|
-
{/each}
|
|
31
|
-
</div>
|
|
32
|
-
</Page>
|
package/src/pages/Error.svelte
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import { getModules } from "@modules";
|
|
3
|
-
|
|
4
|
-
// components
|
|
5
|
-
import { HugeiconsIcon } from "@hugeicons/svelte";
|
|
6
|
-
|
|
7
|
-
import Page from "@ui/Page.svelte";
|
|
8
|
-
import ButtonSmall from "@components/ui/ButtonSmall.svelte";
|
|
9
|
-
|
|
10
|
-
// icons
|
|
11
|
-
import { AlertDiamondIcon } from "@hugeicons-pro/core-solid-rounded";
|
|
12
|
-
import Button from "@components/ui/Button.svelte";
|
|
13
|
-
|
|
14
|
-
</script>
|
|
15
|
-
<Page name="error" title="Error">
|
|
16
|
-
<div class="h-full flex-center flex-col gap-16">
|
|
17
|
-
<div class="flex-center flex-col gap-8 text-center bg-red-500/20 glass w-full p-16 rounded-[16px]">
|
|
18
|
-
<HugeiconsIcon icon={AlertDiamondIcon} className="size-32 text-red-400" />
|
|
19
|
-
<p class="text-sm text-red-200">An unexpected error has occurred. Please try again later.</p>
|
|
20
|
-
<div class="flex-center w-full mt-4">
|
|
21
|
-
<ButtonSmall onclick={() => window.location.reload()}>Refresh</ButtonSmall>
|
|
22
|
-
</div>
|
|
23
|
-
</div>
|
|
24
|
-
</div>
|
|
25
|
-
</Page>
|
package/src/pages/Folders.svelte
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import { getModules } from "@modules";
|
|
3
|
-
const { router, folders } = getModules();
|
|
4
|
-
|
|
5
|
-
import type { FolderType } from "@modules/folders.svelte";
|
|
6
|
-
|
|
7
|
-
// components
|
|
8
|
-
import Page from "@ui/Page.svelte";
|
|
9
|
-
import List from "@ui/List.svelte";
|
|
10
|
-
import ListItem from "@ui/ListItem.svelte";
|
|
11
|
-
import ListLoader from "@ui/ListLoader.svelte";
|
|
12
|
-
import Chip from "@ui/Chip.svelte";
|
|
13
|
-
|
|
14
|
-
// icons
|
|
15
|
-
import {
|
|
16
|
-
Folder01Icon,
|
|
17
|
-
FolderLockedIcon,
|
|
18
|
-
User02Icon
|
|
19
|
-
} from "@hugeicons-pro/core-solid-rounded";
|
|
20
|
-
|
|
21
|
-
const handleFolderSelection = (folder: FolderType) => {
|
|
22
|
-
folders.select(folder);
|
|
23
|
-
router.goto("pages");
|
|
24
|
-
}
|
|
25
|
-
</script>
|
|
26
|
-
|
|
27
|
-
<Page name="folders" title="Folders">
|
|
28
|
-
<List>
|
|
29
|
-
{#each folders?.data ?? [] as folder, index }
|
|
30
|
-
<ListItem
|
|
31
|
-
title={folder.name}
|
|
32
|
-
subtitle={folder.locked && "You don't have access"}
|
|
33
|
-
icon={folder.locked ? FolderLockedIcon : Folder01Icon}
|
|
34
|
-
disabled={folder.locked ?? null}
|
|
35
|
-
{index}
|
|
36
|
-
active={folders.current?.id === folder.id}
|
|
37
|
-
onclick={() => folder ? handleFolderSelection(folder) : null}
|
|
38
|
-
>
|
|
39
|
-
{#snippet after()}
|
|
40
|
-
{#if !folder.locked}<Chip theme="sky" icon={User02Icon}>{folder.users.length}</Chip>{/if}
|
|
41
|
-
{/snippet}
|
|
42
|
-
</ListItem>
|
|
43
|
-
{:else}
|
|
44
|
-
{#if folders?.loading}
|
|
45
|
-
<ListLoader spinner />
|
|
46
|
-
{:else if folders?.error}
|
|
47
|
-
<p>Error loading</p>
|
|
48
|
-
{:else if folders?.empty}
|
|
49
|
-
<p class="p-4 text-center text-sm text-gray-500">No folders</p>
|
|
50
|
-
{/if}
|
|
51
|
-
{/each}
|
|
52
|
-
</List>
|
|
53
|
-
</Page>
|
package/src/pages/Login.svelte
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import { getModules } from "@modules";
|
|
3
|
-
|
|
4
|
-
// components
|
|
5
|
-
import Page from "@ui/Page.svelte";
|
|
6
|
-
import List from "@ui/List.svelte";
|
|
7
|
-
import Input from "@ui/Input.svelte";
|
|
8
|
-
import Button from "@ui/Button.svelte";
|
|
9
|
-
import Msg from "@ui/Msg.svelte";
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
// icons
|
|
14
|
-
import { SquareLock02Icon } from "@hugeicons-pro/core-solid-rounded";
|
|
15
|
-
import { HugeiconsIcon } from "@hugeicons/svelte";
|
|
16
|
-
import Logo from "@components/ui/Logo.svelte";
|
|
17
|
-
|
|
18
|
-
const { auth, router } = getModules();
|
|
19
|
-
|
|
20
|
-
let email = $state("");
|
|
21
|
-
let password = $state("");
|
|
22
|
-
|
|
23
|
-
const handleLogin = (e: Event) => {
|
|
24
|
-
e.preventDefault();
|
|
25
|
-
auth.login(email, password);
|
|
26
|
-
// password = "";
|
|
27
|
-
}
|
|
28
|
-
</script>
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
<Page name="login" navbar={false} tabbar={false}>
|
|
32
|
-
<form class="flex flex-col gap-8 px-8 w-full" onsubmit={(e) => handleLogin(e)}>
|
|
33
|
-
<div class="flex-center flex-col pt-16">
|
|
34
|
-
<HugeiconsIcon icon={SquareLock02Icon} className="size-32 text-sky-500" />
|
|
35
|
-
<p class="text-[18px]/none">Sign In to fubi</p>
|
|
36
|
-
</div>
|
|
37
|
-
<List>
|
|
38
|
-
<Input name="email" type="email" label="E-mail" autocomplete="email" required bind:value={email} />
|
|
39
|
-
<Input name="password" type="password" label="Password" autocomplete="password" required bind:value={password} />
|
|
40
|
-
</List>
|
|
41
|
-
<Msg msg={auth.msg} />
|
|
42
|
-
<Button loading={auth.loading} disabled={auth.loading || router.navigating}>Login</Button>
|
|
43
|
-
</form>
|
|
44
|
-
</Page>
|
package/src/pages/Pages.svelte
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import { getModules } from "@modules";
|
|
3
|
-
const { win, folders, router, domains, pages } = getModules();
|
|
4
|
-
|
|
5
|
-
// components
|
|
6
|
-
import List from "@ui/List.svelte";
|
|
7
|
-
import ListItem from "@ui/ListItem.svelte";
|
|
8
|
-
import ListLoader from "@ui/ListLoader.svelte";
|
|
9
|
-
import Page from "@ui/Page.svelte";
|
|
10
|
-
import Chip from "@ui/Chip.svelte";
|
|
11
|
-
import Navbar from "@ui/Navbar.svelte";
|
|
12
|
-
import Loader from "@ui/Loader.svelte";
|
|
13
|
-
|
|
14
|
-
import { HugeiconsIcon } from "@hugeicons/svelte";
|
|
15
|
-
|
|
16
|
-
// icons
|
|
17
|
-
import {
|
|
18
|
-
FileEmpty02Icon,
|
|
19
|
-
File02Icon,
|
|
20
|
-
AiFileIcon
|
|
21
|
-
} from "@hugeicons-pro/core-solid-rounded";
|
|
22
|
-
import Icon from "@components/ui/Icon.svelte";
|
|
23
|
-
</script>
|
|
24
|
-
|
|
25
|
-
<Page name="pages" title={folders.current.name}>
|
|
26
|
-
<List>
|
|
27
|
-
{#if pages.isNew}
|
|
28
|
-
<ListItem
|
|
29
|
-
classIcon="text-white"
|
|
30
|
-
title={pages.current?.name}
|
|
31
|
-
subtitle="No Comments"
|
|
32
|
-
active
|
|
33
|
-
onclick={() => router.goto("comments")}
|
|
34
|
-
>
|
|
35
|
-
{#snippet start()}
|
|
36
|
-
<Loader progress={0} />
|
|
37
|
-
{/snippet}
|
|
38
|
-
{#snippet after()}
|
|
39
|
-
<Chip theme="sky">Current</Chip>
|
|
40
|
-
{/snippet}
|
|
41
|
-
</ListItem>
|
|
42
|
-
{/if}
|
|
43
|
-
{#each pages?.data as { comments, totalComments, doneComments, progress, pathname, name }, index}
|
|
44
|
-
<a href={pathname === domains?.url?.pathname ? "#" : pathname}>
|
|
45
|
-
<ListItem
|
|
46
|
-
class={comments.length > 0 ? "text-white" : pathname === domains?.url?.pathname ? "text-white" : "text-neutral-400"}
|
|
47
|
-
classIcon={pathname === domains?.url?.pathname ? "text-slate-500" : ""}
|
|
48
|
-
title={name}
|
|
49
|
-
subtitle={comments.length > 0 ? `${doneComments}/${totalComments} Done` : "No Comments"}
|
|
50
|
-
active={pathname === domains?.url?.pathname}
|
|
51
|
-
{index}
|
|
52
|
-
onclick={pathname === domains?.url?.pathname ? () => router.goto("comments") : null}
|
|
53
|
-
>
|
|
54
|
-
{#snippet start()}
|
|
55
|
-
{#if progress === 100}
|
|
56
|
-
<Icon name="check-circle" class="size-22 text-sky-500" />
|
|
57
|
-
{:else}
|
|
58
|
-
<Loader progress={progress} />
|
|
59
|
-
{/if}
|
|
60
|
-
{/snippet}
|
|
61
|
-
|
|
62
|
-
{#snippet after()}
|
|
63
|
-
{#if pathname === domains?.url?.pathname}<Chip theme="sky">Current</Chip>{/if}
|
|
64
|
-
{/snippet}
|
|
65
|
-
</ListItem>
|
|
66
|
-
</a>
|
|
67
|
-
{:else}
|
|
68
|
-
{#if pages?.loading}
|
|
69
|
-
<ListLoader spinner />
|
|
70
|
-
{:else if pages?.error}
|
|
71
|
-
<p>Error loading</p>
|
|
72
|
-
{/if}
|
|
73
|
-
{/each}
|
|
74
|
-
</List>
|
|
75
|
-
</Page>
|
package/src/pages/Thread.svelte
DELETED
package/src/styles/global.css
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
.fubi-scroll-disabled {
|
|
2
|
-
overflow: hidden !important;
|
|
3
|
-
touch-action: none;
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
@media (pointer: coarse) {
|
|
7
|
-
.fubi-scroll-touch-disabled,
|
|
8
|
-
.fubi-scroll-touch-disabled body {
|
|
9
|
-
overflow: hidden !important;
|
|
10
|
-
touch-action: none;
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
html {
|
|
15
|
-
touch-action: pan-y;
|
|
16
|
-
}
|
package/src/styles/router.css
DELETED
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
.page {
|
|
2
|
-
transform: translateX(calc(100% + var(--fubi-window-right))) rotateY(var(--fubi-rotate-to));
|
|
3
|
-
position: absolute;
|
|
4
|
-
transition: box-shadow 300ms ease-out;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
.page.page-current {
|
|
8
|
-
transform: translateX(0%);
|
|
9
|
-
position: relative;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
.window {
|
|
13
|
-
perspective: 1200px;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/* Router animations */
|
|
17
|
-
|
|
18
|
-
/* forward, next */
|
|
19
|
-
.router-transition-forward .page-next {
|
|
20
|
-
animation: next-to-current var(--fubi-router-duration-forward) forwards;
|
|
21
|
-
animation-timing-function: var(--ease-out-cubic);
|
|
22
|
-
z-index: 2;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/* forward, current */
|
|
26
|
-
.router-transition-forward .page-current {
|
|
27
|
-
animation: current-to-prev var(--fubi-router-duration-forward) forwards;
|
|
28
|
-
animation-timing-function: var(--ease-in-out);
|
|
29
|
-
z-index: 1;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/* back, previous */
|
|
33
|
-
.router-transition-back .page-current {
|
|
34
|
-
animation: current-to-next var(--fubi-router-duration-back) forwards;
|
|
35
|
-
animation-timing-function: cubic-bezier(0.2, 0, 0.4, 1);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/* back, current */
|
|
39
|
-
.router-transition-back .page-previous {
|
|
40
|
-
animation: prev-to-current var(--fubi-router-duration-forward) forwards;
|
|
41
|
-
animation-timing-function: var(--ease-out);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
@keyframes next-to-current {
|
|
45
|
-
from {
|
|
46
|
-
transform: translateX(calc(100% + var(--fubi-window-right))) rotateY(var(--fubi-rotate-to));
|
|
47
|
-
}
|
|
48
|
-
to {
|
|
49
|
-
transform: translateX(0%) rotateY(0deg);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
@keyframes current-to-next {
|
|
54
|
-
from {
|
|
55
|
-
transform: translateX(0%) rotateY(0deg);
|
|
56
|
-
}
|
|
57
|
-
to {
|
|
58
|
-
transform: translateX(calc(100% + var(--fubi-window-right))) rotateY(var(--fubi-rotate-to));
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
@keyframes current-to-prev {
|
|
63
|
-
from {
|
|
64
|
-
transform: translateX(0%) translateZ(0) rotateY(0deg);
|
|
65
|
-
}
|
|
66
|
-
to {
|
|
67
|
-
transform: translateX(var(--fubi-translate-x-difference)) translateZ(var(--fubi-translate-z-difference))
|
|
68
|
-
rotateY(0deg);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
@keyframes prev-to-current {
|
|
72
|
-
from {
|
|
73
|
-
transform: translateX(var(--fubi-translate-x-difference)) translateZ(var(--fubi-translate-z-difference))
|
|
74
|
-
rotateY(0deg);
|
|
75
|
-
}
|
|
76
|
-
to {
|
|
77
|
-
transform: translateX(0%) translateZ(0) rotateY(0deg);
|
|
78
|
-
}
|
|
79
|
-
}
|