@oasisomniverse/svelte 1.0.0

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.
Files changed (120) hide show
  1. package/package.json +36 -0
  2. package/src/components/AvatarConnect.svelte +61 -0
  3. package/src/components/AvatarProfile.svelte +124 -0
  4. package/src/components/ComingSoon.svelte +13 -0
  5. package/src/components/Confirmation.svelte +21 -0
  6. package/src/components/Contact.svelte +38 -0
  7. package/src/components/Eggs.svelte +40 -0
  8. package/src/components/ForgotPassword.svelte +67 -0
  9. package/src/components/Game.svelte +28 -0
  10. package/src/components/HyperDrive.svelte +42 -0
  11. package/src/components/KarmaPanel.svelte +74 -0
  12. package/src/components/KarmaToast.svelte +23 -0
  13. package/src/components/Login.svelte +88 -0
  14. package/src/components/Map.svelte +41 -0
  15. package/src/components/MenuMessage.svelte +59 -0
  16. package/src/components/Messaging.svelte +41 -0
  17. package/src/components/Mission.svelte +31 -0
  18. package/src/components/NFT.svelte +37 -0
  19. package/src/components/NavBar.svelte +63 -0
  20. package/src/components/OApp.svelte +36 -0
  21. package/src/components/ONET.svelte +35 -0
  22. package/src/components/ONODE.svelte +36 -0
  23. package/src/components/OasisModal.svelte +35 -0
  24. package/src/components/ProviderDropdown.svelte +35 -0
  25. package/src/components/Providers.svelte +29 -0
  26. package/src/components/Quest.svelte +34 -0
  27. package/src/components/ResetPassword.svelte +28 -0
  28. package/src/components/SearchAvatars.svelte +44 -0
  29. package/src/components/Seeds.svelte +39 -0
  30. package/src/components/Settings.svelte +59 -0
  31. package/src/components/Signup.svelte +115 -0
  32. package/src/components/StarField.svelte +47 -0
  33. package/src/components/VerifyEmail.svelte +18 -0
  34. package/src/components/Wallet.svelte +42 -0
  35. package/src/components/avatar/AvatarWallet.svelte +17 -0
  36. package/src/components/avatar/EditAvatar.svelte +17 -0
  37. package/src/components/avatar/SearchAvatar.svelte +17 -0
  38. package/src/components/avatar/ViewAvatar.svelte +17 -0
  39. package/src/components/comingsoon/ComingSoon.svelte +17 -0
  40. package/src/components/confirmation/Confirmation.svelte +17 -0
  41. package/src/components/contact/Contact.svelte +17 -0
  42. package/src/components/data-screen/AddData.svelte +17 -0
  43. package/src/components/data-screen/CrossChainManagement.svelte +17 -0
  44. package/src/components/data-screen/LoadData.svelte +17 -0
  45. package/src/components/data-screen/ManageData.svelte +17 -0
  46. package/src/components/data-screen/OffChainManagement.svelte +17 -0
  47. package/src/components/data-screen/SearchData.svelte +17 -0
  48. package/src/components/eggs/ManageEggs.svelte +17 -0
  49. package/src/components/eggs/SearchEggs.svelte +17 -0
  50. package/src/components/eggs/ViewEggs.svelte +17 -0
  51. package/src/components/game/SearchProfiles.svelte +17 -0
  52. package/src/components/game/ViewAchievements.svelte +17 -0
  53. package/src/components/game/ViewLeagues.svelte +17 -0
  54. package/src/components/game/ViewTournaments.svelte +17 -0
  55. package/src/components/karma/SearchKarma.svelte +17 -0
  56. package/src/components/karma/ViewAvatarKarma.svelte +17 -0
  57. package/src/components/karma/ViewKarma.svelte +17 -0
  58. package/src/components/karma/VoteKarma.svelte +17 -0
  59. package/src/components/map/Add2dObjectMap.svelte +17 -0
  60. package/src/components/map/Add3dObjectMap.svelte +17 -0
  61. package/src/components/map/AddQuestToMap.svelte +17 -0
  62. package/src/components/map/DownloadOurWorld.svelte +17 -0
  63. package/src/components/map/ManageMap.svelte +17 -0
  64. package/src/components/map/PlotRouteOnMap.svelte +17 -0
  65. package/src/components/map/SearchMap.svelte +17 -0
  66. package/src/components/map/ViewGlobal3dMap.svelte +17 -0
  67. package/src/components/map/ViewHalonsOnMap.svelte +17 -0
  68. package/src/components/map/ViewOappOnMap.svelte +17 -0
  69. package/src/components/map/ViewQuestOnMap.svelte +17 -0
  70. package/src/components/messages/Message.svelte +17 -0
  71. package/src/components/mission/ManageMission.svelte +17 -0
  72. package/src/components/mission/SearchMission.svelte +17 -0
  73. package/src/components/mission/ViewMission.svelte +17 -0
  74. package/src/components/nft/ContactPopup.svelte +17 -0
  75. package/src/components/nft/ManageOasisNft.svelte +17 -0
  76. package/src/components/nft/PurchaseOasisNft.svelte +17 -0
  77. package/src/components/nft/PurchaseOasisVirtualLandNft.svelte +17 -0
  78. package/src/components/nft/SearchOasisNft.svelte +17 -0
  79. package/src/components/nft/Solana.svelte +17 -0
  80. package/src/components/nft/ViewOasisNft.svelte +17 -0
  81. package/src/components/oapp/CreateOAPP.svelte +17 -0
  82. package/src/components/oapp/DeployOAPP.svelte +17 -0
  83. package/src/components/oapp/DownloadOurWorld.svelte +17 -0
  84. package/src/components/oapp/EditOAPP.svelte +17 -0
  85. package/src/components/oapp/InstallOAPP.svelte +17 -0
  86. package/src/components/oapp/LaunchOAPP.svelte +17 -0
  87. package/src/components/oapp/ManageOAPP.svelte +17 -0
  88. package/src/components/oapp/SearchOAPP.svelte +17 -0
  89. package/src/components/provider/ActivityPub.svelte +17 -0
  90. package/src/components/provider/CompareProviderSpeeds.svelte +17 -0
  91. package/src/components/provider/Eosio.svelte +17 -0
  92. package/src/components/provider/Ethereum.svelte +17 -0
  93. package/src/components/provider/Holochain.svelte +17 -0
  94. package/src/components/provider/Ipfs.svelte +17 -0
  95. package/src/components/provider/ManageAutoFailOver.svelte +17 -0
  96. package/src/components/provider/ManageAutoReplicaton.svelte +17 -0
  97. package/src/components/provider/ManageLoadBalancing.svelte +17 -0
  98. package/src/components/provider/ManageProviders.svelte +17 -0
  99. package/src/components/provider/MongoDb.svelte +17 -0
  100. package/src/components/provider/Neo4j.svelte +17 -0
  101. package/src/components/provider/SearchProviders.svelte +17 -0
  102. package/src/components/provider/Seeds.svelte +17 -0
  103. package/src/components/provider/Solid.svelte +17 -0
  104. package/src/components/provider/SqlLite.svelte +17 -0
  105. package/src/components/provider/ThreeFold.svelte +17 -0
  106. package/src/components/provider/ViewProviderStats.svelte +17 -0
  107. package/src/components/provider/ViewProviders.svelte +17 -0
  108. package/src/components/quest/ManageQuest.svelte +17 -0
  109. package/src/components/quest/SearchQuest.svelte +17 -0
  110. package/src/components/quest/ViewQuest.svelte +17 -0
  111. package/src/components/seeds/AcceptInvite.svelte +17 -0
  112. package/src/components/seeds/DonateSeeds.svelte +17 -0
  113. package/src/components/seeds/ManageSeeds.svelte +17 -0
  114. package/src/components/seeds/PayWithSeeds.svelte +17 -0
  115. package/src/components/seeds/RewardSeeds.svelte +17 -0
  116. package/src/components/seeds/SearchSeeds.svelte +17 -0
  117. package/src/components/seeds/SendInvite.svelte +17 -0
  118. package/src/components/seeds/ViewOrganizations.svelte +17 -0
  119. package/src/components/seeds/ViewSeeds.svelte +17 -0
  120. package/src/index.ts +139 -0
