diff --git a/packages/extension/src/types/disposable.ts b/packages/common/src/types/disposable.ts
similarity index 96%
rename from packages/extension/src/types/disposable.ts
rename to packages/common/src/types/disposable.ts
index 668211ce..46f580df 100644
--- a/packages/extension/src/types/disposable.ts
+++ b/packages/common/src/types/disposable.ts
@@ -15,6 +15,7 @@
*
* SPDX-License-Identifier: Apache-2.0
***********************************************************************/
+export const IDisposable = Symbol.for('IDisposable');
export interface IDisposable {
dispose(): void;
diff --git a/packages/extension/src/manager/contexts-states-dispatcher.spec.ts b/packages/extension/src/manager/contexts-states-dispatcher.spec.ts
index c40f4937..40dfd379 100644
--- a/packages/extension/src/manager/contexts-states-dispatcher.spec.ts
+++ b/packages/extension/src/manager/contexts-states-dispatcher.spec.ts
@@ -18,7 +18,7 @@
import { beforeAll, beforeEach, expect, test, vi } from 'vitest';
-import type { IDisposable } from '/@/types/disposable.js';
+import type { IDisposable } from '/@common/types/disposable.js';
import type { ContextPermission } from '/@common/model/kubernetes-contexts-permissions.js';
import type { ContextHealthState } from './context-health-checker.js';
@@ -31,7 +31,7 @@ import type { RpcExtension } from '/@common/rpc/rpc.js';
import { CONTEXTS_HEALTHS, CONTEXTS_PERMISSIONS, RESOURCES_COUNT, UPDATE_RESOURCE } from '/@common/channels.js';
import type { ExtensionContext, TelemetryLogger } from '@podman-desktop/api';
import type { Container } from 'inversify';
-import { InversifyBinding } from '../inject/inversify-binding.js';
+import { InversifyBinding } from '/@/inject/inversify-binding.js';
let container: Container;
const contextsManagerMock: ContextsManager = {
diff --git a/packages/extension/src/types/emitter.ts b/packages/extension/src/types/emitter.ts
index 48b49a41..de60eb79 100644
--- a/packages/extension/src/types/emitter.ts
+++ b/packages/extension/src/types/emitter.ts
@@ -18,7 +18,7 @@
/* eslint-disable @typescript-eslint/no-unsafe-function-type */
-import type { IDisposable } from './disposable.js';
+import type { IDisposable } from '/@common/types/disposable.js';
export type DisposableGroup = { push(disposable: IDisposable): void } | { add(disposable: IDisposable): void };
diff --git a/packages/webview/src/Main.svelte b/packages/webview/src/Main.svelte
index ed8f5649..fcf5d036 100644
--- a/packages/webview/src/Main.svelte
+++ b/packages/webview/src/Main.svelte
@@ -1,6 +1,30 @@
-Kubernetes Dashboard
+{#if mainContext}
+
+{/if}
diff --git a/packages/webview/src/MainContextAware.svelte b/packages/webview/src/MainContextAware.svelte
new file mode 100644
index 00000000..cbe4238a
--- /dev/null
+++ b/packages/webview/src/MainContextAware.svelte
@@ -0,0 +1,28 @@
+
+
+{#if initialized}
+ Kubernetes Dashboard
+
+
+
+
+{/if}
diff --git a/packages/webview/src/component/ResourcesCount.svelte b/packages/webview/src/component/ResourcesCount.svelte
new file mode 100644
index 00000000..b488e397
--- /dev/null
+++ b/packages/webview/src/component/ResourcesCount.svelte
@@ -0,0 +1,14 @@
+
+
+{#if resourcesCount.data?.counts}
+
+ {#each resourcesCount.data.counts as count, index (index)}
+ - {count.contextName}/{count.resourceName}: {count.count}
+ {/each}
+
+{/if}
diff --git a/packages/webview/src/inject/inversify-binding.ts b/packages/webview/src/inject/inversify-binding.ts
new file mode 100644
index 00000000..b4a261f6
--- /dev/null
+++ b/packages/webview/src/inject/inversify-binding.ts
@@ -0,0 +1,48 @@
+/**********************************************************************
+ * Copyright (C) 2025 Red Hat, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ ***********************************************************************/
+
+import 'reflect-metadata';
+
+import type { WebviewApi } from '@podman-desktop/webview-api';
+import { Container } from 'inversify';
+
+import { RpcBrowser } from '/@common/rpc/rpc';
+
+import { statesModule } from '/@/state/state-module';
+
+export class InversifyBinding {
+ #container: Container | undefined;
+
+ #rpcBrowser: RpcBrowser;
+ #webviewApi: WebviewApi;
+
+ constructor(rpcBrowser: RpcBrowser, webviewApi: WebviewApi) {
+ this.#rpcBrowser = rpcBrowser;
+ this.#webviewApi = webviewApi;
+ }
+
+ public async initBindings(): Promise {
+ this.#container = new Container();
+ this.#container.bind(RpcBrowser).toConstantValue(this.#rpcBrowser);
+ this.#container.bind('WebviewApi').toConstantValue(this.#webviewApi);
+
+ await this.#container.load(statesModule);
+
+ return this.#container;
+ }
+}
diff --git a/packages/webview/src/main.ts b/packages/webview/src/main.ts
new file mode 100644
index 00000000..0e259551
--- /dev/null
+++ b/packages/webview/src/main.ts
@@ -0,0 +1,65 @@
+/**********************************************************************
+ * Copyright (C) 2025 Red Hat, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ ***********************************************************************/
+
+import { RpcBrowser } from '/@common/rpc/rpc';
+
+import { InversifyBinding } from './inject/inversify-binding';
+import { IDisposable } from '/@common/types/disposable';
+import { States } from './state/states';
+import { StateObject } from './state/util/state-object.svelte';
+
+export interface MainContext {
+ states: States;
+}
+
+export class Main implements IDisposable {
+ private disposables: IDisposable[] = [];
+
+ async init(): Promise {
+ const webViewApi = acquirePodmanDesktopApi();
+
+ const rpcBrowser: RpcBrowser = new RpcBrowser(window, webViewApi);
+
+ const inversifyBinding = new InversifyBinding(rpcBrowser, webViewApi);
+ const container = await inversifyBinding.initBindings();
+
+ // Grab all state object instances
+ const stateObjectInstances = container.getAll>(StateObject);
+
+ // Init all state object instances
+ for (const stateObjectInstance of stateObjectInstances) {
+ await stateObjectInstance.init();
+ }
+
+ // Register all disposables
+ const disposables = await container.getAllAsync(IDisposable);
+ this.disposables.push(...disposables);
+
+ const mainContext: MainContext = {
+ states: await container.getAsync(States),
+ };
+
+ return mainContext;
+ }
+
+ dispose(): void {
+ for (const disposable of this.disposables) {
+ disposable.dispose();
+ }
+ }
+}
diff --git a/packages/webview/src/state/resources-count.svelte.ts b/packages/webview/src/state/resources-count.svelte.ts
new file mode 100644
index 00000000..9a53ca28
--- /dev/null
+++ b/packages/webview/src/state/resources-count.svelte.ts
@@ -0,0 +1,40 @@
+/**********************************************************************
+ * Copyright (C) 2025 Red Hat, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ ***********************************************************************/
+
+import { inject, injectable } from 'inversify';
+
+import { RESOURCES_COUNT } from '/@common/channels';
+import { RpcBrowser } from '/@common/rpc/rpc';
+
+import { AbsStateObjectImpl, type StateObject } from './util/state-object.svelte';
+import type { ResourcesCountInfo } from '/@common/model/resources-count-info';
+
+// Define a state for the ResourcesCountInfo
+@injectable()
+export class StateResourcesCountInfo
+ extends AbsStateObjectImpl
+ implements StateObject
+{
+ constructor(@inject(RpcBrowser) rpcBrowser: RpcBrowser) {
+ super(rpcBrowser);
+ }
+
+ async init(): Promise {
+ await this.initChannel(RESOURCES_COUNT);
+ }
+}
diff --git a/packages/webview/src/state/state-module.ts b/packages/webview/src/state/state-module.ts
new file mode 100644
index 00000000..c7374423
--- /dev/null
+++ b/packages/webview/src/state/state-module.ts
@@ -0,0 +1,34 @@
+/**********************************************************************
+ * Copyright (C) 2025 Red Hat, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ ***********************************************************************/
+
+import { ContainerModule } from 'inversify';
+
+import { States } from './states';
+import { StateObject } from './util/state-object.svelte';
+import { IDisposable } from '/@common/types/disposable';
+import { StateResourcesCountInfo } from './resources-count.svelte';
+
+const statesModule = new ContainerModule(options => {
+ options.bind(States).toSelf().inSingletonScope();
+
+ options.bind(StateResourcesCountInfo).toSelf().inSingletonScope();
+ options.bind(StateObject).toService(StateResourcesCountInfo);
+ options.bind(IDisposable).toService(StateResourcesCountInfo);
+});
+
+export { statesModule };
diff --git a/packages/webview/src/state/states.ts b/packages/webview/src/state/states.ts
new file mode 100644
index 00000000..418c47ac
--- /dev/null
+++ b/packages/webview/src/state/states.ts
@@ -0,0 +1,30 @@
+/**********************************************************************
+ * Copyright (C) 2025 Red Hat, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ ***********************************************************************/
+
+import { inject, injectable } from 'inversify';
+import { StateResourcesCountInfo } from './resources-count.svelte';
+
+@injectable()
+export class States {
+ @inject(StateResourcesCountInfo)
+ private _stateResourcesCountInfoUI: StateResourcesCountInfo;
+
+ get stateResourcesCountInfoUI(): StateResourcesCountInfo {
+ return this._stateResourcesCountInfoUI;
+ }
+}
diff --git a/packages/webview/src/state/util/state-object.svelte.ts b/packages/webview/src/state/util/state-object.svelte.ts
new file mode 100644
index 00000000..5dcf189a
--- /dev/null
+++ b/packages/webview/src/state/util/state-object.svelte.ts
@@ -0,0 +1,56 @@
+/**********************************************************************
+ * Copyright (C) 2025 Red Hat, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ ***********************************************************************/
+
+import type { RpcBrowser, RpcChannel } from '/@common/rpc/rpc';
+import type { IDisposable } from '/@common/types/disposable';
+
+export const StateObject = Symbol.for('StateObject');
+export interface StateObject extends IDisposable {
+ get data(): T | undefined;
+ init(): Promise;
+}
+
+// Allow to receive event for a given object
+export abstract class AbsStateObjectImpl implements StateObject {
+ #data = $state<{ value: T | undefined }>({ value: undefined });
+
+ #rpcBrowser: RpcBrowser;
+
+ #disposable: IDisposable | undefined;
+
+ constructor(rpcBrowser: RpcBrowser) {
+ this.#rpcBrowser = rpcBrowser;
+ this.#data.value = undefined;
+ }
+
+ get data(): T | undefined {
+ return this.#data.value;
+ }
+
+ protected async initChannel(channel: RpcChannel): Promise {
+ this.#disposable = this.#rpcBrowser.on(channel, value => {
+ this.#data.value = value;
+ });
+ }
+
+ dispose(): void {
+ this.#disposable?.dispose();
+ }
+
+ abstract init(): Promise;
+}