diff --git a/app/javascript/flavours/cybrespace/fonts/premillenium/MSSansSerif.ttf b/app/javascript/flavours/cybrespace/fonts/premillenium/MSSansSerif.ttf
new file mode 100644
index 0000000..3afd76f
Binary files /dev/null and b/app/javascript/flavours/cybrespace/fonts/premillenium/MSSansSerif.ttf differ
diff --git a/app/javascript/flavours/cybrespace/images/alert_badge.png b/app/javascript/flavours/cybrespace/images/alert_badge.png
new file mode 100644
index 0000000..681f6e6
Binary files /dev/null and b/app/javascript/flavours/cybrespace/images/alert_badge.png differ
diff --git a/app/javascript/flavours/cybrespace/images/background-cybre-light.png b/app/javascript/flavours/cybrespace/images/background-cybre-light.png
new file mode 100644
index 0000000..44d76c2
Binary files /dev/null and b/app/javascript/flavours/cybrespace/images/background-cybre-light.png differ
diff --git a/app/javascript/flavours/cybrespace/images/background-cybre.png b/app/javascript/flavours/cybrespace/images/background-cybre.png
new file mode 100644
index 0000000..151fd55
Binary files /dev/null and b/app/javascript/flavours/cybrespace/images/background-cybre.png differ
diff --git a/app/javascript/flavours/cybrespace/images/clippy_frame.png b/app/javascript/flavours/cybrespace/images/clippy_frame.png
new file mode 100644
index 0000000..7f2cd6a
Binary files /dev/null and b/app/javascript/flavours/cybrespace/images/clippy_frame.png differ
diff --git a/app/javascript/flavours/cybrespace/images/clippy_wave.gif b/app/javascript/flavours/cybrespace/images/clippy_wave.gif
new file mode 100644
index 0000000..4d2e38a
Binary files /dev/null and b/app/javascript/flavours/cybrespace/images/clippy_wave.gif differ
diff --git a/app/javascript/flavours/cybrespace/images/cybre-preview.png b/app/javascript/flavours/cybrespace/images/cybre-preview.png
new file mode 100644
index 0000000..810da51
Binary files /dev/null and b/app/javascript/flavours/cybrespace/images/cybre-preview.png differ
diff --git a/app/javascript/flavours/cybrespace/images/elephant-fren.png b/app/javascript/flavours/cybrespace/images/elephant-fren.png
new file mode 100644
index 0000000..3b64edf
Binary files /dev/null and b/app/javascript/flavours/cybrespace/images/elephant-fren.png differ
diff --git a/app/javascript/flavours/cybrespace/images/elephant_ui_disappointed.svg b/app/javascript/flavours/cybrespace/images/elephant_ui_disappointed.svg
new file mode 100644
index 0000000..580c15a
--- /dev/null
+++ b/app/javascript/flavours/cybrespace/images/elephant_ui_disappointed.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/app/javascript/flavours/cybrespace/images/elephant_ui_greeting.svg b/app/javascript/flavours/cybrespace/images/elephant_ui_greeting.svg
new file mode 100644
index 0000000..f3eb4b1
--- /dev/null
+++ b/app/javascript/flavours/cybrespace/images/elephant_ui_greeting.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/app/javascript/flavours/cybrespace/images/elephant_ui_plane.svg b/app/javascript/flavours/cybrespace/images/elephant_ui_plane.svg
new file mode 100644
index 0000000..ca675c9
--- /dev/null
+++ b/app/javascript/flavours/cybrespace/images/elephant_ui_plane.svg
@@ -0,0 +1 @@
+
diff --git a/app/javascript/flavours/cybrespace/images/elephant_ui_working.svg b/app/javascript/flavours/cybrespace/images/elephant_ui_working.svg
new file mode 100644
index 0000000..8ba475d
--- /dev/null
+++ b/app/javascript/flavours/cybrespace/images/elephant_ui_working.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/app/javascript/flavours/cybrespace/images/floppy-1.svg b/app/javascript/flavours/cybrespace/images/floppy-1.svg
new file mode 100644
index 0000000..08c1e4a
--- /dev/null
+++ b/app/javascript/flavours/cybrespace/images/floppy-1.svg
@@ -0,0 +1,64 @@
+
diff --git a/app/javascript/flavours/cybrespace/images/floppy-2.svg b/app/javascript/flavours/cybrespace/images/floppy-2.svg
new file mode 100644
index 0000000..d57a72a
--- /dev/null
+++ b/app/javascript/flavours/cybrespace/images/floppy-2.svg
@@ -0,0 +1,64 @@
+
diff --git a/app/javascript/flavours/cybrespace/images/floppy-3.svg b/app/javascript/flavours/cybrespace/images/floppy-3.svg
new file mode 100644
index 0000000..2fe8ef3
--- /dev/null
+++ b/app/javascript/flavours/cybrespace/images/floppy-3.svg
@@ -0,0 +1,64 @@
+
diff --git a/app/javascript/flavours/cybrespace/images/header-cybre-alt.jpg b/app/javascript/flavours/cybrespace/images/header-cybre-alt.jpg
new file mode 100644
index 0000000..4d2b6b3
Binary files /dev/null and b/app/javascript/flavours/cybrespace/images/header-cybre-alt.jpg differ
diff --git a/app/javascript/flavours/cybrespace/images/header-cybre-colour.jpg b/app/javascript/flavours/cybrespace/images/header-cybre-colour.jpg
new file mode 100644
index 0000000..2d1b5b4
Binary files /dev/null and b/app/javascript/flavours/cybrespace/images/header-cybre-colour.jpg differ
diff --git a/app/javascript/flavours/cybrespace/images/header-cybre.jpeg b/app/javascript/flavours/cybrespace/images/header-cybre.jpeg
new file mode 100644
index 0000000..e9d7b33
Binary files /dev/null and b/app/javascript/flavours/cybrespace/images/header-cybre.jpeg differ
diff --git a/app/javascript/flavours/cybrespace/images/icon_about.png b/app/javascript/flavours/cybrespace/images/icon_about.png
new file mode 100644
index 0000000..08b76dc
Binary files /dev/null and b/app/javascript/flavours/cybrespace/images/icon_about.png differ
diff --git a/app/javascript/flavours/cybrespace/images/icon_blocks.png b/app/javascript/flavours/cybrespace/images/icon_blocks.png
new file mode 100644
index 0000000..8b14908
Binary files /dev/null and b/app/javascript/flavours/cybrespace/images/icon_blocks.png differ
diff --git a/app/javascript/flavours/cybrespace/images/icon_bookmarks.png b/app/javascript/flavours/cybrespace/images/icon_bookmarks.png
new file mode 100644
index 0000000..b0cff13
Binary files /dev/null and b/app/javascript/flavours/cybrespace/images/icon_bookmarks.png differ
diff --git a/app/javascript/flavours/cybrespace/images/icon_cached.svg b/app/javascript/flavours/cybrespace/images/icon_cached.svg
new file mode 100644
index 0000000..1087c43
--- /dev/null
+++ b/app/javascript/flavours/cybrespace/images/icon_cached.svg
@@ -0,0 +1,2 @@
+
+
diff --git a/app/javascript/flavours/cybrespace/images/icon_developers.png b/app/javascript/flavours/cybrespace/images/icon_developers.png
new file mode 100644
index 0000000..c6d2e18
Binary files /dev/null and b/app/javascript/flavours/cybrespace/images/icon_developers.png differ
diff --git a/app/javascript/flavours/cybrespace/images/icon_direct.png b/app/javascript/flavours/cybrespace/images/icon_direct.png
new file mode 100644
index 0000000..71e898a
Binary files /dev/null and b/app/javascript/flavours/cybrespace/images/icon_direct.png differ
diff --git a/app/javascript/flavours/cybrespace/images/icon_docs.png b/app/javascript/flavours/cybrespace/images/icon_docs.png
new file mode 100644
index 0000000..6af1c82
Binary files /dev/null and b/app/javascript/flavours/cybrespace/images/icon_docs.png differ
diff --git a/app/javascript/flavours/cybrespace/images/icon_domain_blocks.png b/app/javascript/flavours/cybrespace/images/icon_domain_blocks.png
new file mode 100644
index 0000000..ed37504
Binary files /dev/null and b/app/javascript/flavours/cybrespace/images/icon_domain_blocks.png differ
diff --git a/app/javascript/flavours/cybrespace/images/icon_done.svg b/app/javascript/flavours/cybrespace/images/icon_done.svg
new file mode 100644
index 0000000..446af14
--- /dev/null
+++ b/app/javascript/flavours/cybrespace/images/icon_done.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/app/javascript/flavours/cybrespace/images/icon_email.svg b/app/javascript/flavours/cybrespace/images/icon_email.svg
new file mode 100644
index 0000000..6d0ad9d
--- /dev/null
+++ b/app/javascript/flavours/cybrespace/images/icon_email.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/app/javascript/flavours/cybrespace/images/icon_file_download.svg b/app/javascript/flavours/cybrespace/images/icon_file_download.svg
new file mode 100644
index 0000000..53e97e4
--- /dev/null
+++ b/app/javascript/flavours/cybrespace/images/icon_file_download.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/app/javascript/flavours/cybrespace/images/icon_flag.svg b/app/javascript/flavours/cybrespace/images/icon_flag.svg
new file mode 100644
index 0000000..3939c9d
--- /dev/null
+++ b/app/javascript/flavours/cybrespace/images/icon_flag.svg
@@ -0,0 +1,4 @@
+
diff --git a/app/javascript/flavours/cybrespace/images/icon_follow_requests.png b/app/javascript/flavours/cybrespace/images/icon_follow_requests.png
new file mode 100644
index 0000000..4123e2a
Binary files /dev/null and b/app/javascript/flavours/cybrespace/images/icon_follow_requests.png differ
diff --git a/app/javascript/flavours/cybrespace/images/icon_grade.svg b/app/javascript/flavours/cybrespace/images/icon_grade.svg
new file mode 100644
index 0000000..f48b468
--- /dev/null
+++ b/app/javascript/flavours/cybrespace/images/icon_grade.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/app/javascript/flavours/cybrespace/images/icon_home.png b/app/javascript/flavours/cybrespace/images/icon_home.png
new file mode 100644
index 0000000..66ce779
Binary files /dev/null and b/app/javascript/flavours/cybrespace/images/icon_home.png differ
diff --git a/app/javascript/flavours/cybrespace/images/icon_invite.png b/app/javascript/flavours/cybrespace/images/icon_invite.png
new file mode 100644
index 0000000..21156ec
Binary files /dev/null and b/app/javascript/flavours/cybrespace/images/icon_invite.png differ
diff --git a/app/javascript/flavours/cybrespace/images/icon_keyboard_shortcuts.png b/app/javascript/flavours/cybrespace/images/icon_keyboard_shortcuts.png
new file mode 100644
index 0000000..d66f393
Binary files /dev/null and b/app/javascript/flavours/cybrespace/images/icon_keyboard_shortcuts.png differ
diff --git a/app/javascript/flavours/cybrespace/images/icon_likes.png b/app/javascript/flavours/cybrespace/images/icon_likes.png
new file mode 100644
index 0000000..17d7a9c
Binary files /dev/null and b/app/javascript/flavours/cybrespace/images/icon_likes.png differ
diff --git a/app/javascript/flavours/cybrespace/images/icon_lists.png b/app/javascript/flavours/cybrespace/images/icon_lists.png
new file mode 100644
index 0000000..3828946
Binary files /dev/null and b/app/javascript/flavours/cybrespace/images/icon_lists.png differ
diff --git a/app/javascript/flavours/cybrespace/images/icon_local.png b/app/javascript/flavours/cybrespace/images/icon_local.png
new file mode 100644
index 0000000..5f82df3
Binary files /dev/null and b/app/javascript/flavours/cybrespace/images/icon_local.png differ
diff --git a/app/javascript/flavours/cybrespace/images/icon_lock_open.svg b/app/javascript/flavours/cybrespace/images/icon_lock_open.svg
new file mode 100644
index 0000000..3288b46
--- /dev/null
+++ b/app/javascript/flavours/cybrespace/images/icon_lock_open.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/app/javascript/flavours/cybrespace/images/icon_logout.png b/app/javascript/flavours/cybrespace/images/icon_logout.png
new file mode 100644
index 0000000..7ff806f
Binary files /dev/null and b/app/javascript/flavours/cybrespace/images/icon_logout.png differ
diff --git a/app/javascript/flavours/cybrespace/images/icon_mobile_apps.png b/app/javascript/flavours/cybrespace/images/icon_mobile_apps.png
new file mode 100644
index 0000000..a7cbd78
Binary files /dev/null and b/app/javascript/flavours/cybrespace/images/icon_mobile_apps.png differ
diff --git a/app/javascript/flavours/cybrespace/images/icon_mutes.png b/app/javascript/flavours/cybrespace/images/icon_mutes.png
new file mode 100644
index 0000000..c2225e9
Binary files /dev/null and b/app/javascript/flavours/cybrespace/images/icon_mutes.png differ
diff --git a/app/javascript/flavours/cybrespace/images/icon_notifications.png b/app/javascript/flavours/cybrespace/images/icon_notifications.png
new file mode 100644
index 0000000..0aaf5e6
Binary files /dev/null and b/app/javascript/flavours/cybrespace/images/icon_notifications.png differ
diff --git a/app/javascript/flavours/cybrespace/images/icon_person_add.svg b/app/javascript/flavours/cybrespace/images/icon_person_add.svg
new file mode 100644
index 0000000..068b8ae
--- /dev/null
+++ b/app/javascript/flavours/cybrespace/images/icon_person_add.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/app/javascript/flavours/cybrespace/images/icon_pin.png b/app/javascript/flavours/cybrespace/images/icon_pin.png
new file mode 100644
index 0000000..2329d8c
Binary files /dev/null and b/app/javascript/flavours/cybrespace/images/icon_pin.png differ
diff --git a/app/javascript/flavours/cybrespace/images/icon_profile_directory.png b/app/javascript/flavours/cybrespace/images/icon_profile_directory.png
new file mode 100644
index 0000000..05a9421
Binary files /dev/null and b/app/javascript/flavours/cybrespace/images/icon_profile_directory.png differ
diff --git a/app/javascript/flavours/cybrespace/images/icon_public.png b/app/javascript/flavours/cybrespace/images/icon_public.png
new file mode 100644
index 0000000..3c09460
Binary files /dev/null and b/app/javascript/flavours/cybrespace/images/icon_public.png differ
diff --git a/app/javascript/flavours/cybrespace/images/icon_reply.svg b/app/javascript/flavours/cybrespace/images/icon_reply.svg
new file mode 100644
index 0000000..cf6a09a
--- /dev/null
+++ b/app/javascript/flavours/cybrespace/images/icon_reply.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/app/javascript/flavours/cybrespace/images/icon_security.png b/app/javascript/flavours/cybrespace/images/icon_security.png
new file mode 100644
index 0000000..ccdbfaf
Binary files /dev/null and b/app/javascript/flavours/cybrespace/images/icon_security.png differ
diff --git a/app/javascript/flavours/cybrespace/images/icon_settings.png b/app/javascript/flavours/cybrespace/images/icon_settings.png
new file mode 100644
index 0000000..07f5c45
Binary files /dev/null and b/app/javascript/flavours/cybrespace/images/icon_settings.png differ
diff --git a/app/javascript/flavours/cybrespace/images/icon_tos.png b/app/javascript/flavours/cybrespace/images/icon_tos.png
new file mode 100644
index 0000000..d0dbb13
Binary files /dev/null and b/app/javascript/flavours/cybrespace/images/icon_tos.png differ
diff --git a/app/javascript/flavours/cybrespace/images/logo-cybre-light.png b/app/javascript/flavours/cybrespace/images/logo-cybre-light.png
new file mode 100644
index 0000000..91908f6
Binary files /dev/null and b/app/javascript/flavours/cybrespace/images/logo-cybre-light.png differ
diff --git a/app/javascript/flavours/cybrespace/images/logo-cybre.png b/app/javascript/flavours/cybrespace/images/logo-cybre.png
new file mode 100644
index 0000000..41dd8fd
Binary files /dev/null and b/app/javascript/flavours/cybrespace/images/logo-cybre.png differ
diff --git a/app/javascript/flavours/cybrespace/images/logo.svg b/app/javascript/flavours/cybrespace/images/logo.svg
new file mode 100644
index 0000000..034a9c2
--- /dev/null
+++ b/app/javascript/flavours/cybrespace/images/logo.svg
@@ -0,0 +1 @@
+
diff --git a/app/javascript/flavours/cybrespace/images/logo_alt.svg b/app/javascript/flavours/cybrespace/images/logo_alt.svg
new file mode 100644
index 0000000..102d4c7
--- /dev/null
+++ b/app/javascript/flavours/cybrespace/images/logo_alt.svg
@@ -0,0 +1 @@
+
diff --git a/app/javascript/flavours/cybrespace/images/logo_full.svg b/app/javascript/flavours/cybrespace/images/logo_full.svg
new file mode 100644
index 0000000..03bcf93
--- /dev/null
+++ b/app/javascript/flavours/cybrespace/images/logo_full.svg
@@ -0,0 +1 @@
+
diff --git a/app/javascript/flavours/cybrespace/images/logo_transparent.svg b/app/javascript/flavours/cybrespace/images/logo_transparent.svg
new file mode 100644
index 0000000..a1e7b40
--- /dev/null
+++ b/app/javascript/flavours/cybrespace/images/logo_transparent.svg
@@ -0,0 +1 @@
+
diff --git a/app/javascript/flavours/cybrespace/images/logo_transparent_black.svg b/app/javascript/flavours/cybrespace/images/logo_transparent_black.svg
new file mode 100644
index 0000000..e44bcf5
--- /dev/null
+++ b/app/javascript/flavours/cybrespace/images/logo_transparent_black.svg
@@ -0,0 +1 @@
+
diff --git a/app/javascript/flavours/cybrespace/images/logo_transparent_white.svg b/app/javascript/flavours/cybrespace/images/logo_transparent_white.svg
new file mode 100644
index 0000000..f061ffe
--- /dev/null
+++ b/app/javascript/flavours/cybrespace/images/logo_transparent_white.svg
@@ -0,0 +1 @@
+
diff --git a/app/javascript/flavours/cybrespace/images/preview.jpg b/app/javascript/flavours/cybrespace/images/preview.jpg
new file mode 100644
index 0000000..ec28567
Binary files /dev/null and b/app/javascript/flavours/cybrespace/images/preview.jpg differ
diff --git a/app/javascript/flavours/cybrespace/images/reticle.png b/app/javascript/flavours/cybrespace/images/reticle.png
new file mode 100644
index 0000000..41a5d1c
Binary files /dev/null and b/app/javascript/flavours/cybrespace/images/reticle.png differ
diff --git a/app/javascript/flavours/cybrespace/images/screen_federation.svg b/app/javascript/flavours/cybrespace/images/screen_federation.svg
new file mode 100644
index 0000000..7019a73
--- /dev/null
+++ b/app/javascript/flavours/cybrespace/images/screen_federation.svg
@@ -0,0 +1 @@
+
diff --git a/app/javascript/flavours/cybrespace/images/screen_hello.svg b/app/javascript/flavours/cybrespace/images/screen_hello.svg
new file mode 100644
index 0000000..7bcdd0a
--- /dev/null
+++ b/app/javascript/flavours/cybrespace/images/screen_hello.svg
@@ -0,0 +1 @@
+
diff --git a/app/javascript/flavours/cybrespace/images/screen_interactions.svg b/app/javascript/flavours/cybrespace/images/screen_interactions.svg
new file mode 100644
index 0000000..66a36f9
--- /dev/null
+++ b/app/javascript/flavours/cybrespace/images/screen_interactions.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/app/javascript/flavours/cybrespace/images/start.png b/app/javascript/flavours/cybrespace/images/start.png
new file mode 100644
index 0000000..7843455
Binary files /dev/null and b/app/javascript/flavours/cybrespace/images/start.png differ
diff --git a/app/javascript/flavours/cybrespace/images/void.png b/app/javascript/flavours/cybrespace/images/void.png
new file mode 100644
index 0000000..d730666
Binary files /dev/null and b/app/javascript/flavours/cybrespace/images/void.png differ
diff --git a/app/javascript/flavours/cybrespace/locales/en.js b/app/javascript/flavours/cybrespace/locales/en.js
new file mode 100644
index 0000000..477cb42
--- /dev/null
+++ b/app/javascript/flavours/cybrespace/locales/en.js
@@ -0,0 +1,145 @@
+import inherited from "mastodon/locales/en.json";
+
+const messages = {
+ "account.block_domain": "Hide everything from {domain}",
+ "account.disclaimer_full": "THESE NUMBERS ARE THE STUFF WHAT YOUR SERVER KNOWS ABOUT AND THERE MIGHT BE MORE THAT IT DONT KNOW ABOUT.",
+ "account.domain_blocked": "Domain hidden",
+ "account.edit_profile": "edit ~/.profile",
+ "account.posts": "Pings",
+ "account.posts_with_replies": "Pings with replies",
+ "account.show_reblogs": "Show boosts from @{name}",
+ "account.unblock_domain": "Unhide {domain}",
+ "account.view_full_profile": "View full profile",
+ "column.blocks": "~/.blocked",
+ "column.bookmarks": "~/.bookmarks",
+ "column.domain_blocks": "~/.blocked_domains",
+ "column.community": "/timelines/local",
+ "column.direct": "~/.dms",
+ "column.favourites": "~/.florps",
+ "column.follow_requests": "~/.follow-requests",
+ "column.home": "/timelines/home",
+ "column.mutes": "~/.muted",
+ "column.notifications": "~/.notifications",
+ "column.pins": "~/.pinned",
+ "column.public": "/timelines/federated",
+ "column_back_button.label": "cd ..",
+ "column_header.hide_settings": "Hide settings",
+ "column_header.show_settings": "Show settings",
+ "column_subheading.navigation": "Navigation",
+ "column_subheading.settings": "Settings",
+ "column_header.profile": "~/.profile",
+ "compose.attach": "Attach...",
+ "compose.attach.doodle": "Draw something",
+ "compose.attach.upload": "Upload a file",
+ "compose_form.hashtag_warning": "This ping won't be listed under any hashtag as it is unlisted. Only public pings can be searched by hashtag.",
+ "compose_form.placeholder": "What is in your databanks?",
+ "compose_form.publish": "Ping",
+ "confirmations.delete.message": "Are you sure you want to delete this status?",
+ "confirmations.delete_list.confirm": "Delete",
+ "confirmations.delete_list.message": "Are you sure you want to permanently delete this list?",
+ "confirmations.domain_block.confirm": "Hide entire domain",
+ "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.",
+ "doodle_button.label": "Add a drawing",
+ "embed.instructions": "Embed this status on your website by copying the code below.",
+ "empty_column.list": "There is nothing in this list yet. When members of this list post new statuses, they will appear here.",
+ "getting_started.appsshort": "Apps",
+ "getting_started.faq": "FAQ",
+ "getting_started.open_source_notice": "Mastodon is open source software. You can contribute or report issues at {github}.",
+ "getting_started.onboarding": "~/tour.sh",
+ "getting_started.userguide": "User Guide",
+ "getting_started.security": "~/.config",
+ "getting_started.heading": "~/getting_started",
+ "home.column_settings.advanced": "Advanced",
+ "home.column_settings.filter_regex": "Filter out by regular expressions",
+ "home.column_settings.show_reblogs": "Show relays",
+ "home.settings": "Column settings",
+ "keyboard_shortcuts.column": "to focus a status in one of the columns",
+ "keyboard_shortcuts.boost": "Relay",
+ "keyboard_shortcuts.favourite": "Florp",
+ "keyboard_shortcuts.bookmark": "Bookmark",
+ "keyboard_shortcuts.toggle_collapse": "Collapse/uncollapse pings",
+ "keyboard_shortcuts.column": "Focus a ping in one of the columns",
+ "keyboard_shortcuts.secondary_toot": "Send ping using secondary privacy setting",
+ "keyboard_shortcuts.favourites": "to open ~/.florps",
+ "keyboard_shortcuts.down": "Move down in the list",
+ "keyboard_shortcuts.enter": "Open ping",
+ "keyboard_shortcuts.my_profile": "Open ~/.profile",
+ "keyboard_shortcuts.notifications": "Open ~/.notifications",
+ "keyboard_shortcuts.heading": "Keyboard Shortcuts",
+ "keyboard_shortcuts.toot": "Start a brand new ping",
+ "media_gallery.toggle_visible": "Toggle visibility",
+ "navigation_bar.blocks": "~/.blocks",
+ "navigation_bar.featured_users": "~/featured_users.txt",
+ "navigation_bar.domain_blocks": "~/.blocked_domains",
+ "navigation_bar.explore": "/usr/bin/explore",
+ "navigation_bar.bookmarks": "~/.bookmarks",
+ "navigation_bar.community_timeline": "/timelines/local",
+ "navigation_bar.direct": "~/.dms",
+ "navigation_bar.edit_profile": "edit ~/.profile",
+ "navigation_bar.favourites": "~/.florps",
+ "navigation_bar.follow_requests": "~/.follow-requests",
+ "navigation_bar.info": "/about/more",
+ "navigation_bar.keyboard_shortcuts": "~/.kbd/shortcuts.conf",
+ "navigation_bar.lists": "~/.lists",
+ "navigation_bar.logout": "Jack out",
+ "navigation_bar.mutes": "~/.muted",
+ "navigation_bar.pins": "~/.pinned",
+ "navigation_bar.preferences": "edit ~/.config",
+ "navigation_bar.app_settings": "edit ~/.app_config",
+ "navigation_bar.public_timeline": "/timelines/federated",
+ "navigation_bar.misc": "/etc",
+ "notification.favourite": "{name} florped your ping",
+ "notification.reblog": "{name} relayed your ping",
+ "notifications.column_settings.push_meta": "This device",
+ "notifications.column_settings.reblog": "Boosts:",
+ "onboarding.done": "Done",
+ "onboarding.page_five.public_timelines": "The local timeline shows public posts from everyone on {domain}. The federated timeline shows public posts from everyone who people on {domain} follow. These are the Public Timelines, a great way to discover new people.",
+ "onboarding.page_four.home": "The home timeline shows posts from people you follow.",
+ "onboarding.page_four.notifications": "The notifications column shows when someone interacts with you.",
+ "onboarding.page_one.federation": "Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+ "onboarding.page_one.full_handle": "Your full handle",
+ "onboarding.page_one.handle_hint": "This is what you would tell your friends to search for.",
+ "onboarding.page_one.welcome": "Welcome to Mastodon!",
+ "onboarding.page_six.admin": "Your instance's admin is {admin}.",
+ "onboarding.page_six.almost_done": "Almost done...",
+ "onboarding.page_six.appetoot": "Hang ten on the cybrewaves!",
+ "onboarding.page_six.apps_available": "There are {apps} available for iOS, Android and other platforms.",
+ "onboarding.page_six.github": "Mastodon is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+ "onboarding.page_six.guidelines": "community guidelines",
+ "onboarding.page_six.read_guidelines": "Please read {domain}'s {guidelines}!",
+ "onboarding.page_six.various_app": "mobile apps",
+ "onboarding.page_three.profile": "Edit your profile to change your avatar, bio, and display name. There, you will also find other preferences.",
+ "onboarding.page_three.search": "Use the search bar to find people and look at hashtags, such as {illustration} and {introductions}. To look for a person who is not on this instance, use their full handle.",
+ "onboarding.page_two.compose": "Write posts from the compose column. You can upload images, change privacy settings, and add content warnings with the icons below.",
+ "onboarding.skip": "Skip",
+ "privacy.change": "Adjust status privacy",
+ "privacy.direct.long": "Post to mentioned users only",
+ "privacy.private.long": "Post to followers only",
+ "privacy.public.long": "Post to public timelines",
+ "privacy.unlisted.long": "Do not post to public timelines",
+ "report.hint": "The report will be sent to your instance moderators. You can provide an explanation of why you are reporting this account below:",
+ "search.placeholder": "Query...",
+ "search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
+ "search_popout.tips.status": "status",
+ "search_results.statuses": "Pings",
+ "standalone.public_title": "Peer into the data grid...",
+ "status.cannot_reblog": "This ping cannot be relayed",
+ "status.favourite": "Florp",
+ "status.open": "Expand this status",
+ "status.pinned": "Pinned ping",
+ "status.reblog": "Relay",
+ "status.reblogged_by": "{name} relayed",
+ "status.sensitive_toggle": "Click to view",
+ "tabs_bar.federated_timeline": "/timelines/federated",
+ "tabs_bar.home": "/timelines/home",
+ "tabs_bar.local_timeline": "/timelines/local",
+ "tabs_bar.notifications": "~/.notifications",
+ "upload_button.label": "Add media",
+ "upload_form.focus": "Crop",
+ "upload_form.undo": "Undo",
+ "video_player.toggle_sound": "Toggle sound",
+ "video_player.toggle_visible": "Toggle visibility",
+ "video_player.video_error": "Video could not be played"
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/cybrespace/names.yml b/app/javascript/flavours/cybrespace/names.yml
new file mode 100644
index 0000000..5549a38
--- /dev/null
+++ b/app/javascript/flavours/cybrespace/names.yml
@@ -0,0 +1,5 @@
+en:
+ flavours:
+ cybrespace:
+ description: A theme inspired by the cybre.space instance.
+ name: Cybre Edition
diff --git a/app/javascript/flavours/cybrespace/pack/common.js b/app/javascript/flavours/cybrespace/pack/common.js
new file mode 100644
index 0000000..6291b18
--- /dev/null
+++ b/app/javascript/flavours/cybrespace/pack/common.js
@@ -0,0 +1,2 @@
+import 'packs/public-path';
+import 'styles/application.scss';
diff --git a/app/javascript/flavours/cybrespace/pack/features/notification.js b/app/javascript/flavours/cybrespace/pack/features/notification.js
new file mode 100644
index 0000000..c3debb1
--- /dev/null
+++ b/app/javascript/flavours/cybrespace/pack/features/notification.js
@@ -0,0 +1,430 @@
+import React from 'react';
+import ImmutablePropTypes from 'react-immutable-proptypes';
+import { injectIntl, FormattedMessage, defineMessages } from 'react-intl';
+import { HotKeys } from 'react-hotkeys';
+import PropTypes from 'prop-types';
+import ImmutablePureComponent from 'react-immutable-pure-component';
+import { me } from 'mastodon/initial_state';
+import StatusContainer from 'mastodon/containers/status_container';
+import AccountContainer from 'mastodon/containers/account_container';
+import Report from './report';
+import FollowRequestContainer from '../containers/follow_request_container';
+import Icon from 'mastodon/components/icon';
+import Permalink from 'mastodon/components/permalink';
+import classNames from 'classnames';
+
+const messages = defineMessages({
+ favourite: { id: 'notification.favourite', defaultMessage: '{name} favourited your status' },
+ follow: { id: 'notification.follow', defaultMessage: '{name} followed you' },
+ ownPoll: { id: 'notification.own_poll', defaultMessage: 'Your poll has ended' },
+ poll: { id: 'notification.poll', defaultMessage: 'A poll you have voted in has ended' },
+ reblog: { id: 'notification.reblog', defaultMessage: '{name} boosted your status' },
+ status: { id: 'notification.status', defaultMessage: '{name} just posted' },
+ update: { id: 'notification.update', defaultMessage: '{name} edited a post' },
+ adminSignUp: { id: 'notification.admin.sign_up', defaultMessage: '{name} signed up' },
+ adminReport: { id: 'notification.admin.report', defaultMessage: '{name} reported {target}' },
+});
+
+const notificationForScreenReader = (intl, message, timestamp) => {
+ const output = [message];
+
+ output.push(intl.formatDate(timestamp, { hour: '2-digit', minute: '2-digit', month: 'short', day: 'numeric' }));
+
+ return output.join(', ');
+};
+
+export default @injectIntl
+class Notification extends ImmutablePureComponent {
+
+ static contextTypes = {
+ router: PropTypes.object,
+ };
+
+ static propTypes = {
+ notification: ImmutablePropTypes.map.isRequired,
+ hidden: PropTypes.bool,
+ onMoveUp: PropTypes.func.isRequired,
+ onMoveDown: PropTypes.func.isRequired,
+ onMention: PropTypes.func.isRequired,
+ onFavourite: PropTypes.func.isRequired,
+ onReblog: PropTypes.func.isRequired,
+ onToggleHidden: PropTypes.func.isRequired,
+ status: ImmutablePropTypes.map,
+ intl: PropTypes.object.isRequired,
+ getScrollPosition: PropTypes.func,
+ updateScrollBottom: PropTypes.func,
+ cacheMediaWidth: PropTypes.func,
+ cachedMediaWidth: PropTypes.number,
+ unread: PropTypes.bool,
+ };
+
+ handleMoveUp = () => {
+ const { notification, onMoveUp } = this.props;
+ onMoveUp(notification.get('id'));
+ }
+
+ handleMoveDown = () => {
+ const { notification, onMoveDown } = this.props;
+ onMoveDown(notification.get('id'));
+ }
+
+ handleOpen = () => {
+ const { notification } = this.props;
+
+ if (notification.get('status')) {
+ this.context.router.history.push(`/@${notification.getIn(['status', 'account', 'acct'])}/${notification.get('status')}`);
+ } else {
+ this.handleOpenProfile();
+ }
+ }
+
+ handleOpenProfile = () => {
+ const { notification } = this.props;
+ this.context.router.history.push(`/@${notification.getIn(['account', 'acct'])}`);
+ }
+
+ handleMention = e => {
+ e.preventDefault();
+
+ const { notification, onMention } = this.props;
+ onMention(notification.get('account'), this.context.router.history);
+ }
+
+ handleHotkeyFavourite = () => {
+ const { status } = this.props;
+ if (status) this.props.onFavourite(status);
+ }
+
+ handleHotkeyBoost = e => {
+ const { status } = this.props;
+ if (status) this.props.onReblog(status, e);
+ }
+
+ handleHotkeyToggleHidden = () => {
+ const { status } = this.props;
+ if (status) this.props.onToggleHidden(status);
+ }
+
+ getHandlers () {
+ return {
+ reply: this.handleMention,
+ favourite: this.handleHotkeyFavourite,
+ boost: this.handleHotkeyBoost,
+ mention: this.handleMention,
+ open: this.handleOpen,
+ openProfile: this.handleOpenProfile,
+ moveUp: this.handleMoveUp,
+ moveDown: this.handleMoveDown,
+ toggleHidden: this.handleHotkeyToggleHidden,
+ };
+ }
+
+ renderFollow (notification, account, link) {
+ const { intl, unread } = this.props;
+
+ return (
+
+
+
+ );
+ }
+
+ renderFollowRequest (notification, account, link) {
+ const { intl, unread } = this.props;
+
+ return (
+
+
+
+ );
+ }
+
+ renderMention (notification) {
+ return (
+
+ );
+ }
+
+ renderFavourite (notification, link) {
+ const { intl, unread } = this.props;
+
+ return (
+
+
+
+ );
+ }
+
+ renderReblog (notification, link) {
+ const { intl, unread } = this.props;
+
+ return (
+
+
+
+ );
+ }
+
+ renderStatus (notification, link) {
+ const { intl, unread } = this.props;
+
+ return (
+
+
+
+ );
+ }
+
+ renderUpdate (notification, link) {
+ const { intl, unread } = this.props;
+
+ return (
+
+
+
+ );
+ }
+
+ renderPoll (notification, account) {
+ const { intl, unread } = this.props;
+ const ownPoll = me === account.get('id');
+ const message = ownPoll ? intl.formatMessage(messages.ownPoll) : intl.formatMessage(messages.poll);
+
+ return (
+
+
+
+
+
+
+
+
+ {ownPoll ? (
+
+ ) : (
+
+ )}
+
+
+
+
+
+
+ );
+ }
+
+ renderAdminSignUp (notification, account, link) {
+ const { intl, unread } = this.props;
+
+ return (
+
+
+
+ );
+ }
+
+ renderAdminReport (notification, account, link) {
+ const { intl, unread, report } = this.props;
+
+ const targetAccount = report.get('target_account');
+ const targetDisplayNameHtml = { __html: targetAccount.get('display_name_html') };
+ const targetLink = ;
+
+ return (
+
+
+
+ );
+ }
+
+ render () {
+ const { notification } = this.props;
+ const account = notification.get('account');
+ const displayNameHtml = { __html: account.get('display_name_html') };
+ const link = ;
+
+ switch(notification.get('type')) {
+ case 'follow':
+ return this.renderFollow(notification, account, link);
+ case 'follow_request':
+ return this.renderFollowRequest(notification, account, link);
+ case 'mention':
+ return this.renderMention(notification);
+ case 'favourite':
+ return this.renderFavourite(notification, link);
+ case 'reblog':
+ return this.renderReblog(notification, link);
+ case 'status':
+ return this.renderStatus(notification, link);
+ case 'update':
+ return this.renderUpdate(notification, link);
+ case 'poll':
+ return this.renderPoll(notification, account);
+ case 'admin.sign_up':
+ return this.renderAdminSignUp(notification, account, link);
+ case 'admin.report':
+ return this.renderAdminReport(notification, account, link);
+ }
+
+ return null;
+ }
+
+}
diff --git a/app/javascript/flavours/cybrespace/theme.yml b/app/javascript/flavours/cybrespace/theme.yml
new file mode 100644
index 0000000..ad2671e
--- /dev/null
+++ b/app/javascript/flavours/cybrespace/theme.yml
@@ -0,0 +1,26 @@
+pack:
+ about: ../glitch/packs/about.js
+ admin:
+ - ../glitch/packs/admin.js
+ - ../glitch/packs/public.js
+ auth: ../glitch/packs/public.js
+ common:
+ filename: ../glitch/packs/common.js
+ stylesheet: true
+ embed: ../glitch/packs/public.js
+ error: ../glitch/packs/error.js
+ home:
+ filename: ../glitch/packs/home.js
+ preload:
+ - flavours/glitch/async/compose
+ - flavours/glitch/async/getting_started
+ - flavours/glitch/async/home_timeline
+ - flavours/glitch/async/notifications
+ mailer:
+ modal:
+ public: ../glitch/packs/public.js
+ settings: ../glitch/packs/settings.js
+ share: ../glitch/packs/share.js
+
+
+locales: locales
diff --git a/app/javascript/flavours/packs/about.js b/app/javascript/flavours/packs/about.js
new file mode 100644
index 0000000..892d825
--- /dev/null
+++ b/app/javascript/flavours/packs/about.js
@@ -0,0 +1,26 @@
+import './public-path';
+import loadPolyfills from '../mastodon/load_polyfills';
+import { start } from '../mastodon/common';
+
+start();
+
+function loaded() {
+ const TimelineContainer = require('../mastodon/containers/timeline_container').default;
+ const React = require('react');
+ const ReactDOM = require('react-dom');
+ const mountNode = document.getElementById('mastodon-timeline');
+
+ if (mountNode !== null) {
+ const props = JSON.parse(mountNode.getAttribute('data-props'));
+ ReactDOM.render(, mountNode);
+ }
+}
+
+function main() {
+ const ready = require('../mastodon/ready').default;
+ ready(loaded);
+}
+
+loadPolyfills().then(main).catch(error => {
+ console.error(error);
+});
diff --git a/app/javascript/flavours/packs/admin.js b/app/javascript/flavours/packs/admin.js
new file mode 100644
index 0000000..5990150
--- /dev/null
+++ b/app/javascript/flavours/packs/admin.js
@@ -0,0 +1,24 @@
+import './public-path';
+import ready from '../mastodon/ready';
+
+ready(() => {
+ const React = require('react');
+ const ReactDOM = require('react-dom');
+
+ [].forEach.call(document.querySelectorAll('[data-admin-component]'), element => {
+ const componentName = element.getAttribute('data-admin-component');
+ const { locale, ...componentProps } = JSON.parse(element.getAttribute('data-props'));
+
+ import('../mastodon/containers/admin_component').then(({ default: AdminComponent }) => {
+ return import('../mastodon/components/admin/' + componentName).then(({ default: Component }) => {
+ ReactDOM.render((
+
+
+
+ ), element);
+ });
+ }).catch(error => {
+ console.error(error);
+ });
+ });
+});
diff --git a/app/javascript/flavours/packs/application.js b/app/javascript/flavours/packs/application.js
new file mode 100644
index 0000000..020f2b4
--- /dev/null
+++ b/app/javascript/flavours/packs/application.js
@@ -0,0 +1,13 @@
+import './public-path';
+import loadPolyfills from '../mastodon/load_polyfills';
+import { start } from '../mastodon/common';
+
+start();
+
+loadPolyfills().then(async () => {
+ const { default: main } = await import('mastodon/main');
+
+ return main();
+}).catch(e => {
+ console.error(e);
+});
diff --git a/app/javascript/flavours/packs/common.js b/app/javascript/flavours/packs/common.js
new file mode 100644
index 0000000..05dff8e
--- /dev/null
+++ b/app/javascript/flavours/packs/common.js
@@ -0,0 +1,2 @@
+import './public-path';
+import 'styles/application.scss';
diff --git a/app/javascript/flavours/packs/error.js b/app/javascript/flavours/packs/error.js
new file mode 100644
index 0000000..6376dc2
--- /dev/null
+++ b/app/javascript/flavours/packs/error.js
@@ -0,0 +1,14 @@
+import './public-path';
+import ready from '../mastodon/ready';
+
+ready(() => {
+ const image = document.querySelector('img');
+
+ image.addEventListener('mouseenter', () => {
+ image.src = '/oops.gif';
+ });
+
+ image.addEventListener('mouseleave', () => {
+ image.src = '/oops.png';
+ });
+});
diff --git a/app/javascript/flavours/packs/public-path.js b/app/javascript/flavours/packs/public-path.js
new file mode 100644
index 0000000..f96109f
--- /dev/null
+++ b/app/javascript/flavours/packs/public-path.js
@@ -0,0 +1,21 @@
+// Dynamically set webpack's loading path depending on a meta header, in order
+// to share the same assets regardless of instance configuration.
+// See https://webpack.js.org/guides/public-path/#on-the-fly
+
+function removeOuterSlashes(string) {
+ return string.replace(/^\/*/, '').replace(/\/*$/, '');
+}
+
+function formatPublicPath(host = '', path = '') {
+ let formattedHost = removeOuterSlashes(host);
+ if (formattedHost && !/^http/i.test(formattedHost)) {
+ formattedHost = `//${formattedHost}`;
+ }
+ const formattedPath = removeOuterSlashes(path);
+ return `${formattedHost}/${formattedPath}/`;
+}
+
+const cdnHost = document.querySelector('meta[name=cdn-host]');
+
+// eslint-disable-next-line camelcase, no-undef, no-unused-vars
+__webpack_public_path__ = formatPublicPath(cdnHost ? cdnHost.content : '', process.env.PUBLIC_OUTPUT_PATH);
diff --git a/app/javascript/flavours/packs/public.js b/app/javascript/flavours/packs/public.js
new file mode 100644
index 0000000..4f60f04
--- /dev/null
+++ b/app/javascript/flavours/packs/public.js
@@ -0,0 +1,175 @@
+import './public-path';
+import loadPolyfills from '../mastodon/load_polyfills';
+import ready from '../mastodon/ready';
+import { start } from '../mastodon/common';
+import loadKeyboardExtensions from '../mastodon/load_keyboard_extensions';
+import 'cocoon-js-vanilla';
+
+start();
+
+function main() {
+ const IntlMessageFormat = require('intl-messageformat').default;
+ const { timeAgoString } = require('../mastodon/components/relative_timestamp');
+ const { delegate } = require('@rails/ujs');
+ const emojify = require('../mastodon/features/emoji/emoji').default;
+ const { getLocale } = require('../mastodon/locales');
+ const { messages } = getLocale();
+ const React = require('react');
+ const ReactDOM = require('react-dom');
+ const Rellax = require('rellax');
+ const { createBrowserHistory } = require('history');
+
+ const scrollToDetailedStatus = () => {
+ const history = createBrowserHistory();
+ const detailedStatuses = document.querySelectorAll('.public-layout .detailed-status');
+ const location = history.location;
+
+ if (detailedStatuses.length === 1 && (!location.state || !location.state.scrolledToDetailedStatus)) {
+ detailedStatuses[0].scrollIntoView();
+ history.replace(location.pathname, { ...location.state, scrolledToDetailedStatus: true });
+ }
+ };
+
+ const getEmojiAnimationHandler = (swapTo) => {
+ return ({ target }) => {
+ target.src = target.getAttribute(swapTo);
+ };
+ };
+
+ ready(() => {
+ const locale = document.documentElement.lang;
+
+ const dateTimeFormat = new Intl.DateTimeFormat(locale, {
+ year: 'numeric',
+ month: 'long',
+ day: 'numeric',
+ hour: 'numeric',
+ minute: 'numeric',
+ });
+
+ [].forEach.call(document.querySelectorAll('.emojify'), (content) => {
+ content.innerHTML = emojify(content.innerHTML);
+ });
+
+ [].forEach.call(document.querySelectorAll('time.formatted'), (content) => {
+ const datetime = new Date(content.getAttribute('datetime'));
+ const formattedDate = dateTimeFormat.format(datetime);
+
+ content.title = formattedDate;
+ content.textContent = formattedDate;
+ });
+
+ [].forEach.call(document.querySelectorAll('time.time-ago'), (content) => {
+ const datetime = new Date(content.getAttribute('datetime'));
+ const now = new Date();
+
+ content.title = dateTimeFormat.format(datetime);
+ content.textContent = timeAgoString({
+ formatMessage: ({ id, defaultMessage }, values) => (new IntlMessageFormat(messages[id] || defaultMessage, locale)).format(values),
+ formatDate: (date, options) => (new Intl.DateTimeFormat(locale, options)).format(date),
+ }, datetime, now, now.getFullYear(), content.getAttribute('datetime').includes('T'));
+ });
+
+ const reactComponents = document.querySelectorAll('[data-component]');
+
+ if (reactComponents.length > 0) {
+ import(/* webpackChunkName: "containers/media_container" */ '../mastodon/containers/media_container')
+ .then(({ default: MediaContainer }) => {
+ [].forEach.call(reactComponents, (component) => {
+ [].forEach.call(component.children, (child) => {
+ component.removeChild(child);
+ });
+ });
+
+ const content = document.createElement('div');
+
+ ReactDOM.render(, content);
+ document.body.appendChild(content);
+ scrollToDetailedStatus();
+ })
+ .catch(error => {
+ console.error(error);
+ scrollToDetailedStatus();
+ });
+ } else {
+ scrollToDetailedStatus();
+ }
+
+ const parallaxComponents = document.querySelectorAll('.parallax');
+
+ if (parallaxComponents.length > 0 ) {
+ new Rellax('.parallax', { speed: -1 });
+ }
+
+ delegate(document, '#registration_user_password_confirmation,#registration_user_password', 'input', () => {
+ const password = document.getElementById('registration_user_password');
+ const confirmation = document.getElementById('registration_user_password_confirmation');
+ if (confirmation.value && confirmation.value.length > password.maxLength) {
+ confirmation.setCustomValidity((new IntlMessageFormat(messages['password_confirmation.exceeds_maxlength'] || 'Password confirmation exceeds the maximum password length', locale)).format());
+ } else if (password.value && password.value !== confirmation.value) {
+ confirmation.setCustomValidity((new IntlMessageFormat(messages['password_confirmation.mismatching'] || 'Password confirmation does not match', locale)).format());
+ } else {
+ confirmation.setCustomValidity('');
+ }
+ });
+
+ delegate(document, '#user_password,#user_password_confirmation', 'input', () => {
+ const password = document.getElementById('user_password');
+ const confirmation = document.getElementById('user_password_confirmation');
+ if (!confirmation) return;
+
+ if (confirmation.value && confirmation.value.length > password.maxLength) {
+ confirmation.setCustomValidity((new IntlMessageFormat(messages['password_confirmation.exceeds_maxlength'] || 'Password confirmation exceeds the maximum password length', locale)).format());
+ } else if (password.value && password.value !== confirmation.value) {
+ confirmation.setCustomValidity((new IntlMessageFormat(messages['password_confirmation.mismatching'] || 'Password confirmation does not match', locale)).format());
+ } else {
+ confirmation.setCustomValidity('');
+ }
+ });
+
+ delegate(document, '.custom-emoji', 'mouseover', getEmojiAnimationHandler('data-original'));
+ delegate(document, '.custom-emoji', 'mouseout', getEmojiAnimationHandler('data-static'));
+
+ delegate(document, '.status__content__spoiler-link', 'click', function() {
+ const statusEl = this.parentNode.parentNode;
+
+ if (statusEl.dataset.spoiler === 'expanded') {
+ statusEl.dataset.spoiler = 'folded';
+ this.textContent = (new IntlMessageFormat(messages['status.show_more'] || 'Show more', locale)).format();
+ } else {
+ statusEl.dataset.spoiler = 'expanded';
+ this.textContent = (new IntlMessageFormat(messages['status.show_less'] || 'Show less', locale)).format();
+ }
+
+ return false;
+ });
+
+ [].forEach.call(document.querySelectorAll('.status__content__spoiler-link'), (spoilerLink) => {
+ const statusEl = spoilerLink.parentNode.parentNode;
+ const message = (statusEl.dataset.spoiler === 'expanded') ? (messages['status.show_less'] || 'Show less') : (messages['status.show_more'] || 'Show more');
+ spoilerLink.textContent = (new IntlMessageFormat(message, locale)).format();
+ });
+ });
+
+ delegate(document, '.sidebar__toggle__icon', 'click', () => {
+ document.querySelector('.sidebar ul').classList.toggle('visible');
+ });
+
+ // Empty the honeypot fields in JS in case something like an extension
+ // automatically filled them.
+ delegate(document, '#registration_new_user,#new_user', 'submit', () => {
+ ['user_website', 'user_confirm_password', 'registration_user_website', 'registration_user_confirm_password'].forEach(id => {
+ const field = document.getElementById(id);
+ if (field) {
+ field.value = '';
+ }
+ });
+ });
+}
+
+loadPolyfills()
+ .then(main)
+ .then(loadKeyboardExtensions)
+ .catch(error => {
+ console.error(error);
+ });
diff --git a/app/javascript/flavours/packs/share.js b/app/javascript/flavours/packs/share.js
new file mode 100644
index 0000000..1225d7b
--- /dev/null
+++ b/app/javascript/flavours/packs/share.js
@@ -0,0 +1,26 @@
+import './public-path';
+import loadPolyfills from '../mastodon/load_polyfills';
+import { start } from '../mastodon/common';
+
+start();
+
+function loaded() {
+ const ComposeContainer = require('../mastodon/containers/compose_container').default;
+ const React = require('react');
+ const ReactDOM = require('react-dom');
+ const mountNode = document.getElementById('mastodon-compose');
+
+ if (mountNode !== null) {
+ const props = JSON.parse(mountNode.getAttribute('data-props'));
+ ReactDOM.render(, mountNode);
+ }
+}
+
+function main() {
+ const ready = require('../mastodon/ready').default;
+ ready(loaded);
+}
+
+loadPolyfills().then(main).catch(error => {
+ console.error(error);
+});
diff --git a/app/javascript/skins/cybrespace/cybrespace-light/common.scss b/app/javascript/skins/cybrespace/cybrespace-light/common.scss
new file mode 100644
index 0000000..014231a
--- /dev/null
+++ b/app/javascript/skins/cybrespace/cybrespace-light/common.scss
@@ -0,0 +1,2 @@
+@import '../cybrespace/components';
+@import 'cybre-light';
diff --git a/app/javascript/skins/cybrespace/cybrespace-light/cybre-light.scss b/app/javascript/skins/cybrespace/cybrespace-light/cybre-light.scss
new file mode 100644
index 0000000..adff3a6
--- /dev/null
+++ b/app/javascript/skins/cybrespace/cybrespace-light/cybre-light.scss
@@ -0,0 +1,962 @@
+@import 'styles/application';
+
+$success-green: #B64579; // Padua
+$base-overlay-background: $black;
+
+$ui-base-color: #f7e8ed; // "darkest"
+$ui-base-alt: #f9f2f5;
+$ui-base-lighter-color: darken($ui-base-color, 40%); // Lighter darkest
+$ui-secondary-color: #ead0d6; // "lightest"
+$ui-primary-color: #bf5677; // "lighter"
+$ui-highlight-color: #bf5677; // "vibrant"
+$primary-text-color: #382b32;
+$dark-text-color: #ca748f;
+$secondary-text-color: #382b32;
+
+$header-color: $ui-primary-color;
+$header-text-color: #fff;
+$icon-button-inactive-color: lighten(desaturate($ui-base-lighter-color, 20%), 20%);
+$action-button-color: $icon-button-inactive-color;
+
+$about-page-text: $primary-text-color;
+
+@import '../cybrespace/cybre-base';
+
+$gold-star: #dd9d08;
+
+/* cybre-specific additions */
+
+.column .static-content.getting-started {
+ background-image: url('flavours/cybrespace/images/logo-cybre-light.png');
+ background-size:auto 50%;
+ background-position: 50% 75%;
+ background-repeat:no-repeat;
+}
+
+.ui, body {
+ background: $ui-base-color url('flavours/cybrespace/images/background-cybre-light.png');
+ background-attachment: fixed;
+}
+
+.drawer__inner__mastodon {
+ display: none;
+}
+
+.landing-page .header-wrapper {
+ background-image:url('flavours/cybrespace/images/header-cybre-alt.jpg');
+ background-size:cover;
+ background-position:50% 50%;
+}
+
+.landing-page.alternative .header {
+ background-image:url('flavours/cybrespace/images/header-cybre-colour.jpg');
+ background-repeat: repeat-x;
+ background-size:contain;
+ height:45vh;
+ width: 100%;
+ position:absolute;
+ z-index: 1;
+ text-align:center;
+
+ display: unset!important;
+}
+
+.landing-page.alternative .header img {
+ margin: auto;
+ max-height:45vh;
+}
+
+
+.landing-page.alternative .grid {
+ position: relative;
+ z-index:2;
+ margin-top:15vh;
+}
+
+.landing-page.alternative .landing-page__hero img {
+ visibility: hidden;
+ max-height:170px;
+}
+
+.landing-page.alternative .landing-page__forms {
+ height:auto;
+}
+
+.landing-page.alternative .column-1 {
+ display:flex;
+ align-items:flex-end;
+}
+
+.landing-page.alternative .column {
+ max-height:initial;
+}
+
+.landing-page.alternative .row__mascot {
+ .floats {
+ position:absolute;
+ img {
+ width:100%;
+ height:100%;
+ }
+ transition: all 0.1s linear;
+ animation-name: floating;
+ animation-iteration-count: infinite;
+ animation-direction: alternate;
+ animation-timing-function: ease-in-out;
+ }
+
+ .float-1 {
+ width:50px;
+ height:50px;
+ bottom:60px;
+ left:110px;
+ animation-duration: 3s;
+ }
+
+ .float-2 {
+ width:130px;
+ height:130px;
+ left:85px;
+ bottom: -60px;
+ animation-duration: 3.5s;
+ animation-delay: 0.2s;
+ }
+
+ .float-3 {
+ width:100px;
+ height:100px;
+ right: 50;
+ top: -10px;
+ animation-duration: 4s;
+ animation-delay: 0.5s;
+ }
+}
+
+/* about.scss */
+
+.landing-page {
+ h1 {
+ color: $about-page-text;
+ small {
+ color: lighten($about-page-text, 10%);
+ }
+ }
+ p, li {
+ color: $about-page-text;
+ }
+
+ .header-wrapper {
+ padding-top:0px;
+
+ background-size:cover;
+ background-position:50% 55%;
+ }
+
+ .header-wrapper {
+ .mascot {
+ width:500px;
+ bottom:-52px;
+ left:-120px;
+ }
+ }
+
+ .container.links {
+ background-color: $ui-base-color;
+ border-top: 5px solid $ui-primary-color;
+ width:100%;
+ max-width:100%;
+ padding:0px calc(50% - 400px);
+
+ a {
+ &:hover {
+ color: lighten($ui-primary-color, 10%);
+ }
+ }
+ }
+
+ .container.hero {
+ .floats {
+ display:none;
+ }
+
+ .closed-registrations-message, form {
+ border-top: 50px solid #5f4770;
+ -webkit-box-shadow: 0 0 6px rgba(0,0,0,.1);
+ box-shadow: 0 0 6px rgba(0,0,0,.1);
+
+ &:before {
+ font-size: 16px;
+ font-family:inherit;
+ line-height:inherit;
+ font-weight:normal;
+ color:white;
+ position:absolute;
+ top:-35px;
+ }
+ }
+
+ .closed-registrations-message:before {
+ content: "Registrations closed";
+ }
+
+ form:before {
+ content: "Register now";
+ }
+ }
+
+ #mastodon-timeline {
+ .column-header {
+ color:white;
+ }
+ }
+}
+
+.features-list__row {
+ .text {
+ color: $about-page-text;
+ }
+}
+
+.information-board {
+ .panel {
+ .panel-header {
+ color: $primary-text-color;
+ border-bottom: 1px solid lighten($ui-secondary-color, 4%);
+
+ a,
+ span {
+ font-weight: 400;
+ color: lighten($ui-primary-color, 4%);
+ }
+ }
+ }
+}
+
+/* components.scss */
+
+.onboarding-modal__page {
+ p {
+ color: $primary-text-color;
+ }
+}
+
+.column-header {
+ background: $header-color;
+ color: $header-text-color;
+ border-top-left-radius: 3px;
+ border-top-right-radius: 3px;
+ box-shadow: 0px 0px 3px rgba(0,0,0,0.3);
+}
+
+.column-header__button {
+ background: $header-color;
+ color: $header-text-color;
+ border-top-right-radius: 3px;
+
+ &:hover {
+ color: darken($ui-base-color, 10%);
+ }
+
+ &.active {
+ color: $primary-text-color;
+ background: darken($ui-base-color, 5%);
+
+ &:hover {
+ background: darken($ui-base-color, 5%);
+ }
+ }
+}
+
+.status-card, .status-card.compact {
+ border-color: $ui-highlight-color;
+}
+
+// selectivity -- needs to override .column-header > button
+.column-header .column-header__back-button {
+ background: $header-color;
+ color:$header-text-color;
+}
+
+.column-back-button {
+ background: $header-color;
+ color:$header-text-color;
+}
+
+.column-header__collapsible-inner {
+ background: darken($ui-base-alt, 2%);
+}
+
+.empty-column-indicator,
+.error-column {
+ color: darken($ui-base-lighter-color, 15%);
+}
+
+.column > .scrollable {
+ background: $ui-base-alt;
+}
+
+.compose-form {
+ .autosuggest-textarea__textarea,
+ .spoiler-input__input {
+ color: $primary-text-color;
+ border: 1px solid $ui-primary-color;
+ }
+
+ .autosuggest-textarea__textarea {
+ border-bottom-width:0px;
+ }
+ .compose-form__modifiers {
+ border: 1px solid $ui-primary-color;
+ border-top-width:0px;
+ }
+
+ .compose-form__buttons button.active:last-child {
+ border-radius:3px;
+ background: $ui-base-color;
+ color: $ui-primary-color;
+ }
+ .compose-form__buttons-wrapper {
+ background-color:$ui-primary-color;
+ }
+
+ .compose-form__warning a {
+ color:white;
+ }
+
+ .icon-button.inverted {
+ color:white;
+
+ &:hover {
+ color:$ui-secondary-color;
+ }
+ }
+}
+
+.character-counter {
+ color:$white;
+}
+
+button.icon-button {
+ &.disabled {
+ }
+}
+
+.icon-button {
+ &.inverted {
+ color: darken($ui-base-lighter-color, 10%);
+ }
+
+ &.overlayed {
+ background: rgba($base-overlay-background, 0.2);
+ color: rgba($white, 0.7);
+
+ &:hover {
+ background: rgba($base-overlay-background, 0.4);
+ }
+ }
+
+ &.disabled {
+ color: desaturate($icon-button-inactive-color, 5%);
+
+ &:hover,
+ &:active,
+ &:focus {
+ color: desaturate($icon-button-inactive-color, 5%);
+ }
+ }
+
+ color: $icon-button-inactive-color;
+
+ &:hover,
+ &:active,
+ &:focus {
+ color: darken($icon-button-inactive-color, 5%);
+ }
+}
+
+.icon-button.star-icon,
+.icon-button.star-icon:active {
+ background:transparent;
+ border:none;
+}
+
+.icon-button.star-icon.active {
+ color: $gold-star;
+ &:active, &:hover, &:focus {
+ color: $gold-star;
+ }
+}
+
+.text-icon-button {
+ color: $white;
+ &.active {
+ background: $ui-base-color;
+ color: $ui-primary-color;
+ }
+ &:focus, &:hover {
+ color: darken($ui-base-color, 3%);
+ }
+}
+.status.status-direct {
+ background: darken($ui-base-alt, 5%);
+ .icon-button.disabled {
+ color: lighten($ui-base-lighter-color, 10%);
+ }
+}
+
+.account__header, .account-card {
+ & > div {
+ background: rgba(lighten($ui-base-color, 4%), 0.6);
+ }
+
+ .account__header__content {
+ color: $primary-text-color;
+ }
+
+ .detailed-status__display-name .display-name strong {
+ color: $ui-highlight-color;
+ }
+
+ .icon-button {
+ &, &:hover {
+ color:desaturate($ui-base-lighter-color, 20%);
+ }
+ &.active {
+ &, &:hover {
+ color:$ui-base-lighter-color;
+ }
+ }
+ }
+}
+
+.account__section-headline a {
+ &.active {
+ color: $primary-text-color;
+
+ &::after {
+ border-bottom-color: $ui-base-alt;
+ }
+
+ &::after {
+ border-bottom-color: $ui-base-alt;
+ }
+ }
+}
+
+.privacy-dropdown.active .privacy-dropdown__value.active .icon-button {
+ color: $ui-primary-color;
+}
+
+.privacy-dropdown__option {
+ color: $primary-text-color;
+
+ strong {
+ color: $primary-text-color;
+ }
+
+ &:hover,
+ &.active {
+ color: $white;
+
+ .privacy-dropdown__option__content {
+ color: $white;
+
+ strong {
+ color: $white;
+ }
+ }
+ }
+}
+
+.emoji-picker-dropdown__menu {
+ .emoji-search-wrapper {
+ border-color: darken($ui-base-color, 10%);
+ }
+ .emoji-search {
+ background: darken($ui-base-color, 5%);
+ border-color: darken($ui-base-color, 10%);
+ }
+ .emoji-mart {
+ color: $ui-primary-color;
+ }
+}
+
+.search-popout {
+ background: $ui-base-color;
+ color: $ui-primary-color;
+
+ h4 {
+ color: $ui-primary-color;
+ }
+
+ em {
+ color: $ui-highlight-color;
+ }
+}
+.search__icon .fa.active {
+ opacity:1.0;
+}
+.search-results__hashtag {
+ color: darken($ui-primary-color, 10%);
+ &:hover {
+ color: lighten($ui-primary-color, 5%);
+ }
+}
+
+.static-content {
+ /*color: $primary-text-color;*/
+}
+
+#Getting-started {
+ background: $ui-primary-color;
+ border-bottom:0px;
+ color:white;
+}
+
+.getting-started {
+ p {
+ color: $primary-text-color;
+ }
+
+ a {
+ color: darken($ui-base-lighter-color, 10%);
+ }
+}
+.getting-started__wrapper {
+ flex: 0 0.5 auto;
+}
+
+.getting-started {
+ .column-link {
+ background: lighten($ui-primary-color, 5%);
+ color:$white;
+ &:hover {
+ background: lighten($ui-primary-color, 10%);
+ }
+ }
+}
+.column-link__badge {
+ background: saturate(darken($ui-primary-color, 5%), 5%);
+}
+.column-subheading {
+ background: darken($ui-primary-color, 5%);
+ color:$white;
+}
+
+.media-spoiler,
+.video-player__spoiler.active {
+ color: $white;
+ &:hover {
+ color: darken($white, 5%);
+ }
+}
+
+.status {
+ border-bottom: 1px solid $ui-secondary-color;
+}
+
+.status__relative-time, .status__display-name {
+ color: darken($ui-base-color, 40%);
+}
+
+.status__content {
+ .status__content__spoiler-link {
+ background: $ui-base-lighter-color;
+
+ &:hover {
+ background: lighten($ui-base-lighter-color, 5%);
+ }
+ }
+}
+
+.muted .status__content p {
+ color: $icon-button-inactive-color;
+}
+
+.dropdown-menu__item {
+ & > a {
+ color: $primary-text-color;
+ &:hover, &:active, &:focus {
+ color: $white;
+ }
+ }
+}
+
+.dropdown--active .dropdown__content {
+ & > ul {
+ background: $ui-base-color;
+ box-shadow: 0 0 5px rgba($base-shadow-color, 0.2);
+ & > li > a {
+ background: $ui-base-color;
+ color: $primary-text-color;
+
+ &:hover {
+ background: $ui-highlight-color;
+ color: $ui-base-color;
+ }
+ }
+ }
+}
+
+.boost-modal,
+.confirmation-modal,
+.report-modal,
+.actions-modal,
+.mute-modal
+{
+ color: $primary-text-color;
+}
+.boost-modal__action-bar,
+.confirmation-modal__action-bar,
+.mute-modal__action-bar,
+.report-modal__action-bar,
+.mute-modal__action-bar {
+ & > div {
+ color: $ui-primary-color;
+ }
+}
+
+.actions-modal
+{
+ ul {
+ li:not(:empty) {
+ a {
+ color: $primary-text-color;
+ button {
+
+ }
+ &.active, &:hover, &:active, &:focus {
+ color: $white;
+ button {
+ color: $white;
+ }
+ }
+ }
+ }
+ }
+}
+
+.react-toggle-track {
+ background-color: $icon-button-inactive-color;
+}
+
+.report-modal__comment .setting-text {
+ color: $primary-text-color;
+ border-bottom-color: lighten($ui-primary-color, 10%);
+ &:focus, &:active {
+ color: $primary-text-color;
+ }
+}
+
+.status.light {
+ .status__content {
+ color: $primary-text-color;
+ }
+ .display-name strong {
+ color: $primary-text-color;
+ }
+}
+
+.reply-indicator__content a {
+ color: lighten($ui-highlight-color, 30%);
+}
+
+.status__content
+{
+ a {
+ color: $ui-highlight-color;
+
+ &:hover {
+ .fa {
+ color: darken($ui-base-color, 40%);
+ }
+ }
+ }
+}
+
+.detailed-status__display-name {
+ color: $ui-base-lighter-color;
+}
+
+.drawer .drawer__inner {
+ overflow: visible;
+ height:inherit;
+ background:$ui-base-alt;
+}
+
+.search__icon .fa {
+ color: $ui-highlight-color;
+}
+
+.drawer__pager {
+ overflow-y:auto;
+}
+
+.drawer .drawer__header {
+ background: $ui-base-color;
+ border-radius:3px;
+}
+
+.onboarding-modal__page h1 {
+ background-color: darken($ui-primary-color, 5%);
+}
+
+.poll__text input[type="text"],
+.compose-form__poll-wrapper select {
+ color: $primary-text-color;
+}
+
+.compose-form__poll-wrapper .button.button-secondary {
+ color: $ui-highlight-color;
+}
+
+/* forms.scss */
+.block-button, .button, button {
+ background-color: $ui-primary-color;
+ color: $white;
+
+ &.button-alternative {
+ color: $ui-base-color;
+ }
+
+ &.logo-button {
+ color: $white;
+ svg path:first-child {
+ fill: $white;
+ }
+ }
+}
+
+.simple_form {
+ p.hint {
+ color: $primary-text-color;
+ }
+
+ .block-button, .button, button {
+ background-color: $ui-primary-color;
+ color: $white;
+
+ &:hover {
+ background-color: lighten($ui-primary-color, 5%);
+ }
+
+ &:active,
+ &:focus {
+ background-color: darken($ui-primary-color, 5%);
+ }
+
+ }
+}
+
+/* admin.scss */
+
+.table > thead > tr > th {
+ border-bottom-color: $ui-secondary-color;
+}
+
+.simple_form h4 {
+ border-bottom: 1px solid $ui-highlight-color;
+}
+
+.admin-wrapper {
+ .content {
+ h2, p.hint, h4, h6 {
+ color: $primary-text-color;
+ }
+
+ .muted-hint {
+ color: $primary-text-color;
+ }
+ }
+
+ .sidebar {
+ .logo {
+ -webkit-filter: invert(100%);
+ filter: invert(100%);
+ }
+
+ ul {
+ ul {
+ a {
+ &.selected {
+ background-color: $ui-primary-color;
+ color: $white;
+
+ &:hover {
+ background-color: lighten($ui-primary-color, 10%);
+ }
+ }
+ }
+ }
+ a {
+ &.selected {
+ background-color: $ui-primary-color;
+ color: $white;
+
+ &:hover {
+ background-color: lighten($ui-primary-color, 10%);
+ }
+ }
+ }
+ }
+ }
+}
+
+.pagination .current {
+ color: $ui-primary-color;
+}
+
+.report-accounts__item > strong {
+ color: $primary-text-color;
+}
+
+.admin-wrapper .content {
+ & > p {
+ color: $primary-text-color;
+ }
+ hr {
+ border-color: $ui-highlight-color;
+ }
+}
+
+/* accounts.scss */
+.card {
+ .name {
+ color: $white;
+ }
+
+ .counter {
+ .counter-number {
+ color: $white;
+ }
+ }
+}
+
+/* stream_entries.scss */
+.activity-stream {
+ .entry {
+ }
+ .status.light {
+ .display-name {
+ strong {
+ color: $primary-text-color;
+ }
+ }
+ .status__content {
+ color: $primary-text-color;
+ }
+ }
+ .detailed-status.light {
+ .detailed-status__display-name {
+ .display-name {
+ strong {
+ color: $primary-text-color;
+ }
+ }
+ }
+ .status__content {
+ color: $primary-text-color;
+ }
+ .status-card,
+ .status-card__title,
+ .status-card__description {
+ color: $primary-text-color;
+ }
+ }
+}
+
+/* accounts.scss */
+.card {
+ .name {
+ color: darken($ui-primary-color, 15%);
+ }
+ .counter {
+ .counter-number {
+ color: darken($ui-primary-color, 15%);
+ }
+ border-color: $ui-primary-color;
+ }
+}
+
+.activity-stream-tabs {
+ a {
+ color: lighten($ui-primary-color, 10%);
+ &.active {
+ color: darken($ui-primary-color, 10%);
+ }
+ }
+}
+
+/* uncategorized */
+
+.empty-column-indicator, .error-column {
+ background-color: $ui-base-alt;
+}
+
+.actions .button.button-alternative {
+ background: $ui-highlight-color;
+ color: $white;
+
+ &:active,
+ &:focus,
+ &:hover {
+ background-color: lighten($ui-highlight-color, 4%);
+ }
+}
+
+.public-layout .header {
+ background: $ui-highlight-color;
+ color: $white;
+}
+
+.public-layout .public-account-header__tabs__name h1 {
+ color: $white;
+ small {
+ color: $white;
+ }
+}
+.public-layout .header .brand:hover,
+.public-layout .header .brand:focus,
+.public-layout .header .brand:active {
+ background: lighten($ui-highlight-color, 5%);
+}
+
+.public-layout .container:last-child {
+ background:$ui-highlight-color;
+ padding-left: 100px;
+ padding-right: 100px;
+ border-radius: 4px;
+ h4 {
+ color: white;
+ }
+}
+
+.modal-layout, .modal-layout__mastodon > * {
+ background: none;
+}
+
+.dashboard__widgets a:not(.name-tag) {
+ color: $primary-text-color;
+}
+
+.tabs-bar__wrapper {
+ background: $ui-base-color url('flavours/cybrespace/images/background-cybre-light.png');
+}
+
+.layout-single-column .navigation-panel {
+ background-color: $ui-highlight-color;
+ height: auto;
+ .column-link {
+ background: lighten($ui-primary-color, 5%);
+ color:$white;
+ &:hover {
+ background: lighten($ui-primary-color, 10%);
+ }
+ &.active {
+ background: darken($ui-primary-color, 5%);
+ }
+ }
+ hr {
+ display: none;
+ }
+}
diff --git a/app/javascript/skins/cybrespace/cybrespace-light/names.yml b/app/javascript/skins/cybrespace/cybrespace-light/names.yml
new file mode 100644
index 0000000..8c39ba9
--- /dev/null
+++ b/app/javascript/skins/cybrespace/cybrespace-light/names.yml
@@ -0,0 +1,4 @@
+en:
+ skins:
+ cybrespace:
+ cybrespace-light: cybre.space Light
diff --git a/app/javascript/skins/cybrespace/cybrespace/common.scss b/app/javascript/skins/cybrespace/cybrespace/common.scss
new file mode 100644
index 0000000..1991d2f
--- /dev/null
+++ b/app/javascript/skins/cybrespace/cybrespace/common.scss
@@ -0,0 +1,25 @@
+@import 'components';
+@import 'cybre';
+@import '~flavours/glitch/styles/index';
+
+.search-popout {
+ color: $dark-text-color;
+ background-color: $ui-secondary-color;
+
+ h4 {
+ color: $dark-text-color;
+ }
+}
+
+.fa-star::before {
+ content: "\f0c7";
+}
+
+.status__content .status__content__text,
+.reply-indicator__content .status__content__text {
+ display: block;
+}
+
+.status__avatar {
+ position: initial;
+}
diff --git a/app/javascript/skins/cybrespace/cybrespace/components.scss b/app/javascript/skins/cybrespace/cybrespace/components.scss
new file mode 100644
index 0000000..a884fd9
--- /dev/null
+++ b/app/javascript/skins/cybrespace/cybrespace/components.scss
@@ -0,0 +1,27 @@
+.no-reduce-motion .icon-button.star-icon {
+ &.activate {
+ & > .fa-floppy-o {
+ animation: spring-rotate-in 1s linear;
+ }
+ }
+
+ &.deactivate {
+ & > .fa-floppy-o {
+ animation: spring-rotate-out 1s linear;
+ }
+ }
+}
+
+.status-card__image-image {
+ border-radius: 0;
+}
+
+.public-layout .container {
+ max-width: initial;
+}
+
+h2 {
+ font-size: 1rem;
+ align-items: center;
+ display: flex;
+}
diff --git a/app/javascript/skins/cybrespace/cybrespace/cybre-base.scss b/app/javascript/skins/cybrespace/cybrespace/cybre-base.scss
new file mode 100644
index 0000000..a8887a8
--- /dev/null
+++ b/app/javascript/skins/cybrespace/cybrespace/cybre-base.scss
@@ -0,0 +1,91 @@
+@import 'styles/application';
+
+/* Wider compose area */
+@media screen and (min-width: 1300px) {
+ .drawer {
+ width: 17%; /* Not part of the flex fun */
+ max-width: 400px;
+ min-width: 330px;
+ }
+ .layout-multiple-columns .column {
+ flex-grow: 1 !important;
+ max-width: 400px;
+ }
+}
+
+/* Don't show outline around statuses if we're in
+ * mouse or touch mode (rather than keyboard) */
+[data-whatinput="mouse"], [data-whatinput="touch"] {
+ .status__content:focus, .status:focus,
+ .status__wrapper:focus, .status__content__text:focus {
+ outline: none;
+ }
+}
+
+/* Less emphatic show more */
+.status__content__read-more-button {
+ font-size: 14px;
+ color: $dark-text-color;
+
+ .status__prepend-icon {
+ padding-right: 4px;
+ }
+}
+
+/* Show a little arrowey thing after the time in a
+ * status to signal that you can click it to see
+ * a detailed view */
+.status time:after,
+.detailed-status__datetime span:after {
+ font: normal normal normal 14px/1 FontAwesome;
+ content: "\00a0\00a0\f08e";
+}
+
+/* Don't display the elephant mascot (we have our
+ * own, thanks) */
+.drawer__inner__mastodon {
+ display: none;
+}
+
+/* Let the compose area/drawer be short, but
+ * expand if necessary */
+.drawer .drawer__inner {
+ overflow: visible;
+ height:inherit;
+ background-image: none;
+}
+.drawer__pager {
+ overflow-y:auto;
+}
+
+/* Put a reasonable background on the single-column compose form */
+.layout-single-column .compose-panel {
+ background-color: $ui-base-color;
+ height: auto;
+ max-height: 100%;
+ overflow-y: visible;
+ margin-top: 65px;
+}
+
+/* Better distinguish the search bar */
+.layout-single-column .compose-panel .search {
+ position:relative;
+ top: -55px;
+ margin-bottom: -55px;
+}
+
+/* Use display: none instead of visibility:hidden
+ * to hide the suggested follows list on non-mobile */
+@media screen and (min-width: 630px) {
+ .search-results .trends {
+ display:none;
+ }
+}
+
+/* Don't display the weird triangles on the modal layout,
+ * because they look strange on cybrespace themes. */
+.modal-layout__mastodon {
+ display:none;
+}
+
+@import 'fullwidth-media';
diff --git a/app/javascript/skins/cybrespace/cybrespace/cybre.scss b/app/javascript/skins/cybrespace/cybrespace/cybre.scss
new file mode 100644
index 0000000..3bfbaa6
--- /dev/null
+++ b/app/javascript/skins/cybrespace/cybrespace/cybre.scss
@@ -0,0 +1,282 @@
+$ui-base-color: #1b1b1b; // darkest
+$ui-highlight-color: #1ea21e; // vibrant
+$ui-secondary-color: #E4F2E4; // lightest
+$ui-primary-color: #E4F2E4; // lighter
+$ui-primary-color-alt: #a0b49c; // darker, for external pages
+
+$about-page-text: lighten($ui-base-color, 50%);
+
+@import 'cybre-base';
+
+@keyframes floating {
+ from {
+ transform: translate(0, 0);
+ }
+ 65% {
+ transform: translate(0, 4px);
+ }
+ to {
+ transform: translate(0, -0);
+ }
+}
+
+body, body.about-body {
+ background: $ui-base-color url('flavours/cybrespace/images/background-cybre.png');
+ background-attachment: fixed;
+}
+
+body.about-body {
+ // basics.scss &.about-body
+ background: darken($ui-base-color, 8%) url('flavours/cybrespace/images/background-cybre.png');
+
+ background-position-y: 200px;
+ background-position-x: center;
+}
+
+.about-body .mascot {
+ display: none;
+}
+
+.muted {
+ .status__content p, .status__content a {
+ color: lighten($ui-base-color, 35%);
+ }
+
+ .status__display-name strong {
+ color: lighten($ui-base-color, 35%);
+ }
+}
+
+.compose-form__buttons button.active:last-child {
+ color:$ui-secondary-color;
+ background-color: $ui-highlight-color;
+ border-radius:3px;
+}
+
+.screenshot-with-signup {
+ min-height:300px;
+}
+
+.container.hero .closed-registrations-message .clock {
+ font-size: 150%;
+ margin: 1em auto;
+}
+
+.column .static-content.getting-started {
+ background-image: url('flavours/cybrespace/images/logo-cybre.png'), url('flavours/cybrespace/images/background-cybre.png');
+ background-size:auto 50%, cover;
+ background-position: 50% 75%, center center;
+ background-repeat:no-repeat, no-repeat;
+}
+
+.columns-area {
+ background: $ui-base-color url('flavours/cybrespace/images/background-cybre.png');
+}
+
+.actions .button.button-alternative {
+ background: $ui-highlight-color;
+ color: $ui-primary-color;
+
+ &:active,
+ &:focus,
+ &:hover {
+ background-color: lighten($ui-highlight-color, 4%);
+ }
+}
+
+@media screen and (max-width: 1280px) {
+ .landing-page .container.links {
+ top: -15px;
+ }
+}
+
+.landing-page.alternative .header {
+ background-image:url('flavours/cybrespace/images/header-cybre-colour.jpg');
+ background-repeat: repeat-x;
+ background-size:contain;
+ height:45vh;
+ width: 100%;
+ position:absolute;
+ z-index: 1;
+ text-align:center;
+
+ display: unset!important;
+}
+
+.landing-page.alternative .header img {
+ margin: auto;
+ max-height:45vh;
+}
+
+
+.landing-page.alternative .grid {
+ position: relative;
+ z-index:2;
+ margin-top:15vh;
+}
+
+.landing-page.alternative .landing-page__hero img {
+ visibility: hidden;
+ max-height:170px;
+}
+
+.landing-page.alternative .landing-page__forms {
+ height:auto;
+}
+
+.landing-page.alternative .column-1 {
+ display:flex;
+ align-items:flex-end;
+}
+
+.landing-page.alternative .column {
+ max-height:initial;
+}
+
+.landing-page.alternative .row__mascot {
+ .floats {
+ position:absolute;
+ img {
+ width:100%;
+ height:100%;
+ }
+ transition: all 0.1s linear;
+ animation-name: floating;
+ animation-iteration-count: infinite;
+ animation-direction: alternate;
+ animation-timing-function: ease-in-out;
+ }
+
+ .float-1 {
+ width:50px;
+ height:50px;
+ bottom:60px;
+ left:110px;
+ animation-duration: 3s;
+ }
+
+ .float-2 {
+ width:130px;
+ height:130px;
+ left:85px;
+ bottom: -60px;
+ animation-duration: 3.5s;
+ animation-delay: 0.2s;
+ }
+
+ .float-3 {
+ width:100px;
+ height:100px;
+ right: 50;
+ top: -10px;
+ animation-duration: 4s;
+ animation-delay: 0.5s;
+ }
+}
+
+.activity-stream {
+ .status.light {
+ .status__header .status__meta .status__relative-time {
+ color: $ui-primary-color-alt;
+ }
+
+ .display-name span {
+ color: $ui-primary-color-alt;
+ }
+
+ .status__content {
+ a.status__content__spoiler-link {
+ background: $ui-primary-color-alt;
+
+ &:hover {
+ background: lighten($ui-primary-color-alt, 8%);
+ }
+ }
+ }
+ }
+
+ .detailed-status.light {
+ .detailed-status__display-name .display-name span {
+ color: $ui-primary-color-alt;
+ }
+
+ .status__content a.status__content__spoiler-link {
+ background: $ui-primary-color-alt;
+
+ &:hover {
+ background: lighten($ui-primary-color-alt, 8%);
+ }
+ }
+
+ .detailed-status__meta {
+ color: $ui-primary-color-alt;
+ }
+ }
+
+ .media-spoiler {
+ background: $ui-primary-color-alt;
+ &:hover {
+ background: darken($ui-primary-color-alt, 5%);
+ }
+ }
+
+ .pre-header {
+ color: $ui-primary-color-alt;
+ .status__display-name.muted strong {
+ color: $ui-primary-color-alt;
+ }
+ }
+}
+
+.embed .activity-stream .entry .detailed-status.light .button.button-secondary.logo-button {
+ color: $ui-primary-color-alt;
+ svg {
+ path:first-child {
+ fill: $ui-primary-color-alt;
+ }
+ }
+ &:active,
+ &:focus,
+ &:hover {
+ svg path:first-child {
+ fill: lighten($ui-primary-color-alt, 4%);
+ }
+ }
+}
+
+.emoji-mart-search {
+ background: $simple-background-color;
+ input {
+ color: $ui-primary-color-alt;
+ border: 1px solid $ui-primary-color-alt;
+ }
+}
+
+.emoji-mart-anchor {
+ color: $ui-primary-color-alt;
+ &:hover {
+ color: darken($ui-primary-color-alt, 8%);
+ }
+}
+
+.search-popout {
+ background: $ui-base-color;
+ color: $ui-primary-color;
+
+ h4 {
+ color: $ui-primary-color;
+ }
+
+ em {
+ color: $ui-highlight-color;
+ }
+}
+
+.status__content a,
+.status__content a.unhandled-link {
+ color: mix($ui-highlight-color, $ui-secondary-color, 10%);
+}
+
+.button:disabled {
+ background-color: #9F9F9F !important;
+}
diff --git a/app/javascript/skins/cybrespace/cybrespace/fullwidth-media.scss b/app/javascript/skins/cybrespace/cybrespace/fullwidth-media.scss
new file mode 100644
index 0000000..f6a036d
--- /dev/null
+++ b/app/javascript/skins/cybrespace/cybrespace/fullwidth-media.scss
@@ -0,0 +1,48 @@
+
+.detailed-status > .media-spoiler,
+.status > .media-spoiler,
+.status .video-player,
+.media-gallery,
+.status .status-card.interactive {
+ margin-top: 20px;
+ margin-left: -68px;
+ width: calc(100% + 80px);
+}
+
+.detailed-status > .media-spoiler,
+.status > .media-spoiler,
+.video-player {
+ max-width: none;
+}
+
+/* If there's no status text, add an extra margin on top */
+.status .status__info + .media-gallery,
+.status .status__info + .media-spoiler,
+.status .status__info + .video-player,
+.status .status__info + .status-card {
+ margin-top: 40px;
+}
+
+.status__video-player-video {
+ transform: unset;
+ top: unset;
+}
+
+.detailed-status .media-gallery {
+ margin-left: -10px;
+ width: calc(100% + 22px);
+}
+
+.public-layout .status {
+ .status__content {
+ min-height: 15px;
+ }
+ & > .media-spoiler,
+ .video-player,
+ .media-gallery,
+ .status-card {
+ margin-top: 20px;
+ width: calc(100% + 94px);
+ margin-left: -78px;
+ }
+}
diff --git a/app/javascript/skins/cybrespace/cybrespace/names.yml b/app/javascript/skins/cybrespace/cybrespace/names.yml
new file mode 100644
index 0000000..ad6a849
--- /dev/null
+++ b/app/javascript/skins/cybrespace/cybrespace/names.yml
@@ -0,0 +1,4 @@
+en:
+ skins:
+ cybrespace:
+ cybrespace: cybre.space