Skip to content

Feature/my awesome feature#3984

Open
SipheDilima247 wants to merge 5 commits into
koala73:mainfrom
SipheDilima247:feature/my-awesome-feature
Open

Feature/my awesome feature#3984
SipheDilima247 wants to merge 5 commits into
koala73:mainfrom
SipheDilima247:feature/my-awesome-feature

Conversation

@SipheDilima247
Copy link
Copy Markdown

Summary

Type of change

  • Bug fix
  • New feature
  • New data source / feed
  • New map layer
  • Refactor / code cleanup
  • Documentation
  • CI / Build / Infrastructure

Affected areas

  • Map / Globe
  • News panels / RSS feeds
  • AI Insights / World Brief
  • Market Radar / Crypto
  • Desktop app (Tauri)
  • API endpoints (/api/*)
  • Config / Settings
  • Other:

Checklist

  • Tested on worldmonitor.app variant
  • Tested on tech.worldmonitor.app variant (if applicable)
  • New RSS feed domains added to api/rss-proxy.js allowlist (if adding feeds)
  • No API keys or secrets committed
  • TypeScript compiles without errors (npm run typecheck)

Screenshots

@vercel
Copy link
Copy Markdown

vercel Bot commented May 31, 2026

@SipheDilima247 is attempting to deploy a commit to the World Monitor Team on Vercel.

A member of the Team first needs to authorize it.

@github-actions github-actions Bot added the trust:safe Brin: contributor trust score safe label May 31, 2026
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 31, 2026

Greptile Summary

This PR introduces a Country Profile feature: a modal overlay showing aggregated per-country intelligence (military, economic, cyber, news), a CountrySelector component for picking a country, and city-level search support in the global search modal.

  • CountryProfileManager, CountrySelector, CountryProfileView, and CountryProfilePanel are all newly added; SearchManager and SearchModal are extended with a city source type.
  • hls.js is bumped from ^1.6.15 to ^1.8.0 and three-globe gets an explicit Vite alias and optimizeDeps entry.

Confidence Score: 3/5

The core country-selection flow is broken before a user can view any profile data — merging as-is ships a non-functional feature.

Two independent defects both block the primary user interaction: the selector modal is rendered into a detached DOM node (invisible to the user) and closeCountrySelector() always throws because CountrySelector has no destroy() method. The city search duplication is a separate third bug. Together, these issues mean the headline feature does not work on merge.

src/app/country-profile-manager.ts and src/components/CountrySelector.ts need the most attention — the broken destroy contract and the detached container are both in this pair of files.

Important Files Changed

Filename Overview
src/app/country-profile-manager.ts New manager for country selector and profile view — contains two critical bugs: destroy() call on CountrySelector which has no such method, and the selector container is never mounted into the live DOM.
src/components/CountrySelector.ts New country picker component — missing destroy() method (called by CountryProfileManager), and the outside-click document listener is never cleaned up.
src/components/CountryProfileView.ts New modal overlay for country intelligence — well-structured but leaks the Escape keydown event listener on every open/close cycle.
src/components/CountryProfilePanel.ts New panel component that aggregates country intelligence — correct use of escapeHtml and panel lifecycle; reads from window globals instead of AppContext for risk scores and news.
src/app/search-manager.ts Adds city search support and 'city' action handler — but registerSource('city', getCitySearchItems()) is called twice, once in init and once in buildSearchItems(), producing duplicate results.
src/services/city-geometry.ts New thin wrapper around CITY_COORDS data for building city search items — straightforward and correct.
src/services/country-geometry.ts Adds isValidCountryCode() utility — correctly normalises the input and validates against the loaded index.
src/components/LiveNewsPanel.ts Only change is adding as any to the hls.js dynamic import path to suppress a type error from the version bump; the cast should be removed.
vite.config.ts Adds three-globe alias and top-level optimizeDeps for hls.js and three-globe — both additions are correctly placed at the top-level config object.
src/app/panel-layout.ts Registers the new CountryProfilePanel using the existing shouldCreatePanel/setAppContext pattern — consistent with the rest of the codebase.

Sequence Diagram

sequenceDiagram
    participant User
    participant App
    participant CPManager as CountryProfileManager
    participant CSelector as CountrySelector
    participant CPView as CountryProfileView
    participant CPPanel as CountryProfilePanel

    User->>App: openCountrySelector()
    App->>CPManager: openCountrySelector()
    CPManager->>CSelector: "new CountrySelector({ container: selectorContainer })"
    Note over CSelector: ⚠️ selectorContainer is detached — UI never visible
    CSelector-->>CPManager: (selector ready, but invisible)

    User->>CSelector: selects a country
    CSelector->>CPManager: onCountrySelected(code, name)
    CPManager->>CPManager: closeCountrySelector()
    CPManager->>CSelector: destroy()
    Note over CSelector: ❌ destroy() does not exist → TypeError

    CPManager->>CPView: new CountryProfileView(...)
    CPView->>CPView: render() + injectStyles()
    Note over CPView: keydown listener added, never removed
    CPView-->>CPManager: (profile view displayed)

    CPManager->>CPManager: loadCountryData(code, name)
    CPManager->>CPPanel: loadCountry(code, name)
    CPPanel->>CPPanel: aggregateCountryData()
    Note over CPPanel: reads window.__COUNTRY_PROFILE_NEWS
    CPPanel-->>CPView: renderCountryProfile(data)
Loading

Reviews (1): Last reviewed commit: "feat: update city filter with city-focus..." | Re-trigger Greptile

Comment on lines +45 to +52
*/
public closeCountrySelector(): void {
if (this.countrySelector) {
this.countrySelector.destroy();
this.countrySelector = null;
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 CountrySelector.destroy() is missing — closes country selector unconditionally

closeCountrySelector() calls this.countrySelector.destroy(), but CountrySelector has no destroy() method defined. Every time a country is selected, selectCountry() calls closeCountrySelector(), which will immediately throw TypeError: this.countrySelector.destroy is not a function before any profile can load. This makes the country selection flow completely non-functional.

Comment on lines +18 to +22
private selectorContainer: HTMLElement;

constructor(appContext: AppContext) {
this.appContext = appContext;
this.selectorContainer = document.createElement('div');
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 selectorContainer never attached to the document — selector is invisible

this.selectorContainer is created with document.createElement('div') but never appended to document.body or appContext.container. When it is passed as options.container to CountrySelector, that class uses it directly without inserting it into the live DOM tree (it only appends to document.body when no container is provided). Since the element is detached, CSS position: fixed has no effect — the rendered selector UI never appears on screen.

Comment thread src/app/search-manager.ts
Comment on lines 711 to 712
data: m,
})));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Duplicate city source registration

