window.open

This commit is contained in:
2026-05-13 21:12:39 +02:00
parent 757d999b9a
commit f79a98229a
10 changed files with 277 additions and 269 deletions

30
bbs.rb
View File

@@ -65,41 +65,41 @@ MAIN_APP = BBS::Application.define do
menubar do
menu '&File' do
item '&Bulletin' do UI::Windows.bulletin(self, services: SERVICES) end
item '&System Info' do UI::Windows.system_info(self, services: SERVICES) end
item '&Bulletin' do UI::Windows::Bulletin.open(self, services: SERVICES) end
item '&System Info' do UI::Windows::SystemInfo.open(self, services: SERVICES) end
separator
item 'E&xit', shortcut: 'Alt-X' do :halt end
end
menu '&Messages' do
item '&Boards…', shortcut: 'F2' do UI::Windows.messages(self, services: SERVICES) end
item '&Boards…', shortcut: 'F2' do UI::Windows::Messages.open(self, services: SERVICES) end
item '&New Post…', shortcut: 'F3' do
board = SERVICES[:boards].boards.first
UI::Windows.compose_message(self, services: SERVICES, board: board)
UI::Windows::ComposeMessage.open(self, services: SERVICES, board: board)
end
end
menu '&Files' do
item '&Blog Posts' do UI::Windows.wiki(self, services: SERVICES, category: 'blog', title: 'Blog Posts') end
item '&HowTo Guides' do UI::Windows.wiki(self, services: SERVICES, category: 'howto', title: 'HowTo Guides') end
item '&Game Catalog' do UI::Windows.games(self, services: SERVICES) end
item '&Blog Posts' do UI::Windows::Wiki.open(self, services: SERVICES, category: 'blog', title: 'Blog Posts') end
item '&HowTo Guides' do UI::Windows::Wiki.open(self, services: SERVICES, category: 'howto', title: 'HowTo Guides') end
item '&Game Catalog' do UI::Windows::Games.open(self, services: SERVICES) end
end
menu '&Users' do
item '&Online' do UI::Windows.online_users(self, services: SERVICES) end
item '&Last Callers' do UI::Windows.last_callers(self, services: SERVICES) end
item '&Profile…' do UI::Windows.profile(self, services: SERVICES) end
item '&Online' do UI::Windows::OnlineUsers.open(self, services: SERVICES) end
item '&Last Callers' do UI::Windows::LastCallers.open(self, services: SERVICES) end
item '&Profile…' do UI::Windows::Profile.open(self, services: SERVICES) end
end
menu '&Chat' do
item '&Open chat', shortcut: 'F4' do UI::Windows.chat(self, services: SERVICES) end
item '&Open chat', shortcut: 'F4' do UI::Windows::Chat.open(self, services: SERVICES) end
end
menu 'S&ysop' do
item '&Console', shortcut: 'F10' do
name = (context[:username] || '').downcase
if SYSOPS.include?(name)
UI::Windows.sysop_console(self, services: SERVICES)
UI::Windows::SysopConsole.open(self, services: SERVICES)
else
BBS::Dialogs.message(self, 'Sysop access denied.', title: ' Sysop ')
end
@@ -123,12 +123,12 @@ MAIN_APP = BBS::Application.define do
end
status_hint('F1', 'Help') { BBS::Dialogs.message(self, 'Top menu: Alt+letter — Windows: Tab/Shift-Tab — Close: Esc') }
status_hint('F2', 'Boards') { UI::Windows.messages(self, services: SERVICES) }
status_hint('F2', 'Boards') { UI::Windows::Messages.open(self, services: SERVICES) }
status_hint('F3', 'Post') {
board = SERVICES[:boards].boards.first
UI::Windows.compose_message(self, services: SERVICES, board: board)
UI::Windows::ComposeMessage.open(self, services: SERVICES, board: board)
}
status_hint('F4', 'Chat') { UI::Windows.chat(self, services: SERVICES) }
status_hint('F4', 'Chat') { UI::Windows::Chat.open(self, services: SERVICES) }
status_hint('F10', 'Menu') { menubar.open(0) }
status_hint('Alt-X', 'Exit') { :halt }

View File

@@ -2,11 +2,10 @@
module UI
module Windows
module_function
# Login splash: ASCII banner + last callers + new message counts. Closed
# with Enter / Esc / clicking the OK button.
def bulletin(app, services:)
class Bulletin
def self.open(app, services:)
banner_lines = [
" \e[1;33m _____ _ _ ____ ____ _____\e[0m",
" \e[1;33m|_ _|__ | | | |_ __ _ _ _ ___ | __ )| __ )/ ____|\e[0m",
@@ -34,4 +33,5 @@ module UI
)
end
end
end
end

View File

