add shared assets at root for base-apps testing
icons/, scripts/, ui/ folders are needed at root for base-apps manifest paths like ../../icons/ to resolve correctly during testing
This commit is contained in:
6
.gitignore
vendored
6
.gitignore
vendored
@@ -17,10 +17,8 @@ test_*.log
|
||||
*_hierarchy.json
|
||||
recorded_actions.json
|
||||
|
||||
# Duplicate asset folders (use src/main/assets/ instead)
|
||||
/icons/
|
||||
/scripts/
|
||||
/ui/
|
||||
# Note: /icons/, /scripts/, /ui/ at root are needed for base-apps testing
|
||||
# They should be copies of src/main/assets/{icons,scripts,ui}
|
||||
|
||||
# Sandbox data created during testing
|
||||
/src/main/assets/sandbox_data/
|
||||
|
||||
BIN
icons/account.tga
LFS
Normal file
BIN
icons/account.tga
LFS
Normal file
Binary file not shown.
BIN
icons/add.tga
LFS
Normal file
BIN
icons/add.tga
LFS
Normal file
Binary file not shown.
BIN
icons/back.tga
LFS
Normal file
BIN
icons/back.tga
LFS
Normal file
Binary file not shown.
BIN
icons/backspace.tga
LFS
Normal file
BIN
icons/backspace.tga
LFS
Normal file
Binary file not shown.
BIN
icons/battery.tga
LFS
Normal file
BIN
icons/battery.tga
LFS
Normal file
Binary file not shown.
BIN
icons/browser.tga
LFS
Normal file
BIN
icons/browser.tga
LFS
Normal file
Binary file not shown.
BIN
icons/calculator.tga
LFS
Normal file
BIN
icons/calculator.tga
LFS
Normal file
Binary file not shown.
BIN
icons/calendar.tga
LFS
Normal file
BIN
icons/calendar.tga
LFS
Normal file
Binary file not shown.
BIN
icons/call_small.tga
LFS
Normal file
BIN
icons/call_small.tga
LFS
Normal file
Binary file not shown.
BIN
icons/camera.tga
LFS
Normal file
BIN
icons/camera.tga
LFS
Normal file
Binary file not shown.
BIN
icons/clock.tga
LFS
Normal file
BIN
icons/clock.tga
LFS
Normal file
Binary file not shown.
BIN
icons/close.tga
LFS
Normal file
BIN
icons/close.tga
LFS
Normal file
Binary file not shown.
BIN
icons/contact_phone.tga
LFS
Normal file
BIN
icons/contact_phone.tga
LFS
Normal file
Binary file not shown.
BIN
icons/contacts.tga
LFS
Normal file
BIN
icons/contacts.tga
LFS
Normal file
Binary file not shown.
BIN
icons/dialpad.tga
LFS
Normal file
BIN
icons/dialpad.tga
LFS
Normal file
Binary file not shown.
BIN
icons/download.tga
LFS
Normal file
BIN
icons/download.tga
LFS
Normal file
Binary file not shown.
BIN
icons/files.tga
LFS
Normal file
BIN
icons/files.tga
LFS
Normal file
Binary file not shown.
BIN
icons/flash.tga
LFS
Normal file
BIN
icons/flash.tga
LFS
Normal file
Binary file not shown.
BIN
icons/forward.tga
LFS
Normal file
BIN
icons/forward.tga
LFS
Normal file
Binary file not shown.
BIN
icons/gallery.tga
LFS
Normal file
BIN
icons/gallery.tga
LFS
Normal file
Binary file not shown.
BIN
icons/game.tga
LFS
Normal file
BIN
icons/game.tga
LFS
Normal file
Binary file not shown.
BIN
icons/heart.tga
LFS
Normal file
BIN
icons/heart.tga
LFS
Normal file
Binary file not shown.
BIN
icons/history.tga
LFS
Normal file
BIN
icons/history.tga
LFS
Normal file
Binary file not shown.
BIN
icons/home.tga
LFS
Normal file
BIN
icons/home.tga
LFS
Normal file
Binary file not shown.
BIN
icons/library.tga
LFS
Normal file
BIN
icons/library.tga
LFS
Normal file
Binary file not shown.
BIN
icons/maps.tga
LFS
Normal file
BIN
icons/maps.tga
LFS
Normal file
Binary file not shown.
BIN
icons/menu.tga
LFS
Normal file
BIN
icons/menu.tga
LFS
Normal file
Binary file not shown.
BIN
icons/message.tga
LFS
Normal file
BIN
icons/message.tga
LFS
Normal file
Binary file not shown.
BIN
icons/more.tga
LFS
Normal file
BIN
icons/more.tga
LFS
Normal file
Binary file not shown.
BIN
icons/music.tga
LFS
Normal file
BIN
icons/music.tga
LFS
Normal file
Binary file not shown.
BIN
icons/notes.tga
LFS
Normal file
BIN
icons/notes.tga
LFS
Normal file
Binary file not shown.
BIN
icons/phone.tga
LFS
Normal file
BIN
icons/phone.tga
LFS
Normal file
Binary file not shown.
BIN
icons/play.tga
LFS
Normal file
BIN
icons/play.tga
LFS
Normal file
Binary file not shown.
BIN
icons/refresh.tga
LFS
Normal file
BIN
icons/refresh.tga
LFS
Normal file
Binary file not shown.
BIN
icons/search.tga
LFS
Normal file
BIN
icons/search.tga
LFS
Normal file
Binary file not shown.
BIN
icons/send.tga
LFS
Normal file
BIN
icons/send.tga
LFS
Normal file
Binary file not shown.
BIN
icons/settings.tga
LFS
Normal file
BIN
icons/settings.tga
LFS
Normal file
Binary file not shown.
BIN
icons/signal.tga
LFS
Normal file
BIN
icons/signal.tga
LFS
Normal file
Binary file not shown.
BIN
icons/store.tga
LFS
Normal file
BIN
icons/store.tga
LFS
Normal file
Binary file not shown.
BIN
icons/switch-camera.tga
LFS
Normal file
BIN
icons/switch-camera.tga
LFS
Normal file
Binary file not shown.
BIN
icons/timer.tga
LFS
Normal file
BIN
icons/timer.tga
LFS
Normal file
Binary file not shown.
BIN
icons/weather.tga
LFS
Normal file
BIN
icons/weather.tga
LFS
Normal file
Binary file not shown.
BIN
icons/wifi.tga
LFS
Normal file
BIN
icons/wifi.tga
LFS
Normal file
Binary file not shown.
103
scripts/layout.lua
Normal file
103
scripts/layout.lua
Normal file
@@ -0,0 +1,103 @@
|
||||
-- Layout System for Virtual Smartphone
|
||||
-- Provides reusable UI component helpers
|
||||
-- Requires navigation.lua to be loaded first
|
||||
|
||||
-- Icon paths (relative to assets/)
|
||||
local ICON_PATH = "../../icons/"
|
||||
|
||||
-- Default icons
|
||||
local icons = {
|
||||
back = ICON_PATH .. "back.tga",
|
||||
home = ICON_PATH .. "home.tga",
|
||||
menu = ICON_PATH .. "menu.tga",
|
||||
search = ICON_PATH .. "search.tga",
|
||||
more = ICON_PATH .. "more.tga",
|
||||
close = ICON_PATH .. "close.tga",
|
||||
wifi = ICON_PATH .. "wifi.tga",
|
||||
signal = ICON_PATH .. "signal.tga",
|
||||
battery = ICON_PATH .. "battery.tga"
|
||||
}
|
||||
|
||||
-- Get current time formatted as HH:MM
|
||||
local function getCurrentTime()
|
||||
-- In sandbox, we might not have os.date, use a default
|
||||
if os and os.date then
|
||||
return os.date("%H:%M")
|
||||
end
|
||||
return "12:30"
|
||||
end
|
||||
|
||||
-- Update status bar time
|
||||
function updateStatusTime(doc)
|
||||
local timeEl = doc:GetElementById("status-time")
|
||||
if timeEl then
|
||||
timeEl.inner_rml = getCurrentTime()
|
||||
end
|
||||
end
|
||||
|
||||
-- Initialize status bar with current time
|
||||
-- Call from document onload
|
||||
function initStatusBar(doc)
|
||||
updateStatusTime(doc)
|
||||
|
||||
-- Set up timer to update time every minute if timers are available
|
||||
if setTimeout then
|
||||
local function updateLoop()
|
||||
updateStatusTime(doc)
|
||||
setTimeout(updateLoop, 60000)
|
||||
end
|
||||
setTimeout(updateLoop, 60000)
|
||||
end
|
||||
end
|
||||
|
||||
-- Initialize app bar back button
|
||||
-- Call from document onload
|
||||
function initAppBar(doc)
|
||||
local backBtn = doc:GetElementById("app-bar-back")
|
||||
if backBtn then
|
||||
-- Back button is handled via onclick in RML
|
||||
-- This is for any additional setup
|
||||
end
|
||||
end
|
||||
|
||||
-- Initialize system navigation bar
|
||||
-- Call from document onload
|
||||
function initSystemNav(doc)
|
||||
-- Navigation buttons are handled via onclick in RML
|
||||
-- This is for any additional setup
|
||||
end
|
||||
|
||||
-- Full layout initialization
|
||||
-- Call from document onload: initLayout(document)
|
||||
function initLayout(doc)
|
||||
initStatusBar(doc)
|
||||
initAppBar(doc)
|
||||
initSystemNav(doc)
|
||||
print("Layout initialized")
|
||||
end
|
||||
|
||||
-- Handle back button press (for app bar or system nav)
|
||||
function onBackPressed()
|
||||
if canGoBack and canGoBack() then
|
||||
goBack()
|
||||
else
|
||||
-- If at root, go home
|
||||
if goHome then
|
||||
goHome()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Handle home button press
|
||||
function onHomePressed()
|
||||
if goHome then
|
||||
goHome()
|
||||
end
|
||||
end
|
||||
|
||||
-- Handle recent apps button press (placeholder)
|
||||
function onRecentPressed()
|
||||
print("Recent apps pressed (not implemented)")
|
||||
end
|
||||
|
||||
print("Layout system loaded")
|
||||
145
scripts/navigation.lua
Normal file
145
scripts/navigation.lua
Normal file
@@ -0,0 +1,145 @@
|
||||
-- Navigation System for Virtual Smartphone
|
||||
-- Handles screen transitions and state management
|
||||
|
||||
-- Screen registry - maps screen names to RML file paths
|
||||
local screens = {
|
||||
home = "apps/home/home.rml",
|
||||
lock = "apps/home/lock.rml",
|
||||
dialer = "apps/dialer/dialer.rml",
|
||||
calling = "apps/dialer/calling.rml",
|
||||
contacts = "apps/contacts/contacts.rml",
|
||||
contact_detail = "apps/contacts/contact_detail.rml",
|
||||
messages = "apps/messages/messages.rml",
|
||||
chat = "apps/messages/chat.rml",
|
||||
settings = "apps/settings/settings.rml",
|
||||
browser = "apps/browser/browser.rml",
|
||||
store = "apps/store/store.rml",
|
||||
camera = "apps/camera/camera.rml",
|
||||
music = "apps/music/music.rml"
|
||||
}
|
||||
|
||||
-- Use global state to persist across document loads
|
||||
-- Initialize only if not already set
|
||||
if not _G.nav_state then
|
||||
_G.nav_state = {
|
||||
history = {},
|
||||
current_screen = "home",
|
||||
nav_direction = "none" -- "forward", "back", "home", "none"
|
||||
}
|
||||
end
|
||||
|
||||
-- Local references for convenience
|
||||
local history = _G.nav_state.history
|
||||
local function get_current() return _G.nav_state.current_screen end
|
||||
local function set_current(s) _G.nav_state.current_screen = s end
|
||||
local function get_direction() return _G.nav_state.nav_direction end
|
||||
local function set_direction(d) _G.nav_state.nav_direction = d end
|
||||
|
||||
-- Apply animation class based on navigation direction
|
||||
local function applyNavAnimation()
|
||||
local dir = get_direction()
|
||||
print("Applying animation, direction: " .. dir)
|
||||
if dir ~= "none" and document then
|
||||
-- In RmlUi Lua, get body element
|
||||
local body = document.body
|
||||
if body then
|
||||
print("Found body element, setting class nav-" .. dir)
|
||||
-- Set the appropriate animation class
|
||||
if dir == "forward" then
|
||||
body:SetClass("nav-forward", true)
|
||||
elseif dir == "back" then
|
||||
body:SetClass("nav-back", true)
|
||||
elseif dir == "home" then
|
||||
body:SetClass("nav-home", true)
|
||||
else
|
||||
body:SetClass("nav-default", true)
|
||||
end
|
||||
else
|
||||
print("Body element not found!")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Navigate to a screen by name
|
||||
function navigateTo(screen_name)
|
||||
print("navigateTo called with: " .. tostring(screen_name))
|
||||
local path = screens[screen_name]
|
||||
if path then
|
||||
-- Push current screen to history before navigating
|
||||
table.insert(history, get_current())
|
||||
set_current(screen_name)
|
||||
set_direction("forward")
|
||||
|
||||
-- Load the new screen using C++ function
|
||||
local success = loadScreen(path)
|
||||
if success then
|
||||
applyNavAnimation()
|
||||
print("Navigated to: " .. screen_name .. " (history depth: " .. #history .. ")")
|
||||
else
|
||||
-- Restore previous state on failure
|
||||
set_current(table.remove(history))
|
||||
print("Failed to navigate to: " .. screen_name)
|
||||
end
|
||||
return success
|
||||
else
|
||||
print("Unknown screen: " .. screen_name)
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
-- Go back to previous screen
|
||||
function goBack()
|
||||
print("goBack called (history depth: " .. #history .. ")")
|
||||
if #history > 0 then
|
||||
local previous = table.remove(history)
|
||||
local path = screens[previous]
|
||||
if path then
|
||||
set_current(previous)
|
||||
set_direction("back")
|
||||
loadScreen(path)
|
||||
applyNavAnimation()
|
||||
print("Back to: " .. previous)
|
||||
return true
|
||||
end
|
||||
else
|
||||
print("No history to go back to")
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- Go to home screen (clear history)
|
||||
function goHome()
|
||||
-- Clear the history table
|
||||
for i = #history, 1, -1 do
|
||||
history[i] = nil
|
||||
end
|
||||
set_current("home")
|
||||
set_direction("home")
|
||||
loadScreen(screens.home)
|
||||
applyNavAnimation()
|
||||
print("Navigated to home")
|
||||
end
|
||||
|
||||
-- Get current screen name
|
||||
function getCurrentScreen()
|
||||
return get_current()
|
||||
end
|
||||
|
||||
-- Check if we can go back
|
||||
function canGoBack()
|
||||
return #history > 0
|
||||
end
|
||||
|
||||
-- Clear navigation history
|
||||
function clearHistory()
|
||||
for i = #history, 1, -1 do
|
||||
history[i] = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- Get history depth
|
||||
function getHistoryDepth()
|
||||
return #history
|
||||
end
|
||||
|
||||
print("Navigation system initialized (current: " .. get_current() .. ", history: " .. #history .. ")")
|
||||
1485
ui/components.rcss
Normal file
1485
ui/components.rcss
Normal file
File diff suppressed because it is too large
Load Diff
93
ui/html.rcss
Normal file
93
ui/html.rcss
Normal file
@@ -0,0 +1,93 @@
|
||||
body, div,
|
||||
h1, h2, h3, h4,
|
||||
h5, h6, p,
|
||||
hr, pre,
|
||||
tabset tabs
|
||||
{
|
||||
display: block;
|
||||
}
|
||||
|
||||
h1
|
||||
{
|
||||
font-size: 2em;
|
||||
margin: .67em 0;
|
||||
}
|
||||
|
||||
h2
|
||||
{
|
||||
font-size: 1.5em;
|
||||
margin: .75em 0;
|
||||
}
|
||||
|
||||
h3
|
||||
{
|
||||
font-size: 1.17em;
|
||||
margin: .83em 0;
|
||||
}
|
||||
|
||||
h4, p
|
||||
{
|
||||
margin: 1.12em 0;
|
||||
}
|
||||
|
||||
h5
|
||||
{
|
||||
font-size: .83em;
|
||||
margin: 1.5em 0;
|
||||
}
|
||||
|
||||
h6
|
||||
{
|
||||
font-size: .75em;
|
||||
margin: 1.67em 0;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4,
|
||||
h5, h6, strong
|
||||
{
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
em
|
||||
{
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
pre
|
||||
{
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
hr
|
||||
{
|
||||
border-width: 1px;
|
||||
}
|
||||
|
||||
table
|
||||
{
|
||||
box-sizing: border-box;
|
||||
display: table;
|
||||
}
|
||||
tr
|
||||
{
|
||||
box-sizing: border-box;
|
||||
display: table-row;
|
||||
}
|
||||
td
|
||||
{
|
||||
box-sizing: border-box;
|
||||
display: table-cell;
|
||||
}
|
||||
col
|
||||
{
|
||||
box-sizing: border-box;
|
||||
display: table-column;
|
||||
}
|
||||
colgroup
|
||||
{
|
||||
display: table-column-group;
|
||||
}
|
||||
thead, tbody, tfoot
|
||||
{
|
||||
display: table-row-group;
|
||||
}
|
||||
270
ui/layout.rcss
Normal file
270
ui/layout.rcss
Normal file
@@ -0,0 +1,270 @@
|
||||
/* ==============================================
|
||||
Layout Components: Reusable App Layout Structure
|
||||
System status bar, app bar, navigation bar
|
||||
============================================== */
|
||||
|
||||
/* ============== System Status Bar ============== */
|
||||
/* Top bar showing time, signal, wifi, battery */
|
||||
|
||||
.system-status-bar {
|
||||
height: 36px;
|
||||
padding: 0 16px;
|
||||
background-color: transparent;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 16px;
|
||||
color: #FFFFFF;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.system-status-bar.bg-surface {
|
||||
background-color: #1E1E1E;
|
||||
}
|
||||
|
||||
.system-status-time {
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.system-status-icons {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.system-status-icons img {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* ============== App Bar ============== */
|
||||
/* Title bar with back button and optional actions */
|
||||
|
||||
.app-bar {
|
||||
height: 72px;
|
||||
padding: 0 8px;
|
||||
background-color: #1E1E1E;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
z-index: 900;
|
||||
}
|
||||
|
||||
.app-bar.transparent {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.app-bar.primary {
|
||||
background-color: #121212;
|
||||
}
|
||||
|
||||
.app-bar-back {
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
border-radius: 28px;
|
||||
}
|
||||
|
||||
.app-bar-back:hover {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.app-bar-back:active {
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.app-bar-back img {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.app-bar-title {
|
||||
flex: 1;
|
||||
font-size: 24px;
|
||||
font-weight: 500;
|
||||
color: #FFFFFF;
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
.app-bar-actions {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.app-bar-action {
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
border-radius: 28px;
|
||||
}
|
||||
|
||||
.app-bar-action:hover {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.app-bar-action:active {
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.app-bar-action img {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* ============== System Navigation Bar ============== */
|
||||
/* Bottom bar with back, home, and recent buttons */
|
||||
|
||||
.system-nav-bar {
|
||||
height: 56px;
|
||||
background-color: #0A0A0A;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.system-nav-bar.transparent {
|
||||
background-color: rgba(10, 10, 10, 0.9);
|
||||
}
|
||||
|
||||
.system-nav-btn {
|
||||
width: 72px;
|
||||
height: 48px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
border-radius: 24px;
|
||||
}
|
||||
|
||||
.system-nav-btn:hover {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.system-nav-btn:active {
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.system-nav-btn img {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
pointer-events: none;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
/* Home button - pill shape */
|
||||
.system-nav-home {
|
||||
width: 96px;
|
||||
height: 8px;
|
||||
background-color: #FFFFFF;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.system-nav-home:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.system-nav-home:active {
|
||||
opacity: 1.0;
|
||||
}
|
||||
|
||||
/* ============== Screen Layout ============== */
|
||||
/* Standard app screen structure */
|
||||
|
||||
.app-screen {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #121212;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.app-content {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* Content padding for nav bar */
|
||||
.app-content.with-nav {
|
||||
padding-bottom: 56px;
|
||||
}
|
||||
|
||||
/* Content padding for dock */
|
||||
.app-content.with-dock {
|
||||
padding-bottom: 100px;
|
||||
}
|
||||
|
||||
/* ============== Combined Header ============== */
|
||||
/* Status bar + App bar combined */
|
||||
|
||||
.app-header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: #1E1E1E;
|
||||
}
|
||||
|
||||
.app-header.transparent {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.app-header .system-status-bar {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
/* ============== Notification Badge ============== */
|
||||
|
||||
.notification-badge {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
min-width: 20px;
|
||||
height: 20px;
|
||||
background-color: #CF6679;
|
||||
border-radius: 10px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: #FFFFFF;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 6px;
|
||||
}
|
||||
|
||||
/* ============== Recording Indicator ============== */
|
||||
/* Shown when camera/mic is active */
|
||||
|
||||
.recording-indicator {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
background-color: #F44336;
|
||||
border-radius: 6px;
|
||||
animation: recording-pulse 1.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes recording-pulse {
|
||||
0%, 100% { opacity: 1.0; }
|
||||
50% { opacity: 0.4; }
|
||||
}
|
||||
|
||||
/* ============== Divider ============== */
|
||||
|
||||
.header-divider {
|
||||
height: 1px;
|
||||
background-color: #333333;
|
||||
}
|
||||
333
ui/theme.rcss
Normal file
333
ui/theme.rcss
Normal file
@@ -0,0 +1,333 @@
|
||||
/* ==============================================
|
||||
Theme: Material Dark for Virtual Smartphone (VR Optimized)
|
||||
All sizes increased for VR readability and raycast interaction
|
||||
============================================== */
|
||||
|
||||
/* Base body styling */
|
||||
body {
|
||||
font-family: LatoLatin;
|
||||
background-color: #121212;
|
||||
color: #FFFFFF;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
animation: 0.2s cubic-out fade-in;
|
||||
}
|
||||
|
||||
/* ============== Typography (VR-sized) ============== */
|
||||
|
||||
.text-h1 {
|
||||
font-size: 120px;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.text-h2 {
|
||||
font-size: 80px;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.text-h3 {
|
||||
font-size: 64px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.text-h4 {
|
||||
font-size: 48px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.text-h5 {
|
||||
font-size: 32px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.text-h6 {
|
||||
font-size: 28px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.text-body1 {
|
||||
font-size: 22px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.text-body2 {
|
||||
font-size: 18px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.text-caption {
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.text-overline {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
|
||||
/* ============== Text Colors ============== */
|
||||
|
||||
.text-primary {
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.text-secondary {
|
||||
color: #B3B3B3;
|
||||
}
|
||||
|
||||
.text-disabled {
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.text-accent {
|
||||
color: #BB86FC;
|
||||
}
|
||||
|
||||
.text-accent-secondary {
|
||||
color: #03DAC6;
|
||||
}
|
||||
|
||||
.text-error {
|
||||
color: #CF6679;
|
||||
}
|
||||
|
||||
/* ============== Background Colors ============== */
|
||||
|
||||
.bg-primary {
|
||||
background-color: #121212;
|
||||
}
|
||||
|
||||
.bg-surface {
|
||||
background-color: #1E1E1E;
|
||||
}
|
||||
|
||||
.bg-surface-variant {
|
||||
background-color: #2D2D2D;
|
||||
}
|
||||
|
||||
.bg-accent {
|
||||
background-color: #BB86FC;
|
||||
}
|
||||
|
||||
.bg-accent-secondary {
|
||||
background-color: #03DAC6;
|
||||
}
|
||||
|
||||
.bg-error {
|
||||
background-color: #CF6679;
|
||||
}
|
||||
|
||||
/* Hover highlight color - used for interactive element feedback */
|
||||
.bg-hover {
|
||||
background-color: rgba(255, 255, 255, 0.12);
|
||||
}
|
||||
|
||||
/* ============== Spacing Utilities (VR-scaled) ============== */
|
||||
|
||||
.p-0 { padding: 0; }
|
||||
.p-1 { padding: 6px; }
|
||||
.p-2 { padding: 12px; }
|
||||
.p-3 { padding: 18px; }
|
||||
.p-4 { padding: 24px; }
|
||||
.p-5 { padding: 36px; }
|
||||
.p-6 { padding: 48px; }
|
||||
.p-8 { padding: 72px; }
|
||||
|
||||
.m-0 { margin: 0; }
|
||||
.m-1 { margin: 6px; }
|
||||
.m-2 { margin: 12px; }
|
||||
.m-3 { margin: 18px; }
|
||||
.m-4 { margin: 24px; }
|
||||
.m-5 { margin: 36px; }
|
||||
.m-6 { margin: 48px; }
|
||||
.m-8 { margin: 72px; }
|
||||
|
||||
.mt-1 { margin-top: 6px; }
|
||||
.mt-2 { margin-top: 12px; }
|
||||
.mt-3 { margin-top: 18px; }
|
||||
.mt-4 { margin-top: 24px; }
|
||||
|
||||
.mb-1 { margin-bottom: 6px; }
|
||||
.mb-2 { margin-bottom: 12px; }
|
||||
.mb-3 { margin-bottom: 18px; }
|
||||
.mb-4 { margin-bottom: 24px; }
|
||||
|
||||
/* ============== Layout Utilities ============== */
|
||||
|
||||
.flex {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.flex-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.flex-col {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.flex-center {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.flex-between {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.flex-around {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.flex-1 {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.w-full {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.h-full {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.text-left {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.text-right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
/* ============== Border Utilities ============== */
|
||||
|
||||
.rounded {
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.rounded-lg {
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.rounded-xl {
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
.rounded-full {
|
||||
border-radius: 9999px;
|
||||
}
|
||||
|
||||
/* ============== Screen Structure ============== */
|
||||
|
||||
.screen {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: #121212;
|
||||
}
|
||||
|
||||
.screen-content {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/* ============== Animations ============== */
|
||||
|
||||
@keyframes fade-in {
|
||||
0% { opacity: 0; }
|
||||
100% { opacity: 1; }
|
||||
}
|
||||
|
||||
@keyframes slide-in-right {
|
||||
0% { transform: translateX(100px); opacity: 0; }
|
||||
100% { transform: translateX(0px); opacity: 1; }
|
||||
}
|
||||
|
||||
@keyframes slide-in-left {
|
||||
0% { transform: translateX(-100px); opacity: 0; }
|
||||
100% { transform: translateX(0px); opacity: 1; }
|
||||
}
|
||||
|
||||
@keyframes slide-up {
|
||||
0% { transform: translateY(50px); opacity: 0; }
|
||||
100% { transform: translateY(0px); opacity: 1; }
|
||||
}
|
||||
|
||||
@keyframes scale-in {
|
||||
0% { transform: scale(0.9); opacity: 0; }
|
||||
100% { transform: scale(1.0); opacity: 1; }
|
||||
}
|
||||
|
||||
/* Hover highlight animation */
|
||||
@keyframes hover-pulse {
|
||||
0% { background-color: rgba(255, 255, 255, 0.0); }
|
||||
50% { background-color: rgba(255, 255, 255, 0.15); }
|
||||
100% { background-color: rgba(255, 255, 255, 0.1); }
|
||||
}
|
||||
|
||||
/* Screen transition classes */
|
||||
.nav-forward {
|
||||
animation: 0.2s cubic-out slide-in-right;
|
||||
}
|
||||
|
||||
.nav-back {
|
||||
animation: 0.2s cubic-out slide-in-left;
|
||||
}
|
||||
|
||||
.nav-home {
|
||||
animation: 0.2s back-out scale-in;
|
||||
}
|
||||
|
||||
.nav-default {
|
||||
animation: 0.15s cubic-out fade-in;
|
||||
}
|
||||
|
||||
/* Animation utility classes */
|
||||
.animate-fade-in {
|
||||
animation: 0.2s cubic-out fade-in;
|
||||
}
|
||||
|
||||
.animate-slide-right {
|
||||
animation: 0.25s cubic-out slide-in-right;
|
||||
}
|
||||
|
||||
.animate-slide-left {
|
||||
animation: 0.25s cubic-out slide-in-left;
|
||||
}
|
||||
|
||||
.animate-slide-up {
|
||||
animation: 0.2s cubic-out slide-up;
|
||||
}
|
||||
|
||||
.animate-scale-in {
|
||||
animation: 0.2s back-out scale-in;
|
||||
}
|
||||
|
||||
/* ============== Interactive Base Class ============== */
|
||||
/* All interactive elements should use cursor: pointer and have hover feedback */
|
||||
|
||||
.interactive {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.interactive:hover {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.interactive:active {
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
Reference in New Issue
Block a user