@@ -0,0 +1,88 @@
1
+ <script lang="ts">
2
+ import { createEventDispatcher } from 'svelte'
3
+ import { login } from './oasisStore'
4
+
5
+ const dispatch = createEventDispatcher<{ switchTo: 'signup' | 'forgot'; loggedIn: void }>()
6
+
7
+ let username = ''
8
+ let password = ''
9
+ let rememberMe = false
10
+ let showPwd = false
11
+ let loading = false
12
+ let error = ''
13
+
14
+ async function submit() {
15
+ if (!username || !password) { error = 'Please fill in all fields.'; return }
16
+ loading = true
17
+ error = ''
18
+ try {
19
+ await login(username, password)
20
+ dispatch('loggedIn')
21
+ } catch (e: any) {
22
+ error = e?.message ?? 'Login failed. Please check your credentials.'
23
+ } finally {
24
+ loading = false
25
+ }
26
+ }
27
+ </script>
28
+
29
+ <div class="oasis-auth-form">
30
+ <div class="oasis-auth-header">
31
+ <h2 class="oasis-auth-title">Beam In</h2>
32
+ <p class="oasis-auth-sub">
33
+ Don't have an account?
34
+ <a class="oasis-link" href="#" on:click|preventDefault={() => dispatch('switchTo', 'signup')}>Sign Up</a>
35
+ </p>
36
+ </div>
37
+
38
+ {#if error}
39
+ <div class="oasis-auth-error">{error}</div>
40
+ {/if}
41
+
42
+ <div class="oasis-field">
43
+ <label class="oasis-label" for="oasis-login-username">Username</label>
44
+ <input class="oasis-input" id="oasis-login-username" type="text" bind:value={username} placeholder="yourusername" autocomplete="username" />
45
+ </div>
46
+
47
+ <div class="oasis-field">
48
+ <label class="oasis-label" for="oasis-login-password">Password</label>
49
+ <div class="oasis-input-wrap">
50
+ <input class="oasis-input" id="oasis-login-password" type={showPwd ? 'text' : 'password'} bind:value={password} placeholder="••••••••" autocomplete="current-password" />
51
+ <button class="oasis-pwd-toggle" type="button" on:click={() => showPwd = !showPwd} aria-label={showPwd ? 'Hide password' : 'Show password'}>
52
+ {showPwd ? '🙈' : '👁️'}
53
+ </button>
54
+ </div>
55
+ <a class="oasis-link oasis-link--small" href="#" on:click|preventDefault={() => dispatch('switchTo', 'forgot')}>Forgot Password?</a>
56
+ </div>
57
+
58
+ <div class="oasis-field oasis-field--row">
59
+ <input class="oasis-checkbox" id="oasis-remember" type="checkbox" bind:checked={rememberMe} />
60
+ <label class="oasis-label oasis-label--inline" for="oasis-remember">Remember Me</label>
61
+ </div>
62
+
63
+ <button class="oasis-btn" type="button" disabled={loading} on:click={submit}>
64
+ {loading ? 'Connecting…' : 'Beam In'}
65
+ </button>
66
+ </div>
67
+
68
+ <style>
69
+ .oasis-auth-form { display:flex; flex-direction:column; gap:20px; }
70
+ .oasis-auth-header { text-align:center; }
71
+ .oasis-auth-title { font-family:'Orbitron',sans-serif; font-size:22px; color:#fff; margin:0 0 6px; }
72
+ .oasis-auth-sub { font-size:13px; color:#7a9bbf; margin:0; }
73
+ .oasis-auth-error { background:rgba(255,80,80,.12); border:1px solid rgba(255,80,80,.3); color:#ff6b6b; border-radius:8px; padding:10px 14px; font-size:13px; }
74
+ .oasis-field { display:flex; flex-direction:column; gap:6px; }
75
+ .oasis-field--row { flex-direction:row; align-items:center; gap:10px; }
76
+ .oasis-label { font-size:12px; font-weight:600; letter-spacing:.06em; color:#7a9bbf; text-transform:uppercase; }
77
+ .oasis-label--inline { text-transform:none; font-size:13px; color:#a8bfd8; }
78
+ .oasis-input-wrap { position:relative; }
79
+ .oasis-input { width:100%; background:rgba(255,255,255,.05); border:1px solid rgba(0,200,255,.2); border-radius:8px; padding:10px 14px; color:#fff; font-size:14px; outline:none; box-sizing:border-box; transition:border-color .2s; }
80
+ .oasis-input:focus { border-color:rgba(0,200,255,.5); }
81
+ .oasis-input-wrap .oasis-input { padding-right:44px; }
82
+ .oasis-pwd-toggle { position:absolute; right:10px; top:50%; transform:translateY(-50%); background:none; border:none; cursor:pointer; font-size:16px; padding:0; line-height:1; }
83
+ .oasis-checkbox { width:16px; height:16px; accent-color:#00c8ff; cursor:pointer; flex-shrink:0; }
84
+ .oasis-link { color:#00c8ff; text-decoration:none; cursor:pointer; }
85
+ .oasis-link--small { font-size:12px; margin-top:2px; }
86
+ .oasis-btn { background:linear-gradient(135deg,#00c8ff,#0080ff); border:none; border-radius:8px; color:#fff; font-family:'Orbitron',sans-serif; font-size:13px; font-weight:700; letter-spacing:.08em; padding:12px; cursor:pointer; transition:opacity .2s; }
87
+ .oasis-btn:disabled { opacity:.5; cursor:not-allowed; }
88
+ </style>
@@ -0,0 +1,41 @@
1
+ <script lang="ts">
2
+ interface Location { id: string; name: string; type: string; x: number; y: number; description: string }
3
+ let selected: Location | null = null
4
+ const locations: Location[] = [
5
+ { id: '1', name: 'OASIS Hub Alpha', type: 'Hub', x: 20, y: 30, description: 'Central hub for OASIS operations and avatar registration.' },
6
+ { id: '2', name: 'Neon City', type: 'City', x: 55, y: 20, description: 'A sprawling cyberpunk metropolis in the OASIS.' },
7
+ { id: '3', name: 'Enchanted Forest', type: 'Nature', x: 75, y: 60, description: 'Ancient woodland teeming with rare seeds and creatures.' },
8
+ { id: '4', name: 'Battle Grounds', type: 'Quest Zone', x: 35, y: 70, description: 'Arena for quests, challenges, and PvP competitions.' },
9
+ { id: '5', name: 'Crystal Spire', type: 'Hub', x: 85, y: 35, description: 'Trading post and NFT marketplace in the sky.' },
10
+ ]
11
+ const types = [{ icon: '🏙️', label: 'City' }, { icon: '🌲', label: 'Nature' }, { icon: '⚔️', label: 'Quest Zone' }, { icon: '🏛️', label: 'Hub' }]
12
+ const icons: Record<string, string> = { Hub: '🏛️', City: '🏙️', Nature: '🌲', 'Quest Zone': '⚔️' }
13
+ function locIcon(type: string) { return icons[type] ?? '📍' }
14
+ </script>
15
+ <div class="map-shell">
16
+ <div class="map-header"><h2 class="map-title">🗺️ OASIS Map</h2><p class="map-sub">Explore the OASIS Omniverse.</p></div>
17
+ <div class="map-viewport">
18
+ <div class="map-grid-bg"></div>
19
+ {#each locations as loc (loc.id)}
20
+ <div class="map-pin" class:map-pin--selected={selected?.id === loc.id} style="left:{loc.x}%;top:{loc.y}%" on:click={() => selected = loc} title={loc.name}>
21
+ <div class="map-pin-dot">{locIcon(loc.type)}</div>
22
+ </div>
23
+ {/each}
24
+ </div>
25
+ {#if selected}
26
+ <div class="map-detail">
27
+ <div class="map-detail-name">{selected.name}</div>
28
+ <div class="map-detail-type">{selected.type}</div>
29
+ <div class="map-detail-desc">{selected.description}</div>
30
+ <button class="map-btn" on:click={() => selected = null}>Close</button>
31
+ </div>
32
+ {/if}
33
+ <div class="map-legend">
34
+ {#each types as t}
35
+ <div class="map-legend-item"><span>{t.icon}</span><span>{t.label}</span></div>
36
+ {/each}
37
+ </div>
38
+ </div>
39
+ <style>
40
+ .map-shell{display:flex;flex-direction:column;gap:16px}.map-header{text-align:center}.map-title{font-family:'Orbitron',sans-serif;font-size:20px;color:#fff;margin:0 0 6px}.map-sub{font-size:13px;color:#7a9bbf;margin:0}.map-viewport{position:relative;height:300px;background:#030d1a;border:1px solid rgba(0,200,255,.2);border-radius:12px;overflow:hidden}.map-grid-bg{position:absolute;inset:0;background-image:linear-gradient(rgba(0,200,255,.05) 1px,transparent 1px),linear-gradient(90deg,rgba(0,200,255,.05) 1px,transparent 1px);background-size:40px 40px}.map-pin{position:absolute;transform:translate(-50%,-50%);cursor:pointer;transition:transform .2s}.map-pin:hover{transform:translate(-50%,-50%) scale(1.2)}.map-pin--selected :global(.map-pin-dot){outline:2px solid #00c8ff;outline-offset:3px;border-radius:50%}.map-pin-dot{font-size:22px}.map-detail{background:rgba(255,255,255,.04);border:1px solid rgba(0,200,255,.2);border-radius:10px;padding:14px 16px;display:flex;align-items:center;gap:12px}.map-detail-name{font-family:'Orbitron',sans-serif;font-size:13px;color:#fff;flex:0 0 auto}.map-detail-type{font-size:11px;color:#5ba8ff;text-transform:uppercase;letter-spacing:.06em;flex:0 0 auto}.map-detail-desc{font-size:12px;color:#7a9bbf;flex:1}.map-btn{background:transparent;border:1px solid rgba(0,200,255,.3);border-radius:7px;color:#00c8ff;font-family:'Orbitron',sans-serif;font-size:11px;font-weight:700;padding:6px 14px;cursor:pointer}.map-legend{display:flex;gap:16px;flex-wrap:wrap}.map-legend-item{display:flex;align-items:center;gap:6px;font-size:12px;color:#7a9bbf}
41
+ </style>
@@ -0,0 +1,59 @@
1
+ <script lang="ts">
2
+ import { createEventDispatcher } from 'svelte';
3
+
4
+ export let title = 'Notice';
5
+ export let message = '';
6
+ export let icon = 'ℹ️';
7
+ export let type: 'info' | 'success' | 'warning' | 'error' = 'info';
8
+ export let badge = '';
9
+ export let actions: Array<{ label: string; onClick: () => void }> = [];
10
+
11
+ const dispatch = createEventDispatcher();
12
+
13
+ const TYPE_COLORS = {
14
+ info: { border: 'rgba(0,200,255,.25)', bg: 'rgba(0,200,255,.05)' },
15
+ success: { border: 'rgba(72,220,130,.25)', bg: 'rgba(72,220,130,.05)' },
16
+ warning: { border: 'rgba(255,180,60,.25)', bg: 'rgba(255,180,60,.05)' },
17
+ error: { border: 'rgba(255,80,80,.25)', bg: 'rgba(255,80,80,.05)' },
18
+ };
19
+
20
+ let visible = true;
21
+ $: colors = TYPE_COLORS[type] ?? TYPE_COLORS.info;
22
+
23
+ function dismiss() { visible = false; dispatch('dismissed'); }
24
+ </script>
25
+
26
+ {#if visible}
27
+ <div class="mm-shell" style="border: 1px solid {colors.border}; background: {colors.bg}">
28
+ <div class="mm-header">
29
+ <span class="mm-icon">{icon}</span>
30
+ <span class="mm-title">{title}</span>
31
+ {#if badge}<span class="mm-badge">{badge}</span>{/if}
32
+ <button class="mm-close" on:click={dismiss} aria-label="Dismiss">✕</button>
33
+ </div>
34
+ <div class="mm-body">
35
+ {#if message}<p class="mm-msg">{message}</p>{/if}
36
+ {#if actions.length > 0}
37
+ <div class="mm-actions">
38
+ {#each actions as a, i}
39
+ <button class={i === 0 ? 'mm-btn' : 'mm-btn-ghost'} on:click={a.onClick}>{a.label}</button>
40
+ {/each}
41
+ </div>
42
+ {/if}
43
+ </div>
44
+ </div>
45
+ {/if}
46
+
47
+ <style>
48
+ .mm-shell { font-family: 'Rajdhani', sans-serif; border-radius: 12px; overflow: hidden; }
49
+ .mm-header { padding: 14px 18px; background: rgba(0,200,255,.07); border-bottom: 1px solid rgba(0,200,255,.12); display: flex; align-items: center; gap: 12px; }
50
+ .mm-icon { font-size: 20px; }
51
+ .mm-title { font-family: 'Orbitron', sans-serif; font-size: 15px; font-weight: 700; color: #fff; flex: 1; }
52
+ .mm-badge { display: inline-block; background: rgba(0,200,255,.12); border: 1px solid rgba(0,200,255,.3); color: #00c8ff; border-radius: 999px; font-size: 11px; font-weight: 700; letter-spacing: .08em; padding: 3px 12px; text-transform: uppercase; }
53
+ .mm-close { background: none; border: none; color: #7a9bbf; font-size: 18px; cursor: pointer; line-height: 1; }
54
+ .mm-body { padding: 18px; display: flex; flex-direction: column; gap: 12px; }
55
+ .mm-msg { font-size: 14px; color: #a8bfd8; line-height: 1.65; text-align: center; margin: 0; }
56
+ .mm-actions { display: flex; gap: 10px; justify-content: center; margin-top: 4px; }
57
+ .mm-btn { background: linear-gradient(135deg,#00c8ff,#0080ff); border: none; border-radius: 8px; color: #fff; font-family: 'Orbitron', sans-serif; font-size: 12px; font-weight: 700; letter-spacing: .08em; padding: 9px 22px; cursor: pointer; }
58
+ .mm-btn-ghost { background: transparent; border: 1px solid rgba(0,200,255,.3); border-radius: 8px; color: #00c8ff; font-family: 'Orbitron', sans-serif; font-size: 12px; font-weight: 700; letter-spacing: .08em; padding: 9px 22px; cursor: pointer; }
59
+ </style>
@@ -0,0 +1,41 @@
1
+ <script lang="ts">
2
+ interface Msg { id: string; from: string; preview: string; time: string; read: boolean; body: string }
3
+ let searchQuery = '', reply = '', selected: Msg | null = null
4
+ let messages: Msg[] = [
5
+ { id: '1', from: 'OASIS Team', preview: 'Welcome to the OASIS network!', time: 'Just now', read: false, body: 'Welcome to the OASIS Omniverse! Your avatar is now connected to the network. Explore NFTs, karma, quests and more.' },
6
+ { id: '2', from: 'System', preview: 'Your karma level has increased.', time: '2h ago', read: true, body: 'Congratulations! Your karma level has increased. Keep contributing to the community to unlock new features and NFTs.' },
7
+ ]
8
+ $: filtered = messages.filter(m => m.from.toLowerCase().includes(searchQuery.toLowerCase()) || m.preview.toLowerCase().includes(searchQuery.toLowerCase()))
9
+ function open(m: Msg) { m.read = true; selected = { ...m }; reply = ''; messages = [...messages] }
10
+ function sendReply() { if (!reply.trim()) return; reply = '' }
11
+ </script>
12
+ <div class="msg-shell">
13
+ <div class="msg-sidebar">
14
+ <div class="msg-search-wrap"><input class="msg-search" type="text" bind:value={searchQuery} placeholder="Search messages…" /></div>
15
+ <div class="msg-list">
16
+ {#each filtered as m (m.id)}
17
+ <div class="msg-item" class:msg-item--active={selected?.id === m.id} class:msg-item--unread={!m.read} on:click={() => open(m)}>
18
+ <div class="msg-from">{m.from}</div>
19
+ <div class="msg-preview">{m.preview}</div>
20
+ <div class="msg-time">{m.time}</div>
21
+ </div>
22
+ {/each}
23
+ {#if filtered.length === 0}<div class="msg-empty">No messages found.</div>{/if}
24
+ </div>
25
+ </div>
26
+ <div class="msg-main">
27
+ {#if selected}
28
+ <div class="msg-detail-header"><div class="msg-detail-from">{selected.from}</div><div class="msg-detail-time">{selected.time}</div></div>
29
+ <div class="msg-body">{selected.body}</div>
30
+ <div class="msg-reply-wrap">
31
+ <textarea class="msg-reply-input" bind:value={reply} placeholder="Write a reply…" rows="3"></textarea>
32
+ <button class="oasis-btn" disabled={!reply.trim()} on:click={sendReply}>Send</button>
33
+ </div>
34
+ {:else}
35
+ <div class="msg-placeholder">📬 Select a message to read it</div>
36
+ {/if}
37
+ </div>
38
+ </div>
39
+ <style>
40
+ .msg-shell{display:grid;grid-template-columns:280px 1fr;height:480px;border:1px solid rgba(0,200,255,.15);border-radius:12px;overflow:hidden}.msg-sidebar{border-right:1px solid rgba(0,200,255,.1);display:flex;flex-direction:column}.msg-search-wrap{padding:12px;border-bottom:1px solid rgba(0,200,255,.1)}.msg-search{width:100%;background:rgba(255,255,255,.05);border:1px solid rgba(0,200,255,.2);border-radius:8px;padding:8px 12px;color:#fff;font-size:13px;outline:none;box-sizing:border-box}.msg-list{flex:1;overflow-y:auto}.msg-item{padding:12px 14px;border-bottom:1px solid rgba(0,200,255,.07);cursor:pointer;transition:background .15s}.msg-item:hover{background:rgba(0,200,255,.06)}.msg-item--active{background:rgba(0,200,255,.1);border-left:3px solid #00c8ff}.msg-item--unread .msg-from{font-weight:700}.msg-from{font-size:13px;color:#e0f0ff;margin-bottom:3px}.msg-preview{font-size:12px;color:#7a9bbf;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.msg-time{font-size:11px;color:#4a6a88;margin-top:4px}.msg-empty{padding:24px;text-align:center;color:#4a6a88;font-size:13px}.msg-main{display:flex;flex-direction:column;background:rgba(0,0,0,.2)}.msg-placeholder{display:flex;align-items:center;justify-content:center;flex:1;color:#4a6a88;font-size:14px}.msg-detail-header{display:flex;justify-content:space-between;align-items:center;padding:16px 20px;border-bottom:1px solid rgba(0,200,255,.1)}.msg-detail-from{font-family:'Orbitron',sans-serif;font-size:14px;color:#fff}.msg-detail-time{font-size:12px;color:#4a6a88}.msg-body{flex:1;padding:20px;font-size:14px;color:#a8bfd8;line-height:1.7;overflow-y:auto}.msg-reply-wrap{padding:16px;border-top:1px solid rgba(0,200,255,.1);display:flex;flex-direction:column;gap:10px}.msg-reply-input{width:100%;background:rgba(255,255,255,.05);border:1px solid rgba(0,200,255,.2);border-radius:8px;padding:10px 14px;color:#fff;font-size:13px;outline:none;resize:none;box-sizing:border-box;font-family:inherit}.oasis-btn{align-self:flex-end;background:linear-gradient(135deg,#00c8ff,#0080ff);border:none;border-radius:8px;color:#fff;font-family:'Orbitron',sans-serif;font-size:12px;font-weight:700;letter-spacing:.08em;padding:9px 24px;cursor:pointer;transition:opacity .2s}.oasis-btn:disabled{opacity:.4;cursor:not-allowed}
41
+ </style>
@@ -0,0 +1,31 @@
1
+ <script lang="ts">
2
+ interface Mission { id: string; title: string; description: string; reward: number; status: 'available' | 'active' | 'complete' }
3
+ let missions: Mission[] = [
4
+ { id: '1', title: 'First Contact', description: 'Log into OASIS for the first time and explore the hub.', reward: 100, status: 'complete' },
5
+ { id: '2', title: 'Karma Cultivator', description: 'Earn 500 karma through positive actions in the OASIS.', reward: 250, status: 'active' },
6
+ { id: '3', title: 'NFT Hunter', description: 'Acquire your first rare NFT from the marketplace.', reward: 400, status: 'available' },
7
+ { id: '4', title: 'Seed Planter', description: 'Plant 3 seeds and nurture them to full growth.', reward: 150, status: 'available' },
8
+ ]
9
+ function statusLabel(s: string) { return s === 'available' ? 'Available' : s === 'active' ? 'In Progress' : 'Complete' }
10
+ function accept(m: Mission) { m.status = 'active'; missions = [...missions] }
11
+ function complete(m: Mission) { m.status = 'complete'; missions = [...missions] }
12
+ </script>
13
+ <div class="mis-shell">
14
+ <div class="mis-header"><h2 class="mis-title">🎯 Missions</h2><p class="mis-sub">Complete missions to earn karma and unlock rewards.</p></div>
15
+ <div class="mis-list">
16
+ {#each missions as m (m.id)}
17
+ <div class="mis-card mis-card--{m.status}">
18
+ <div class="mis-left"><div class="mis-title-text">{m.title}</div><div class="mis-desc">{m.description}</div></div>
19
+ <div class="mis-right">
20
+ <div class="mis-reward">+{m.reward} karma</div>
21
+ <div class="mis-status-badge mis-status-badge--{m.status}">{statusLabel(m.status)}</div>
22
+ {#if m.status === 'available'}<button class="mis-btn" on:click={() => accept(m)}>Accept</button>{/if}
23
+ {#if m.status === 'active'}<button class="mis-btn mis-btn--complete" on:click={() => complete(m)}>Complete</button>{/if}
24
+ </div>
25
+ </div>
26
+ {/each}
27
+ </div>
28
+ </div>
29
+ <style>
30
+ .mis-shell{display:flex;flex-direction:column;gap:18px}.mis-header{text-align:center}.mis-title{font-family:'Orbitron',sans-serif;font-size:20px;color:#fff;margin:0 0 6px}.mis-sub{font-size:13px;color:#7a9bbf;margin:0}.mis-list{display:flex;flex-direction:column;gap:12px}.mis-card{display:flex;align-items:center;gap:16px;background:rgba(255,255,255,.04);border:1px solid rgba(0,200,255,.12);border-radius:12px;padding:16px}.mis-card--active{border-color:rgba(0,200,255,.35);background:rgba(0,200,255,.05)}.mis-card--complete{border-color:rgba(72,220,130,.25);background:rgba(72,220,130,.04);opacity:.7}.mis-left{flex:1}.mis-title-text{font-family:'Orbitron',sans-serif;font-size:13px;color:#fff;margin-bottom:4px}.mis-desc{font-size:12px;color:#7a9bbf;line-height:1.5}.mis-right{display:flex;flex-direction:column;align-items:flex-end;gap:8px;flex-shrink:0}.mis-reward{font-size:12px;color:#48dc82;font-weight:600;white-space:nowrap}.mis-status-badge{font-size:10px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;border-radius:999px;padding:3px 10px;border:1px solid currentColor}.mis-status-badge--available{color:#7a9bbf;border-color:rgba(122,155,191,.3)}.mis-status-badge--active{color:#00c8ff;border-color:rgba(0,200,255,.35)}.mis-status-badge--complete{color:#48dc82;border-color:rgba(72,220,130,.3)}.mis-btn{background:linear-gradient(135deg,#00c8ff,#0080ff);border:none;border-radius:7px;color:#fff;font-family:'Orbitron',sans-serif;font-size:11px;font-weight:700;letter-spacing:.08em;padding:7px 16px;cursor:pointer;white-space:nowrap}.mis-btn--complete{background:linear-gradient(135deg,#48dc82,#00aa55)}
31
+ </style>
@@ -0,0 +1,37 @@
1
+ <script lang="ts">
2
+ interface NFT { id: string; name: string; rarity: string; type: string; karma: number; image: string }
3
+ const tabs = ['All', 'Avatar', 'Land', 'Item', 'Art']
4
+ let activeTab = 'All'
5
+ let nfts: NFT[] = [
6
+ { id: '1', name: 'Cosmic Warrior', rarity: 'Legendary', type: 'Avatar', karma: 500, image: '⚔️' },
7
+ { id: '2', name: 'Forest Realm', rarity: 'Epic', type: 'Land', karma: 250, image: '🌲' },
8
+ { id: '3', name: 'Quantum Blade', rarity: 'Rare', type: 'Item', karma: 100, image: '🗡️' },
9
+ { id: '4', name: 'Nebula Portrait', rarity: 'Common', type: 'Art', karma: 30, image: '🎨' },
10
+ { id: '5', name: 'Light Keeper', rarity: 'Epic', type: 'Avatar', karma: 200, image: '🌟' },
11
+ { id: '6', name: 'Sky Citadel', rarity: 'Rare', type: 'Land', karma: 120, image: '🏰' },
12
+ ]
13
+ $: filtered = activeTab === 'All' ? nfts : nfts.filter(n => n.type === activeTab)
14
+ </script>
15
+ <div class="nft-shell">
16
+ <div class="nft-header"><h2 class="nft-title">🖼️ NFTs</h2><p class="nft-sub">Your OASIS non-fungible token collection.</p></div>
17
+ <div class="nft-tabs">
18
+ {#each tabs as t}
19
+ <button class="nft-tab" class:nft-tab--active={activeTab === t} on:click={() => activeTab = t}>{t}</button>
20
+ {/each}
21
+ </div>
22
+ <div class="nft-grid">
23
+ {#each filtered as n (n.id)}
24
+ <div class="nft-card">
25
+ <div class="nft-img">{n.image}</div>
26
+ <div class="nft-rarity nft-rarity--{n.rarity.toLowerCase()}">{n.rarity}</div>
27
+ <div class="nft-name">{n.name}</div>
28
+ <div class="nft-type">{n.type}</div>
29
+ <div class="nft-karma">+{n.karma} karma</div>
30
+ </div>
31
+ {/each}
32
+ {#if filtered.length === 0}<div class="nft-empty">No NFTs in this category.</div>{/if}
33
+ </div>
34
+ </div>
35
+ <style>
36
+ .nft-shell{display:flex;flex-direction:column;gap:18px}.nft-header{text-align:center}.nft-title{font-family:'Orbitron',sans-serif;font-size:20px;color:#fff;margin:0 0 6px}.nft-sub{font-size:13px;color:#7a9bbf;margin:0}.nft-tabs{display:flex;gap:8px;flex-wrap:wrap}.nft-tab{background:rgba(255,255,255,.05);border:1px solid rgba(0,200,255,.2);border-radius:8px;color:#7a9bbf;font-family:'Orbitron',sans-serif;font-size:11px;font-weight:700;letter-spacing:.06em;padding:7px 16px;cursor:pointer;transition:all .2s}.nft-tab--active{background:rgba(0,200,255,.15);border-color:#00c8ff;color:#00c8ff}.nft-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:14px}.nft-card{background:rgba(255,255,255,.04);border:1px solid rgba(0,200,255,.12);border-radius:12px;padding:14px;display:flex;flex-direction:column;align-items:center;gap:8px;transition:border-color .2s}.nft-card:hover{border-color:rgba(0,200,255,.35)}.nft-img{font-size:44px}.nft-rarity{font-size:10px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;border-radius:999px;padding:3px 10px;border:1px solid currentColor}.nft-rarity--common{color:#7a9bbf;border-color:rgba(122,155,191,.3)}.nft-rarity--rare{color:#5ba8ff;border-color:rgba(91,168,255,.3)}.nft-rarity--epic{color:#b87fff;border-color:rgba(184,127,255,.3)}.nft-rarity--legendary{color:#ffb43c;border-color:rgba(255,180,60,.3)}.nft-name{font-family:'Orbitron',sans-serif;font-size:12px;color:#fff;text-align:center}.nft-type{font-size:11px;color:#7a9bbf}.nft-karma{font-size:12px;color:#48dc82;font-weight:600}.nft-empty{grid-column:1/-1;text-align:center;color:#4a6a88;padding:32px;font-size:14px}
37
+ </style>
@@ -0,0 +1,63 @@
1
+ <script lang="ts">
2
+ import { onMount, onDestroy } from 'svelte'
3
+ import AvatarConnect from './AvatarConnect.svelte'
4
+
5
+ let menuOpen = false
6
+ let scrolled = false
7
+
8
+ const links = [
9
+ { href: '#mission', label: 'Mission' },
10
+ { href: '#disciplines', label: 'Disciplines' },
11
+ { href: '#courses', label: 'Courses' },
12
+ { href: '#origins', label: 'Origins' },
13
+ { href: '#forum', label: 'Forum' },
14
+ { href: '#features', label: 'Features' },
15
+ { href: '#ecosystem', label: 'Ecosystem' },
16
+ { href: '#karma', label: 'Karma' },
17
+ ]
18
+
19
+ function onScroll() { scrolled = window.scrollY > 40 }
20
+ onMount(() => window.addEventListener('scroll', onScroll))
21
+ onDestroy(() => window.removeEventListener('scroll', onScroll))
22
+ </script>
23
+
24
+ <nav class="nav" class:scrolled>
25
+ <a class="nav-brand" href="#">
26
+ <span class="brand-icon">⚖</span>
27
+ <span>JUSTICE <em>LEAGUE</em> ACADEMY</span>
28
+ </a>
29
+ <button class="nav-toggle" class:open={menuOpen} on:click={() => menuOpen = !menuOpen} aria-label="Menu">
30
+ <span /><span /><span />
31
+ </button>
32
+ <ul class="nav-links" class:open={menuOpen}>
33
+ {#each links as link}
34
+ <li><a href={link.href} on:click={() => menuOpen = false}>{link.label}</a></li>
35
+ {/each}
36
+ </ul>
37
+ <div class="nav-right">
38
+ <AvatarConnect />
39
+ </div>
40
+ </nav>
41
+
42
+ <style>
43
+ .nav { position: fixed; top: 0; left: 0; right: 0; z-index: 100; padding: 18px 40px; display: flex; align-items: center; justify-content: space-between; background: rgba(3,7,20,.88); backdrop-filter: blur(12px); border-bottom: 1px solid rgba(59,130,246,.12); transition: padding .3s; }
44
+ .nav.scrolled { padding: 12px 40px; }
45
+ .nav-brand { font-family: 'Orbitron', sans-serif; font-size: 11px; font-weight: 700; letter-spacing: .16em; color: #fff; text-decoration: none; display: flex; align-items: center; gap: 10px; white-space: nowrap; }
46
+ .brand-icon { color: #3b82f6; font-size: 16px; }
47
+ :global(.nav-brand em) { color: #a855f7; font-style: normal; }
48
+ .nav-links { display: flex; gap: 16px; list-style: none; padding: 0; }
49
+ .nav-links :global(a) { font-family: 'Share Tech Mono', monospace; font-size: 11px; letter-spacing: .07em; color: #a8bfd8; text-decoration: none; text-transform: uppercase; transition: color .2s; }
50
+ .nav-links :global(a:hover) { color: #3b82f6; }
51
+ .nav-right { display: flex; align-items: center; gap: 10px; flex-shrink: 0; }
52
+ .nav-toggle { display: none; flex-direction: column; justify-content: center; gap: 5px; width: 32px; height: 32px; background: none; border: none; cursor: pointer; }
53
+ .nav-toggle span { display: block; width: 100%; height: 2px; background: #a8bfd8; transition: transform .25s, opacity .25s; }
54
+ .nav-toggle.open span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
55
+ .nav-toggle.open span:nth-child(2) { opacity: 0; }
56
+ .nav-toggle.open span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }
57
+ @media (max-width: 1000px) {
58
+ .nav-toggle { display: flex; }
59
+ .nav-links { position: fixed; top: 0; right: 0; height: 100vh; width: min(78vw,320px); flex-direction: column; justify-content: flex-start; align-items: flex-start; gap: 14px; padding: 76px 32px 24px; overflow-y: auto; background: rgba(3,7,20,.97); backdrop-filter: blur(12px); border-left: 1px solid rgba(59,130,246,.12); transform: translateX(100%); transition: transform .3s ease; }
60
+ .nav-links.open { transform: translateX(0); }
61
+ .nav { padding: 14px 20px; }
62
+ }
63
+ </style>
@@ -0,0 +1,36 @@
1
+ <script lang="ts">
2
+ interface OApp { id: string; name: string; description: string; category: string; icon: string; karma: number; launched: boolean }
3
+ let search = ''
4
+ let oapps: OApp[] = [
5
+ { id: '1', name: 'OLAND', description: 'Virtual land management and terraforming in the OASIS.', category: 'Metaverse', icon: '🌍', karma: 200, launched: false },
6
+ { id: '2', name: 'OMARKET', description: 'Decentralised marketplace for NFTs and digital goods.', category: 'Commerce', icon: '🛒', karma: 150, launched: false },
7
+ { id: '3', name: 'OCHAT', description: 'Encrypted peer-to-peer messaging on the ONET.', category: 'Social', icon: '💬', karma: 80, launched: false },
8
+ { id: '4', name: 'OQUESTS', description: 'Daily quests and challenges for karma rewards.', category: 'Gaming', icon: '⚔️', karma: 300, launched: false },
9
+ ]
10
+ $: filtered = oapps.filter(a => a.name.toLowerCase().includes(search.toLowerCase()) || a.description.toLowerCase().includes(search.toLowerCase()))
11
+ function toggle(a: OApp) { a.launched = !a.launched; oapps = [...oapps] }
12
+ </script>
13
+ <div class="oapp-shell">
14
+ <div class="oapp-header"><h2 class="oapp-title">📱 OApps</h2><p class="oapp-sub">Decentralised apps built on the OASIS network.</p></div>
15
+ <input class="oasis-input" type="text" bind:value={search} placeholder="Search OApps…" />
16
+ <div class="oapp-grid">
17
+ {#each filtered as a (a.id)}
18
+ <div class="oapp-card">
19
+ <div class="oapp-icon">{a.icon}</div>
20
+ <div class="oapp-info">
21
+ <div class="oapp-name">{a.name}</div>
22
+ <div class="oapp-cat">{a.category}</div>
23
+ <div class="oapp-desc">{a.description}</div>
24
+ </div>
25
+ <div class="oapp-footer">
26
+ <div class="oapp-karma">+{a.karma} karma</div>
27
+ <button class="oapp-btn" class:oapp-btn--launched={a.launched} on:click={() => toggle(a)}>{a.launched ? 'Quit' : 'Launch'}</button>
28
+ </div>
29
+ </div>
30
+ {/each}
31
+ {#if filtered.length === 0}<div class="oapp-empty">No OApps found.</div>{/if}
32
+ </div>
33
+ </div>
34
+ <style>
35
+ .oapp-shell{display:flex;flex-direction:column;gap:18px}.oapp-header{text-align:center}.oapp-title{font-family:'Orbitron',sans-serif;font-size:20px;color:#fff;margin:0 0 6px}.oapp-sub{font-size:13px;color:#7a9bbf;margin:0}.oasis-input{width:100%;background:rgba(255,255,255,.05);border:1px solid rgba(0,200,255,.2);border-radius:8px;padding:10px 14px;color:#fff;font-size:14px;outline:none;box-sizing:border-box;transition:border-color .2s;font-family:inherit}.oasis-input:focus{border-color:rgba(0,200,255,.5)}.oapp-grid{display:flex;flex-direction:column;gap:12px}.oapp-card{display:flex;align-items:flex-start;gap:14px;background:rgba(255,255,255,.04);border:1px solid rgba(0,200,255,.12);border-radius:12px;padding:16px;transition:border-color .2s}.oapp-card:hover{border-color:rgba(0,200,255,.3)}.oapp-icon{font-size:32px;flex-shrink:0}.oapp-info{flex:1}.oapp-name{font-family:'Orbitron',sans-serif;font-size:13px;color:#fff;margin-bottom:3px}.oapp-cat{font-size:11px;color:#5ba8ff;text-transform:uppercase;letter-spacing:.06em;margin-bottom:5px}.oapp-desc{font-size:12px;color:#7a9bbf;line-height:1.5}.oapp-footer{display:flex;flex-direction:column;align-items:flex-end;gap:8px;flex-shrink:0}.oapp-karma{font-size:11px;color:#48dc82;font-weight:600;white-space:nowrap}.oapp-btn{background:linear-gradient(135deg,#00c8ff,#0080ff);border:none;border-radius:7px;color:#fff;font-family:'Orbitron',sans-serif;font-size:11px;font-weight:700;letter-spacing:.08em;padding:7px 16px;cursor:pointer;transition:opacity .2s;white-space:nowrap}.oapp-btn--launched{background:rgba(255,80,80,.2);border:1px solid rgba(255,80,80,.4);color:#ff6b6b}.oapp-empty{text-align:center;color:#4a6a88;font-size:14px;padding:32px}
36
+ </style>
@@ -0,0 +1,35 @@
1
+ <script lang="ts">
2
+ let connected = false
3
+ const stats = [{ label: 'Peers', value: '2,847' }, { label: 'Speed', value: '1.2 Gb/s' }, { label: 'Encrypted', value: '100%' }]
4
+ const peers = [
5
+ { id: '1', name: 'AvatarX_7291', location: 'New York, US', ping: 14 },
6
+ { id: '2', name: 'StarKeeper_99', location: 'London, UK', ping: 32 },
7
+ { id: '3', name: 'NeonRider_404', location: 'Tokyo, JP', ping: 67 },
8
+ ]
9
+ </script>
10
+ <div class="onet-shell">
11
+ <div class="onet-header"><h2 class="onet-title">🌐 ONET</h2><p class="onet-sub">The OASIS decentralised internet layer.</p></div>
12
+ <div class="onet-status-card" class:onet-status--active={connected}>
13
+ <div class="onet-status-dot" class:onet-status-dot--active={connected}></div>
14
+ <div class="onet-status-info"><div class="onet-status-label">ONET Connection</div><div class="onet-status-val">{connected ? 'Connected' : 'Disconnected'}</div></div>
15
+ <button class="onet-btn" on:click={() => connected = !connected}>{connected ? 'Disconnect' : 'Connect'}</button>
16
+ </div>
17
+ {#if connected}
18
+ <div class="onet-stats">
19
+ {#each stats as s}<div class="onet-stat"><div class="onet-stat-val">{s.value}</div><div class="onet-stat-label">{s.label}</div></div>{/each}
20
+ </div>
21
+ <div class="onet-peers-header">Active Peers</div>
22
+ <div class="onet-peers">
23
+ {#each peers as p (p.id)}
24
+ <div class="onet-peer">
25
+ <div class="onet-peer-icon">👤</div>
26
+ <div class="onet-peer-info"><div class="onet-peer-name">{p.name}</div><div class="onet-peer-location">{p.location}</div></div>
27
+ <div class="onet-peer-ping">{p.ping}ms</div>
28
+ </div>
29
+ {/each}
30
+ </div>
31
+ {/if}
32
+ </div>
33
+ <style>
34
+ .onet-shell{display:flex;flex-direction:column;gap:18px}.onet-header{text-align:center}.onet-title{font-family:'Orbitron',sans-serif;font-size:20px;color:#fff;margin:0 0 6px}.onet-sub{font-size:13px;color:#7a9bbf;margin:0}.onet-status-card{display:flex;align-items:center;gap:14px;background:rgba(255,255,255,.04);border:1px solid rgba(0,200,255,.12);border-radius:12px;padding:16px}.onet-status--active{border-color:rgba(72,220,130,.3)}.onet-status-dot{width:12px;height:12px;border-radius:50%;background:#4a6a88;flex-shrink:0;transition:all .3s}.onet-status-dot--active{background:#48dc82;box-shadow:0 0 10px #48dc82}.onet-status-info{flex:1}.onet-status-label{font-size:12px;color:#7a9bbf;text-transform:uppercase;letter-spacing:.06em}.onet-status-val{font-family:'Orbitron',sans-serif;font-size:14px;color:#fff;margin-top:3px}.onet-btn{background:linear-gradient(135deg,#00c8ff,#0080ff);border:none;border-radius:8px;color:#fff;font-family:'Orbitron',sans-serif;font-size:12px;font-weight:700;letter-spacing:.08em;padding:9px 18px;cursor:pointer}.onet-stats{display:grid;grid-template-columns:repeat(3,1fr);gap:12px}.onet-stat{background:rgba(255,255,255,.04);border:1px solid rgba(0,200,255,.1);border-radius:10px;padding:14px;text-align:center}.onet-stat-val{font-family:'Share Tech Mono',monospace;font-size:18px;color:#00c8ff;font-weight:600}.onet-stat-label{font-size:11px;color:#7a9bbf;margin-top:4px;text-transform:uppercase;letter-spacing:.06em}.onet-peers-header{font-family:'Orbitron',sans-serif;font-size:12px;color:#7a9bbf;letter-spacing:.06em;text-transform:uppercase}.onet-peers{display:flex;flex-direction:column;gap:8px}.onet-peer{display:flex;align-items:center;gap:12px;padding:10px 14px;background:rgba(255,255,255,.03);border:1px solid rgba(0,200,255,.07);border-radius:8px}.onet-peer-icon{font-size:20px}.onet-peer-info{flex:1}.onet-peer-name{font-size:13px;color:#e0f0ff}.onet-peer-location{font-size:11px;color:#4a6a88;margin-top:2px}.onet-peer-ping{font-family:'Share Tech Mono',monospace;font-size:12px;color:#48dc82}
35
+ </style>
@@ -0,0 +1,36 @@
1
+ <script lang="ts">
2
+ let running = false
3
+ const metrics = [{ label: 'CPU', value: '18%' }, { label: 'RAM', value: '2.1 GB' }, { label: 'Storage', value: '47 GB' }, { label: 'Blocks', value: '12,450' }]
4
+ const logs = [
5
+ { time: '09:42:01', level: 'info', message: 'ONODE started. Connecting to ONET…' },
6
+ { time: '09:42:02', level: 'info', message: 'Connected to 12 peers.' },
7
+ { time: '09:42:05', level: 'info', message: 'Block #12450 validated.' },
8
+ { time: '09:42:08', level: 'warn', message: 'Peer ONODE-77 latency high: 340ms.' },
9
+ { time: '09:42:12', level: 'info', message: 'Data sync complete. 100% coverage.' },
10
+ ]
11
+ </script>
12
+ <div class="nd-shell">
13
+ <div class="nd-header"><h2 class="nd-title">🖥️ ONODE</h2><p class="nd-sub">Your personal OASIS node. Store, validate, and relay data.</p></div>
14
+ <div class="nd-status-row">
15
+ <div class="nd-status-pill" class:nd-status-pill--running={running}>{running ? '● Running' : '○ Stopped'}</div>
16
+ <button class="nd-btn" class:nd-btn--stop={running} on:click={() => running = !running}>{running ? 'Stop Node' : 'Start Node'}</button>
17
+ </div>
18
+ {#if running}
19
+ <div class="nd-metrics">
20
+ {#each metrics as m}<div class="nd-metric"><div class="nd-metric-val">{m.value}</div><div class="nd-metric-label">{m.label}</div></div>{/each}
21
+ </div>
22
+ <div class="nd-logs-header">Node Logs</div>
23
+ <div class="nd-logs">
24
+ {#each logs as l (l.time)}
25
+ <div class="nd-log nd-log--{l.level}">
26
+ <span class="nd-log-time">{l.time}</span>
27
+ <span class="nd-log-level">[{l.level.toUpperCase()}]</span>
28
+ <span class="nd-log-msg">{l.message}</span>
29
+ </div>
30
+ {/each}
31
+ </div>
32
+ {/if}
33
+ </div>
34
+ <style>
35
+ .nd-shell{display:flex;flex-direction:column;gap:18px}.nd-header{text-align:center}.nd-title{font-family:'Orbitron',sans-serif;font-size:20px;color:#fff;margin:0 0 6px}.nd-sub{font-size:13px;color:#7a9bbf;margin:0}.nd-status-row{display:flex;align-items:center;gap:14px}.nd-status-pill{flex:1;background:rgba(255,255,255,.04);border:1px solid rgba(255,80,80,.2);border-radius:8px;padding:10px 14px;font-family:'Orbitron',sans-serif;font-size:13px;color:#ff6b6b}.nd-status-pill--running{border-color:rgba(72,220,130,.3);color:#48dc82}.nd-btn{background:linear-gradient(135deg,#00c8ff,#0080ff);border:none;border-radius:8px;color:#fff;font-family:'Orbitron',sans-serif;font-size:12px;font-weight:700;letter-spacing:.08em;padding:10px 20px;cursor:pointer}.nd-btn--stop{background:rgba(255,80,80,.2);border:1px solid rgba(255,80,80,.35);color:#ff6b6b}.nd-metrics{display:grid;grid-template-columns:repeat(4,1fr);gap:10px}.nd-metric{background:rgba(255,255,255,.04);border:1px solid rgba(0,200,255,.1);border-radius:10px;padding:12px;text-align:center}.nd-metric-val{font-family:'Share Tech Mono',monospace;font-size:16px;color:#00c8ff;font-weight:600}.nd-metric-label{font-size:10px;color:#7a9bbf;margin-top:4px;text-transform:uppercase;letter-spacing:.06em}.nd-logs-header{font-family:'Orbitron',sans-serif;font-size:12px;color:#7a9bbf;letter-spacing:.06em;text-transform:uppercase}.nd-logs{background:#020a14;border:1px solid rgba(0,200,255,.1);border-radius:8px;padding:12px;display:flex;flex-direction:column;gap:5px;max-height:160px;overflow-y:auto;font-family:'Share Tech Mono',monospace}.nd-log{display:flex;gap:10px;font-size:11px}.nd-log-time{color:#4a6a88;flex-shrink:0}.nd-log--info .nd-log-level{color:#00c8ff}.nd-log--warn .nd-log-level{color:#ffb43c}.nd-log--error .nd-log-level{color:#ff6b6b}.nd-log-msg{color:#7a9bbf}
36
+ </style>
@@ -0,0 +1,35 @@
1
+ <script lang="ts">
2
+ import { scale } from 'svelte/transition'
3
+ export let open = false
4
+ export let accentColor = 'rgba(59,130,246,.3)'
5
+
6
+ import { createEventDispatcher } from 'svelte'
7
+ const dispatch = createEventDispatcher()
8
+ </script>
9
+
10
+ {#if open}
11
+ <!-- svelte-ignore a11y-click-events-have-key-events a11y-no-static-element-interactions -->
12
+ <div class="backdrop" on:click={() => dispatch('close')} transition:scale={{ duration: 200, start: 0.96 }}>
13
+ <div class="modal-box" style="border-color:{accentColor}" on:click|stopPropagation>
14
+ <button class="modal-close" on:click={() => dispatch('close')}>✕</button>
15
+ <slot />
16
+ </div>
17
+ </div>
18
+ {/if}
19
+
20
+ <style>
21
+ .backdrop {
22
+ position: fixed; inset: 0; z-index: 9000;
23
+ background: rgba(3,7,20,.85); backdrop-filter: blur(8px);
24
+ display: flex; align-items: center; justify-content: center; padding: 20px;
25
+ }
26
+ .modal-box {
27
+ background: #0a1535; border: 1px solid; border-radius: 20px;
28
+ padding: 40px 36px; max-width: 520px; width: 100%;
29
+ position: relative; max-height: 90vh; overflow-y: auto;
30
+ }
31
+ .modal-close {
32
+ position: absolute; top: 16px; right: 18px;
33
+ background: none; border: none; color: #a8bfd8; font-size: 20px; cursor: pointer;
34
+ }
35
+ </style>
@@ -0,0 +1,35 @@
1
+ <script lang="ts">
2
+ import { createEventDispatcher } from 'svelte'
3
+ interface Provider { id: string; name: string; icon: string; color: string }
4
+ const dispatch = createEventDispatcher<{ providerChange: Provider }>()
5
+ const providers: Provider[] = [
6
+ { id: 'ethereum', name: 'Ethereum', icon: '⟠', color: '#627eea' },
7
+ { id: 'solana', name: 'Solana', icon: '◎', color: '#9945ff' },
8
+ { id: 'polygon', name: 'Polygon', icon: '⬡', color: '#8247e5' },
9
+ { id: 'arbitrum', name: 'Arbitrum', icon: '🔵', color: '#2d9cdb' },
10
+ { id: 'eosio', name: 'EOSIO', icon: '🔮', color: '#443f54' },
11
+ { id: 'holochain', name: 'Holochain', icon: '⬡', color: '#00e5be' },
12
+ ]
13
+ let open = false, selected = providers[0]
14
+ function select(p: Provider) { selected = p; open = false; dispatch('providerChange', p) }
15
+ </script>
16
+ <div class="pd-shell">
17
+ <div class="pd-trigger" class:pd-trigger--open={open} on:click={() => open = !open}>
18
+ <div class="pd-icon" style="color:{selected.color}">{selected.icon}</div>
19
+ <div class="pd-label"><div class="pd-name">{selected.name}</div><div class="pd-sub">Active Provider</div></div>
20
+ <div class="pd-arrow" class:pd-arrow--open={open}>▼</div>
21
+ </div>
22
+ {#if open}
23
+ <div class="pd-menu">
24
+ {#each providers as p (p.id)}
25
+ <div class="pd-option" class:pd-option--selected={p.id === selected.id} on:click={() => select(p)}>
26
+ <div class="pd-opt-icon" style="color:{p.color}">{p.icon}</div>
27
+ <div class="pd-opt-name">{p.name}</div>
28
+ </div>
29
+ {/each}
30
+ </div>
31
+ {/if}
32
+ </div>
33
+ <style>
34
+ .pd-shell{position:relative}.pd-trigger{display:flex;align-items:center;gap:10px;background:rgba(255,255,255,.05);border:1px solid rgba(0,200,255,.25);border-radius:10px;padding:10px 14px;cursor:pointer;transition:border-color .2s;color:#fff}.pd-trigger:hover,.pd-trigger--open{border-color:rgba(0,200,255,.5)}.pd-icon{font-size:20px;width:28px;text-align:center}.pd-label{flex:1}.pd-name{font-family:'Orbitron',sans-serif;font-size:13px;font-weight:700;color:#fff}.pd-sub{font-size:11px;color:#7a9bbf;margin-top:2px}.pd-arrow{font-size:10px;color:#7a9bbf;transition:transform .2s}.pd-arrow--open{transform:rotate(180deg)}.pd-menu{position:absolute;top:calc(100% + 6px);left:0;right:0;background:#0d1b2e;border:1px solid rgba(0,200,255,.2);border-radius:10px;overflow:hidden;z-index:100;box-shadow:0 8px 32px rgba(0,0,0,.5)}.pd-option{display:flex;align-items:center;gap:10px;padding:10px 14px;cursor:pointer;transition:background .15s;color:#e0f0ff}.pd-option:hover{background:rgba(0,200,255,.1)}.pd-option--selected{background:rgba(0,200,255,.12);border-left:3px solid #00c8ff}.pd-opt-icon{font-size:18px;width:24px;text-align:center}.pd-opt-name{font-family:'Orbitron',sans-serif;font-size:12px;font-weight:700}
35
+ </style>
@@ -0,0 +1,29 @@
1
+ <script lang="ts">
2
+ interface Provider { id: string; name: string; icon: string; color: string; type: string; connected: boolean; description: string }
3
+ let providers: Provider[] = [
4
+ { id: '1', name: 'Ethereum', icon: '⟠', color: '#627eea', type: 'Blockchain', description: 'Connect your Ethereum wallet (MetaMask, WalletConnect).', connected: false },
5
+ { id: '2', name: 'Solana', icon: '◎', color: '#9945ff', type: 'Blockchain', description: 'Connect your Solana wallet (Phantom, Solflare).', connected: false },
6
+ { id: '3', name: 'EOSIO', icon: '🔮', color: '#443f54', type: 'Blockchain', description: 'Connect via EOSIO for high-speed transactions.', connected: false },
7
+ { id: '4', name: 'Holochain', icon: '⬡', color: '#00e5be', type: 'P2P Network', description: 'Peer-to-peer data storage on Holochain.', connected: false },
8
+ { id: '5', name: 'The Graph', icon: '📊', color: '#6f4cff', type: 'Indexer', description: 'Query blockchain data with The Graph protocol.', connected: false },
9
+ ]
10
+ function toggle(p: Provider) { p.connected = !p.connected; providers = [...providers] }
11
+ </script>
12
+ <div class="prov-shell">
13
+ <div class="prov-header"><h2 class="prov-title">🔗 Providers</h2><p class="prov-sub">Connect blockchain providers to your OASIS avatar.</p></div>
14
+ <div class="prov-list">
15
+ {#each providers as p (p.id)}
16
+ <div class="prov-card" class:prov-card--connected={p.connected}>
17
+ <div class="prov-icon" style="color:{p.color}">{p.icon}</div>
18
+ <div class="prov-info"><div class="prov-name">{p.name}</div><div class="prov-type">{p.type}</div><div class="prov-desc">{p.description}</div></div>
19
+ <div class="prov-actions">
20
+ <div class="prov-status-dot" class:prov-status-dot--on={p.connected}></div>
21
+ <button class="prov-btn" class:prov-btn--disconnect={p.connected} on:click={() => toggle(p)}>{p.connected ? 'Disconnect' : 'Connect'}</button>
22
+ </div>
23
+ </div>
24
+ {/each}
25
+ </div>
26
+ </div>
27
+ <style>
28
+ .prov-shell{display:flex;flex-direction:column;gap:18px}.prov-header{text-align:center}.prov-title{font-family:'Orbitron',sans-serif;font-size:20px;color:#fff;margin:0 0 6px}.prov-sub{font-size:13px;color:#7a9bbf;margin:0}.prov-list{display:flex;flex-direction:column;gap:10px}.prov-card{display:flex;align-items:center;gap:14px;background:rgba(255,255,255,.04);border:1px solid rgba(0,200,255,.12);border-radius:12px;padding:14px 16px;transition:border-color .2s}.prov-card--connected{border-color:rgba(72,220,130,.3);background:rgba(72,220,130,.03)}.prov-icon{font-size:26px;flex-shrink:0}.prov-info{flex:1}.prov-name{font-family:'Orbitron',sans-serif;font-size:13px;color:#fff;margin-bottom:2px}.prov-type{font-size:11px;color:#5ba8ff;text-transform:uppercase;letter-spacing:.06em;margin-bottom:4px}.prov-desc{font-size:12px;color:#7a9bbf}.prov-actions{display:flex;flex-direction:column;align-items:center;gap:8px;flex-shrink:0}.prov-status-dot{width:8px;height:8px;border-radius:50%;background:#4a6a88}.prov-status-dot--on{background:#48dc82;box-shadow:0 0 6px #48dc82}.prov-btn{background:linear-gradient(135deg,#00c8ff,#0080ff);border:none;border-radius:7px;color:#fff;font-family:'Orbitron',sans-serif;font-size:10px;font-weight:700;letter-spacing:.06em;padding:7px 14px;cursor:pointer;white-space:nowrap}.prov-btn--disconnect{background:rgba(255,80,80,.15);border:1px solid rgba(255,80,80,.3);color:#ff6b6b}
29
+ </style>