wayBetter for X

This commit is contained in:
CRTkafa 2026-10-01 06:01:13 +07:00
commit 434f193217
720 changed files with 147465 additions and 0 deletions

View file

@ -0,0 +1,105 @@
// wayBetter for X on the wayBetter shell frame: what the frame needs to know about X (its pages,
// accounts, address chips, search, palette items, deck columns, the compose dock, which pages have
// a composer, the Twitter/X wording) and the app's own pieces (the wayBetter panel, the drafts
// picker, the install screen). Importing this module sets the frame up; `Shell` is its root.
import { getVersion } from '@tauri-apps/api/app';
import { invoke } from '@tauri-apps/api/core';
import { listen } from '@tauri-apps/api/event';
import { getCurrentWindow } from '@tauri-apps/api/window';
// The panel and the install screen first: their styles load before the frame's shell.css, which
// wins where class names meet (the panel's .bt-seg control vs the address bar's .bt-seg chips).
import { Panel } from '../ui/Panel';
import { InstallHost } from './InstallDialog';
import { DEFAULT_MENU, createShell, menuWith, shell, siteChanged, type ShellSite } from '../../../../waybetter/packages/shell/src/frame/index.ts';
import type { IconName } from '../../../../waybetter/packages/shell/src/frame/icons.tsx';
import { menuModes, openOnboardingWhenDue } from '../../../../waybetter/packages/shell/src/panel/Modes.tsx';
import { reportDetails } from '../../../../waybetter/packages/shell/src/panel/PluginFeatures.tsx';
import { i18n, LANGUAGES, t, tx } from '../i18n/index.ts';
import { language } from '../i18n/language.ts';
import { LOGO_PNG } from '../ui/logo-data';
import { parseAddress } from './addressModel';
import { COMPOSE_URL, COMPOSE_WIDTH, ComposeDock, composeNavigated } from './ComposeDock';
import { DraftPickerHost } from './DraftPicker';
import { isXUrl as hasComposer } from './mediaModel';
import { PRODUCT, X_PALETTE } from './paletteModel';
import { isXUrl } from './paneModel';
import { HOME_URL, LOGIN_URL, LOGOUT_URL, X_ACCOUNT_RULES } from './profileModel';
import { xSearchUrl } from './searchInput';
import { themes } from './themes';
import { X_DECK, searchCompleter } from './xColumns';
const X = 'https://x.com';
/** X's own pages on the new-tab page; names are `newtab.link.<id>` in the catalogs (X's own words for them). */
const LINKS: Array<{ id: string; icon: IconName; path: string | null }> = [
{ id: 'home', icon: 'home', path: null },
{ id: 'notifications', icon: 'bell', path: '/notifications' },
{ id: 'messages', icon: 'mail', path: '/messages' },
{ id: 'bookmarks', icon: 'bookmark', path: '/i/bookmarks' },
{ id: 'explore', icon: 'explore', path: '/explore' },
{ id: 'lists', icon: 'list', path: '/i/lists' },
];
/** Site-chip wording in the address bar and the new-tab search: the `twitterWording` tweak. */
let wording: 'twitter' | 'x' = 'twitter';
function readWording() {
invoke<{ twitterWording?: boolean }>('features_get')
.then(features => {
const next = features?.twitterWording === false ? 'x' : 'twitter';
if (next === wording) return;
wording = next;
siteChanged();
})
.catch(() => {});
}
export const X_SITE: ShellSite = {
name: 'X',
product: PRODUCT,
deferWindowShow: true,
logo: LOGO_PNG,
homeUrl: HOME_URL,
loginUrl: LOGIN_URL,
logoutUrl: LOGOUT_URL,
isSiteUrl: isXUrl,
accounts: X_ACCOUNT_RULES,
searchUrl: xSearchUrl,
address: url => parseAddress(url, wording),
addressCompleter: searchCompleter(),
panel: { id: 'better-twttr', title: 'wayBetter', component: Panel },
newTab: {
search: () => t('newtab.search', { site: wording === 'x' ? 'X' : 'Twitter' }),
links: LINKS.map(link => ({ id: link.id, icon: link.icon, label: () => tx(`newtab.link.${link.id}`), url: home => (link.path ? X + link.path : home) })),
},
palette: X_PALETTE,
deck: { ...X_DECK, compose: { url: COMPOSE_URL, width: COMPOSE_WIDTH, Dock: ComposeDock, navigated: composeNavigated } },
media: { canAttach: hasComposer },
// The panel's Languages section (string packs) is there: the app menu links to it.
features: { strings: true },
themes,
// The app menu (the titlebar's "Settings and more", Alt+F): the framework's rows, and the draft vault beside the panel's other sections.
menu: {
groups: menuWith(DEFAULT_MENU, 'workspaces', [{ id: 'drafts', icon: 'draft', label: () => `${tx('palette.section.drafts')}…`, run: () => shell.openPanelSection('drafts') }]),
languages: { list: LANGUAGES, state: language.state, subscribe: language.subscribe, set: language.set },
modes: menuModes(),
version: getVersion,
// "Report a problem": the public repo's new-issue page, and which features paused themselves.
report: { issueUrl: 'https://git.crt.fyi/CRT/waybetter-for-x/issues/new', details: reportDetails },
},
overlays: [DraftPickerHost, InstallHost],
start() {
// The tweak is set in the wayBetter panel; coming back to the window picks up a change.
readWording();
window.addEventListener('focus', readWording);
// The first-run screen (Basic / Advanced / Power User) is the panel's: open it when it is due.
openOnboardingWhenDue();
return () => window.removeEventListener('focus', readWording);
},
};
export const Shell = createShell(X_SITE, {
native: { invoke, listen, currentWindow: getCurrentWindow },
i18n,
dev: import.meta.env.DEV,
});