@@ -2,9 +2,8 @@
module UI
module Windows
module_function
def chat(app, services:)
class Chat
def self.open(app, services:)
username = app.context[:username] || 'Anonymous'
BBS::Windows::Stream.open(app,
title: ' Live Chat ',
@@ -15,8 +14,10 @@ module UI
)
end
def format_chat_line(line)
def self.format_chat_line(line)
" \e[2;37m#{line.timestamp}\e[0m \e[1;33m#{line.username}\e[0m: #{line.text}"
end
private_class_method :format_chat_line
end
end
end

View File

@@ -2,10 +2,9 @@
module UI
module Windows
module_function
# Game catalog browser. Left=titles, right=card with story + links.
def games(app, services:)
class Games
def self.open(app, services:)
BBS::Windows::MasterDetail.open(app,
title: ' Game Catalog ',
items: services[:games].all,
@@ -16,7 +15,7 @@ module UI
)
end
def game_card_lines(game, width)
def self.game_card_lines(game, width)
lines = []
lines << " \e[1;33m#{game.title}\e[0m"
lines << " \e[0;36m#{game.platform} · #{game.author}\e[0m"
@@ -30,5 +29,7 @@ module UI
end
lines
end
private_class_method :game_card_lines
end
end
end

View File

@@ -2,11 +2,10 @@
module UI
module Windows
module_function
# Two-pane message reader: left=board list, right=messages of the selected
# board. Press &New to compose, &Refresh to reload.
def messages(app, services:)
class Messages
def self.open(app, services:)
BBS::Windows::MasterDetail.open(app,
title: ' Message Boards ',
items: services[:boards].boards,
@@ -17,7 +16,7 @@ module UI
actions: [
{ label: '&New', align: :left, on_click: ->(window) {
board = window.list_widget.selected_item
compose_message(app, services: services, board: board,
ComposeMessage.open(app, services: services, board: board,
on_posted: -> { window.reload_detail })
:keep
} },
@@ -29,7 +28,7 @@ module UI
)
end
def board_lines(services, board, width)
def self.board_lines(services, board, width)
msgs = services[:boards].messages(board.id, 100)
return [" (no messages on #{board.title} yet — press 'n' to start one)"] if msgs.empty?
lines = []
@@ -40,8 +39,11 @@ module UI
end
lines
end
private_class_method :board_lines
end
def compose_message(app, services:, board:, on_posted: nil)
class ComposeMessage
def self.open(app, services:, board:, on_posted: nil)
return BBS::Dialogs.message(app, 'Select a board first.', title: ' Compose ') unless board
username = app.context[:username] || 'Anonymous'
@@ -68,4 +70,5 @@ module UI
)
end
end
end
end

View File

@@ -2,9 +2,8 @@
module UI
module Windows
module_function
def profile(app, services:)
class Profile
def self.open(app, services:)
username = app.context[:username] || 'Anonymous'
existing = services[:profile].find(username) || {}
BBS::Windows::Form.open(app,
@@ -28,4 +27,5 @@ module UI
)
end
end
end
end

View File

@@ -2,9 +2,8 @@
module UI
module Windows
module_function
def sysop_console(app, services:)
class SysopConsole
def self.open(app, services:)
body = []
body << " Sysop: \e[1;33m#{app.context[:username]}\e[0m"
body << :spacer
@@ -39,4 +38,5 @@ module UI
)
end
end
end
end

View File

@@ -2,9 +2,8 @@
module UI
module Windows
module_function
def system_info(app, services:)
class SystemInfo
def self.open(app, services:)
rows = [
['Online users', services[:online].count.to_s],
['Messages', services[:boards].total_count.to_s],
@@ -21,7 +20,7 @@ module UI
)
end
def format_uptime(start)
def self.format_uptime(start)
return '' unless start
seconds = (Time.now - start).to_i
h = seconds / 3600
@@ -29,5 +28,7 @@ module UI
s = seconds % 60
"#{h}h #{m}m #{s}s"
end
private_class_method :format_uptime
end
end
end

View File

@@ -2,9 +2,8 @@
module UI
module Windows
module_function
def online_users(app, services:)
class OnlineUsers
def self.open(app, services:)
users = services[:online].user_list
username = app.context[:username]
body = if users.empty?
@@ -18,8 +17,10 @@ module UI
scroll: true, body: body
)
end
end
def last_callers(app, services:)
class LastCallers
def self.open(app, services:)
callers = services[:last_callers].recent(15)
body = if callers.empty?
[' (no callers recorded yet)']
@@ -36,4 +37,5 @@ module UI
)
end
end
end
end

View File

@@ -2,11 +2,10 @@
module UI
module Windows
module_function
# Split-view wiki browser. Left=titles, right=selected page content
# (rendered with markdown-aware formatting).
def wiki(app, services:, category:, title:)
class Wiki
def self.open(app, services:, category:, title:)
items = services[:wiki].list(category)
BBS::Windows::MasterDetail.open(app,
title: " #{title} ",
@@ -25,4 +24,5 @@ module UI
)
end
end
end
end