registerSource('city', getCitySearchItems()) is called twice: once in the init() flow (around line 210) and again here at the end of buildSearchItems(). Depending on SearchModal.registerSource's implementation, this either double-populates city results (showing every city twice) or silently overwrites the earlier registration with a second full copy of the same data. The second call should be removed.

Comment on lines +474 to +479
if (indicator) indicator.remove();
this.onClose?.();
}

public destroy(): void {
this.close();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Keyboard event listener is never removed — accumulates on repeated open/close

keyHandler is a local variable whose reference is not stored on the instance. destroy()close() removes the DOM container but never calls document.removeEventListener('keydown', keyHandler). Each time a CountryProfileView is opened and closed, one more stale listener remains on document. After several cycles, every Escape keypress triggers multiple close() calls on discarded instances, each dispatching this.onClose?.() unnecessarily.

Comment on lines +115 to +120
return title.includes(countryCode.toLowerCase()) ||
title.includes(countryCode.toUpperCase()) ||
countries.includes(countryCode.toLowerCase());
});

// Store filtered news for use in panels
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Global window properties used for cross-component state instead of AppContext

window.__SELECTED_COUNTRY_CODE and window.__COUNTRY_PROFILE_NEWS (also read in CountryProfilePanel as window.__COUNTRY_RISK_SCORES) bypass the established AppContext state tree. This pattern risks stale reads if components initialize before the values are set, and the globals persist in any frame/tab that shares the same window, making it impossible to safely open two country profiles across different contexts. The existing AppContext has appropriate places for this state.

} else {
// Chrome / Firefox: lazy-load hls.js only when needed
const { default: Hls } = await import('hls.js');
const { default: Hls } = await import('hls.js' as any);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 The as any cast on the module specifier suppresses TypeScript's module resolution for hls.js. With the version bump to ^1.8.0 the types should be present; if there is a genuine type error, it should be addressed in the type definitions rather than silenced here.

Suggested change
const { default: Hls } = await import('hls.js' as any);
const { default: Hls } = await import('hls.js');

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@koala73
Copy link
Copy Markdown
Owner

koala73 commented Jun 1, 2026

did you see the comments greptile wrote on your PR @SipheDilima247 ??

@SipheDilima247
Copy link
Copy Markdown
Author

did you see the comments greptile wrote on your PR @SipheDilima247 ??

Yes, currently working on getting the feature working properly and fixing these bugs collectively. Will keep pushing throughout the day

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

trust:safe Brin: contributor trust score safe

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants