import thisHtml from './projects-explorer.html';
import thisCss from './projects-explorer.css';
import stylesCss from '../../../styles.css';
import rootCss from '../../../root.css';
import { extractErrorMsg, getSaferSubstring, } from "@ibgib/helper-gib/dist/helpers/utils-helper.mjs";
import { IbGibAddr } from "@ibgib/ts-gib/dist/types.mjs";
import { IbGib_V1 } from "@ibgib/ts-gib/dist/V1/types.mjs";
import { getIbAndGib, getIbGibAddr } from "@ibgib/ts-gib/dist/helper.mjs";
import { getGibInfo, } from "@ibgib/ts-gib/dist/V1/transforms/transform-helper.mjs";
import { MetaspaceService } from "@ibgib/core-gib/dist/witness/space/metaspace/metaspace-types.mjs";
import { fnObs } from "@ibgib/core-gib/dist/common/pubsub/observer/observer-helper.mjs";
import { IbGibTimelineUpdateInfo } from "@ibgib/core-gib/dist/common/other/other-types.mjs";
import { getTjpAddr } from "@ibgib/core-gib/dist/common/other/ibgib-helper.mjs";
import { AgentWitnessAny, } from "@ibgib/web-gib/dist/witness/agent/agent-one-file.mjs";
import {
getDeterministicColorInfo,
getColorStrings,
} from "@ibgib/web-gib/dist/helpers.mjs";
import {
alertUser, copyToClipboard,
highlightElement,
shadowRoot_getElementById,
} from "@ibgib/web-gib/dist/helpers.web.mjs";
import { GEMINI_DEFAULT_MODEL_STR, } from "@ibgib/web-gib/dist/witness/agent/gemini/gemini-constants.mjs";
import { tellUserFunctionInfo } from "@ibgib/web-gib/dist/api/commands/chat/tell-user.mjs";
import { getProjects, parseProjectIb, } from "@ibgib/web-gib/dist/common/project/project-helper.mjs";
import { getAgentsSvc } from "@ibgib/web-gib/dist/witness/agent/agents-service-v1.mjs";
import {
IbGibDynamicComponentInstanceBase_ParentOfTabs,
IbGibDynamicComponentMetaBase,
} from "@ibgib/web-gib/dist/ui/component/ibgib-dynamic-component-bases.mjs";
import {
ElementsBase, IbGibDynamicComponentInstance,
IbGibDynamicComponentInstanceInitOpts, ChildInfoBase,
} from "@ibgib/web-gib/dist/ui/component/component-types.mjs";
import { getComponentSvc } from "@ibgib/web-gib/dist/ui/component/ibgib-component-service.mjs";
import { getAgents } from "@ibgib/web-gib/dist/witness/agent/agent-helpers.mjs";
import { debounce, getGlobalMetaspace_waitIfNeeded } from "@ibgib/web-gib/dist/helpers.mjs";
import { IbGibSettings, SettingsWithTabs } from "@ibgib/web-gib/dist/common/settings/settings-types.mjs";
import { SettingsType } from "@ibgib/web-gib/dist/common/settings/settings-constants.mjs";
import { GLOBAL_LOG_A_LOT, } from "../../../constants.mjs";
import {
AGENT_INITIAL_CHAT_TEXT_PROJECTSAGENT,
AGENT_INITIAL_SYSTEM_TEXT_PROJECTSAGENT
} from "../../../agent-texts/projects-agent-texts.mjs";
import { EXPLORERITEM_COMPONENT_NAME, ExplorerItemComponentInstance } from "../../common/explorer-item/explorer-item-component-one-file.mjs";
import { getComponentCtorArg, getDefaultFnGetAPIKey, getIbGibGlobalThis_BlankGib } from '../../../helpers.web.mjs';
const logalot = GLOBAL_LOG_A_LOT;
export const AGENT_SPECIAL_IBGIB_TYPE_PROJECTSAGENT = 'projectsagent';
export const AGENT_AVAILABLE_FUNCTIONS_PROJECTSAGENT = [
tellUserFunctionInfo,
// ...RenderAgentFunctionInfos,
];
export const PROJECTS_EXPLORER_COMPONENT_NAME: string = 'ibgib-projects-explorer';
export class ProjectsExplorerComponentMeta extends IbGibDynamicComponentMetaBase {
protected override lc: string = `[${ProjectsExplorerComponentMeta.name}]`;
/**
* temporary regexp path for our initial dev. this component will become
* attached to actual ib^gib addrs
*
*
* either the path is
* * /apps/projects/gib/projects
* * /apps/projects/ABC123.456DEF/project%20my-project%201744553032000
*
* The point is that it's either a bland projects^gib to indicate create a
* new project or it's a valid gib and a valid project ib. lazy regexp here
*/
// routeRegExp?: RegExp = /apps\/projects\/(gib|.*)\/(projects|project \/.*\/.*)?/;
routeRegExp?: RegExp = new RegExp(`^${PROJECTS_EXPLORER_COMPONENT_NAME}$`);
componentName: string = PROJECTS_EXPLORER_COMPONENT_NAME;
constructor() {
super(getComponentCtorArg());
customElements.define(this.componentName, ProjectsExplorerComponentInstance);
}
async createInstance({
path,
ibGibAddr
}: {
/**
* todo: store this in the instance (i think) but will change this when needed
*/
path: string;
ibGibAddr: IbGibAddr;
}): Promise {
const lc = `${this.lc}[${this.createInstance.name}]`;
try {
if (logalot) { console.log(`${lc} starting... (I: 75ddd5c933420026d8129cf5dfb5e825)`); }
const component = document.createElement(this.componentName) as ProjectsExplorerComponentInstance;
await component.initialize({
ibGibAddr,
meta: this,
html: thisHtml,
css: [rootCss, stylesCss, thisCss],
});
return component;
} catch (error) {
console.error(`${lc} ${extractErrorMsg(error)}`);
throw error;
} finally {
if (logalot) { console.log(`${lc} complete.`); }
}
}
}
/**
* helper interface for managing tabs and their associated ibgibs
*/
interface ProjectChildInfo extends ChildInfoBase {
// tabBtnEl: HTMLElement;
// addr: IbGibAddr;
/**
* access the ibGib via this component.
*
* This component wraps an ibgib proxy that automatically stays up-to-date
* when new ibgib frames are added to the ibgib's timeline and published to
* the metaspace. (via metaspace.registerNewIbGib)
*/
// projectComponent?: ExplorerItemComponentInstance;
// agent?: AgentWitnessAny;
// active: boolean;
}
interface ProjectsExplorerElements extends ElementsBase {
headerEl: HTMLElement;
// headerTabsEl: HTMLElement;
projectListEl: HTMLElement;
projectListFilterEl: HTMLInputElement;
footerEl: HTMLElement;
// addBtnEl: HTMLButtonElement;
// ellipsisBtnEl: HTMLElement;
// ellipsisPopoverEl: HTMLElement;
}
export class ProjectsExplorerComponentInstance
extends IbGibDynamicComponentInstanceBase_ParentOfTabs
implements IbGibDynamicComponentInstance {
protected override lc: string = `[${ProjectsExplorerComponentInstance.name}]`;
private projectListFilterText: string = '';
// projectTabInfos: ProjectChildInfo[] = [];
get activeProjectTabInfo(): ProjectChildInfo | undefined {
return this.childInfos.find(x => x.active);
}
metaspace: MetaspaceService | undefined;
protected get settingsType(): SettingsType {
return SettingsType.projectsExplorer;
}
constructor() {
super();
}
override async initialize(opts: IbGibDynamicComponentInstanceInitOpts): Promise {
const lc = `${this.lc}[${this.initialize.name}]`;
try {
if (logalot) { console.log(`${lc} starting... (I: efee75e15accb11b99113dd84aa91e25)`); }
// before any initialization, we want to ensure we are bootstrapped
// await getIbGibGlobalThis_BlankGib().bootstrapPromise; // this is in the super call now
if (!this.metaspace) {
// wait for the metaspace to be initialized
this.metaspace = await getGlobalMetaspace_waitIfNeeded({ delayIntervalMs: 50 });
}
// opts.ibGibAddr = await this.metaspace.getLatestAddr({ addr: opts.ibGibAddr }) ?? opts.ibGibAddr;
await super.initialize(opts);
this.agentsInitialized = this.initAgents(); // spins off
} catch (error) {
console.error(`${lc} ${extractErrorMsg(error)}`);
throw error;
} finally {
if (logalot) { console.log(`${lc} complete.`); }
}
}
override async created(): Promise {
const lc = `${this.lc}[${this.created.name}]`;
try {
if (logalot) { console.log(`${lc} starting... (I: dbda48643e911488a96ccbc803111825)`); }
// const { meta, htmlPath, scriptPaths, cssPaths } = opts;
// does nothing atow
await this.initElements();
if (!this.elements) { throw new Error(`(UNEXPECTED) just initElements but this.elements falsy? (E: 322ce753a848fce61bef88f8bf724825)`); }
const { } = this.elements;
await this.agentsInitialized;
const globalBlankGib = getIbGibGlobalThis_BlankGib();
globalBlankGib.projectsExplorerComponent = this;
// setTimeout(() => { });
await this.renderUI();
} catch (error) {
console.error(`${lc} ${extractErrorMsg(error)}`);
throw error;
} finally {
if (logalot) { console.log(`${lc} complete.`); }
}
}
override async disconnected(): Promise {
const lc = `${this.lc}[${this.disconnected.name}]`;
try {
if (logalot) { console.log(`${lc} starting... (I: 4aa68862ac656ae6b540ed98e824e325)`); }
// no action atow
} catch (error) {
console.error(`${lc} ${extractErrorMsg(error)}`);
throw error;
} finally {
if (logalot) { console.log(`${lc} complete.`); }
}
}
protected override async renderUI(): Promise {
const lc = `${this.lc}[${this.renderUI.name}]`;
try {
if (logalot) { console.log(`${lc} starting... (I: 8371d8c58d883becd8cacbd88ae07c25)`); }
await this.renderUI_projectList();
} catch (error) {
console.error(`${lc} ${extractErrorMsg(error)}`);
throw error;
} finally {
if (logalot) { console.log(`${lc} complete.`); }
}
}
protected async renderUI_projectList(): Promise {
const lc = `${this.lc}[${this.renderUI_projectList.name}]`;
try {
if (logalot) { console.log(`${lc} starting... (I: genuuid)`); }
if (!this.shadowRoot) { throw new Error(`(UNEXPECTED) this.shadowRoot falsy? (E: genuuid)`); }
const { shadowRoot } = this;
if (!this.metaspace) { throw new Error(`(UNEXPECTED) this.metaspace falsy? (E: genuuid)`); }
const projectList = shadowRoot_getElementById(shadowRoot, 'project-list');
projectList.innerHTML = '';
const addedGibs: string[] = [];
const projectIbGibs = await getProjects({ metaspace: this.metaspace });
for (const projectIbGib of projectIbGibs) {
const projectAddr = getIbGibAddr({ ibGib: projectIbGib });
const { ib: projectIb, gib: projectGib } =
getIbAndGib({ ibGibAddr: projectAddr });
let projectName = parseProjectIb({ ib: projectIb }).safeName;
if (!projectName) {
console.warn(`${lc} projectName falsy? changed project ib format? (W: genuuid)`)
projectName = projectIb;
}
// skip adding this project if it's filtered out
let filterOut =
(!!this.projectListFilterText &&
!projectName.toLowerCase().includes(this.projectListFilterText.toLowerCase()))
|| addedGibs.includes(projectGib)
;
if (!filterOut) {
addedGibs.push(projectGib);
const liProject = document.createElement('li');
const componentSvc = await getComponentSvc();
const itemComponent = await componentSvc.getComponentInstance({
useRegExpPrefilter: true,
path: EXPLORERITEM_COMPONENT_NAME,
ibGibAddr: projectAddr,
}) as ExplorerItemComponentInstance;
await componentSvc.inject({
parentEl: liProject,
componentToInject: itemComponent,
});
projectList.appendChild(liProject);
} else {
if (logalot) { console.log(`${lc} case-insensitive filtered out ${projectName} with filter ${this.projectListFilterText} (I: genuuid)`); }
}
}
} catch (error) {
console.error(`${lc} ${extractErrorMsg(error)}`);
throw error;
} finally {
if (logalot) { console.log(`${lc} complete.`); }
}
}
public override async activateIbGib({ addr, ibGib, }: { addr?: IbGibAddr; ibGib?: IbGib_V1; }): Promise {
const lc = `${this.lc}[${this.activateIbGib.name}]`;
try {
if (logalot) { console.log(`${lc} starting... (I: 4a03e843bf98c3fa382e6a8598dec825)`); }
if (!this.metaspace) { debugger; throw new Error(`(UNEXPECTED) this.metaspace falsy? (E: 1a72d80af929b77c0fa3a1f80a973825)`); }
await super.activateIbGib({ addr, ibGib });
// removed a bunch of stuff taken from projects-component
} catch (error) {
console.error(`${lc} ${extractErrorMsg(error)}`);
throw error;
} finally {
if (logalot) { console.log(`${lc} complete.`); }
}
}
/**
* @internal
* gets the project tab info for the given addr.
*
* creates and loads the project ibgib component, which itself loads the
* ibgib internally (or throws) if not already loaded.
*
* @returns ProjectChildInfo with fully loaded component and ibgib.
*
* @see {@link ProjectChildInfo}
*/
protected async getLoadedChildInfo({
addr,
ibGib,
}: {
addr: IbGibAddr,
ibGib?: IbGib_V1,
}): Promise {
const lc = `${this.lc}[${this.getLoadedChildInfo.name}]`;
try {
if (logalot) { console.log(`${lc} starting... (I: ecaad8b7c0d5a3289edcacc8d5830825)`); }
if (!addr) { throw new Error(`(UNEXPECTED) addr falsy? (E: c00d98024143d46ac885f0d9fc098c25)`); }
const tjpGib = getGibInfo({ ibGibAddr: addr }).tjpGib ?? getIbAndGib({ ibGibAddr: addr }).gib;
const fnCreateAndLoadProjectComponent = async () => {
const componentSvc = await getComponentSvc();
const childComponent = await componentSvc.getComponentInstance({
path: EXPLORERITEM_COMPONENT_NAME,
ibGibAddr: addr,
useRegExpPrefilter: true,
}) as ExplorerItemComponentInstance | undefined;
if (!childComponent) {
debugger; // error couldn't create component instance for project?
throw new Error(`(UNEXPECTED) projectComponent falsy? couldn't create component instance for project? (E: c6b248bc481f21dc74eb6b993692d825)`);
}
// await projectComponent.loadIbGib();
// await projectComponent.initialized;
if (!childComponent.ibGib) {
debugger; // error couldn't load ibGib for project?
throw new Error(`(UNEXPECTED) ibGib falsy? couldn't load ibGib for project? (E: 2b1558512458ec70497f1d58a7b00825)`);
}
// guaranteed loaded ibGib
return childComponent;
}
/** the point of this function is to populate this */
let childInfo: ProjectChildInfo;
/**
* filtered for same timeline of addr via tjpGib
*/
const filtered = this.childInfos.filter(x => {
const tabGibInfo = getGibInfo({ ibGibAddr: x.addr });
const tabTjpGib = tabGibInfo.tjpGib ?? getIbAndGib({ ibGibAddr: x.addr }).gib;
return tabTjpGib === tjpGib;
});
if (filtered.length > 0) {
// already have an existing tab
// const filtered = this.childInfos.filter(x => x.addr === addr);
if (filtered.length !== 1) { throw new Error(`(UNEXPECTED) filtered.length !== 1? (E: ebcc484b751821619a3985bfd194b825)`); }
childInfo = filtered[0];
if (childInfo.component) {
console.log(`${lc} childInfo.component already truthy. (I: 3639381951e8478068cd7688fb954f25)`);
} else {
childInfo.component = await fnCreateAndLoadProjectComponent();
// ibGib = childInfo.component!.ibGib!;
}
} else {
// no existing tab, so create new project tab info
const projectComponent = await fnCreateAndLoadProjectComponent();
ibGib = projectComponent.ibGib!; // guaranteed in above fn
// both addr and ibGib guaranteed now
const tabBtnEl = await this.addChild({ addr, ibGib })
childInfo = {
addr,
childBtnEl: tabBtnEl,
component: projectComponent,
active: false,
};
}
return childInfo;
} catch (error) {
console.error(`${lc} ${extractErrorMsg(error)}`);
throw error;
} finally {
if (logalot) { console.log(`${lc} complete.`); }
}
}
/**
* creates a new tab element (span atow 03/2025), adds it to the
* headerTabsEl and returns the new span element.
*
* @returns the newly created tab span element
*/
protected override async addChild({
addr,
ibGib,
}: {
ibGib: IbGib_V1,
addr?: IbGibAddr,
}): Promise {
const lc = `${this.lc}[${this.addChild.name}]`;
try {
if (logalot) { console.log(`${lc} starting... (I: d5a6d80fb9e8df427f5bc293143ab825)`); }
// #region init/validate
if (!this.elements) { throw new Error(`(UNEXPECTED) this.elements falsy? (E: b937c8169f5840a5e878ac58d013e725)`); }
// const { headerTabsEl } = this.elements;
addr ??= getIbGibAddr({ ibGib });
if (addr !== getIbGibAddr({ ibGib })) {
throw new Error(`(UNEXPECTED) addr !== getIbGibAddr({ibGib})? (E: a349d8d20ca86cac682c87f8898be125)`);
}
const tjpAddr = getTjpAddr({ ibGib, defaultIfNone: 'incomingAddr' });
const { gib: tjpGib } = getIbAndGib({ ibGibAddr: tjpAddr });
// #endregion init/validate
// create the tab button element
const span = document.createElement('span');
span.id = `projects-tab-button-${addr}`;
span.classList.add('panel-tab-button');
// if (activate) { span.classList.add('active'); }
this.updateTabTitleAndText({ span, ibGib });
let {
punctiliarColor,
punctiliarColorTranslucent,
tjpColor,
tjpColorContrast,
tjpColorTranslucent,
errorMsg
} = getDeterministicColorInfo({ ibGib, translucentAlpha: 70 });
if (!errorMsg) {
span.style.borderColor = tjpColor ?? punctiliarColor;
span.style.backgroundColor = tjpColorTranslucent ?? punctiliarColorTranslucent;
span.style.color = tjpColorContrast ?? getColorStrings(90, tjpGib).at(2) ?? 'red'
// this.style.setProperty('--ibgib-color', punctiliarColor);
// this.style.setProperty('--ibgib-color-translucent', punctiliarColor);
// this.style.setProperty('--tjp-color', tjpColor ?? punctiliarColor);
// this.style.setProperty('--tjp-color-translucent', tjpColorTranslucent ?? punctiliarColorTranslucent);
} else {
// don't set anything
console.error(`${lc} ${errorMsg} (E: b855d82bb8af0a2468aef54d983b6825)`);
}
// headerTabsEl.appendChild(span);
span.addEventListener('click', async (event) => {
// if (logalot) { console.log(`${lc} activating...`); }
// await this.activateProject({ projectAddr: addr });
await this.activateIbGib({ addr });
});
if (!this.metaspace) { throw new Error(`(UNEXPECTED) this.metaspace falsy? (E: 692f4e402551f41beba2bca88ad6c125)`); }
if (!this.metaspace.latestObs) { throw new Error(`(UNEXPECTED) this.metaspace.latestObs falsy? (E: a1423ef1c978672ad9a72ae9188ac825)`); }
await this.metaspace.latestObs?.subscribe(fnObs({
next: async (updateInfo: IbGibTimelineUpdateInfo) => {
if (updateInfo.tjpAddr !== tjpAddr) { return; /* <<<< returns early */ }
if (updateInfo.latestIbGib) {
this.updateTabTitleAndText({ span, ibGib: updateInfo.latestIbGib })
} else {
console.error(`{lc}[next] updateInfo.latestIbGib falsy? (E: 3df9efd911e8d84ddd9f3fafa45a8825)`);
}
},
complete: async () => {
console.warn(`${lc}[complete] completed executed? (W: 8bf2c8723158050e5861bf885fbe4925)`);
},
error: async (error) => {
debugger; // error in metaspace.latestObs dispatch?
console.error(`${lc}[error] ${extractErrorMsg(error)}`);
},
}));
return span;
} catch (error) {
console.error(`${lc} ${extractErrorMsg(error)}`);
throw error;
} finally {
if (logalot) { console.log(`${lc} complete.`); }
}
}
protected removeTabBtn({ tabInfo, }: { tabInfo: ProjectChildInfo; }): Promise {
throw new Error("Method not implemented.");
}
private updateTabTitleAndText({
span,
ibGib,
}: {
span: HTMLElement,
ibGib: IbGib_V1,
}): void {
const lc = `[${this.updateTabTitleAndText.name}]`;
try {
if (logalot) { console.log(`${lc} starting... (I: bf99b7f4991876f96986d1814b6ad825)`); }
if (!ibGib.data) { throw new Error(`(UNEXPECTED) ibGib.data falsy? (E: 4b4a1aef1f362a411811d733b18f6825)`); }
if (!ibGib.data.name) { console.warn(`${lc} ibGib.data.name falsy? (W: 07c1e8963bad9e12cda272a237c42d25)`); }
const title = ibGib.data.name ?? 'untitled'
const desc = ibGib.data.description ?? '';
span.title = desc ? `${title}\n${desc}` : title;
const MAX_TAB_TEXT_LENGTH = 12;
span.textContent = getSaferSubstring({ text: title, length: MAX_TAB_TEXT_LENGTH, });
if (title.length > MAX_TAB_TEXT_LENGTH) {
span.textContent += '…';
}
} catch (error) {
console.error(`${lc} ${extractErrorMsg(error)}`);
throw error;
} finally {
if (logalot) { console.log(`${lc} complete.`); }
}
}
async initElements(): Promise {
const lc = `${this.lc}[${this.initElements.name}]`;
try {
if (logalot) { console.log(`${lc} starting... (I: 757cf5efc3e99ffca3dbad4ec11d4825)`); }
if (!this.shadowRoot) { throw new Error(`(UNEXPECTED) this.shadowRoot falsy? (E: 292b82f80698b67e9ec9d44dd2f13825)`); }
const { shadowRoot } = this;
const headerEl = shadowRoot_getElementById(shadowRoot, 'projects-explorer-header');
// headerTabsEl
// const headerTabsEl = shadowRoot_getElementById(shadowRoot, 'projects-explorer-header-tabs');
const contentEl = shadowRoot_getElementById(shadowRoot, 'projects-explorer-content');
const projectListFilterEl = shadowRoot_getElementById(shadowRoot, 'project-list-filter');
const fnApplyFilter = async (text: string) => {
this.projectListFilterText = text;
await this.renderUI_projectList();
}
const fnApplyFilter_debounced = debounce(fnApplyFilter, 1_000);
// Add an event listener to the textarea
projectListFilterEl.addEventListener('input', async (event) => {
const target = event.target as HTMLInputElement;
fnApplyFilter_debounced(target.value);
});
const projectListEl = shadowRoot_getElementById(shadowRoot, 'project-list');
const pContent = document.createElement('p');
pContent.textContent = '[loading...]';
pContent.style.textAlign = 'center';
pContent.style.fontStyle = 'italic';
projectListEl.appendChild(pContent);
const footerEl = shadowRoot_getElementById(shadowRoot, 'projects-explorer-footer');
footerEl.style.display = 'none';
// ellipsisBtnEl
// const ellipsisBtnEl = shadowRoot_getElementById(shadowRoot, 'projects-explorer-header-ellipsis-btn');
// // ellipsisPopoverEl - when user clicks ellipsis
// const ellipsisPopoverEl = shadowRoot_getElementById(shadowRoot, 'ellipsis-popover');
// const ellipsisPopoverOptions = ellipsisPopoverEl.querySelectorAll('.ellipsis-popover-option');
// ellipsisBtnEl.addEventListener('click', async (event) => {
// ellipsisPopoverEl.style.position = 'absolute';
// ellipsisPopoverEl.style.top = `${ellipsisBtnEl.offsetTop + ellipsisBtnEl.clientHeight}px`;
// ellipsisPopoverEl.style.left = `${ellipsisBtnEl.offsetLeft}px`;
// });
// // Event listeners for popover options
// ellipsisPopoverOptions.forEach(option => {
// option.addEventListener('click', async (event: Event) => {
// try {
// const target = event.target as HTMLElement;
// const optionType = target.getAttribute('data-option');
// if (optionType) {
// await this.handleEllipsisPopoverSelected(optionType);
// }
// } catch (error) {
// const emsg = `${lc}[ellipsisPopoverOption] ${extractErrorMsg(error)} (E: f0d1c840dfc809d39f2b8cd83ddb3225)`;
// console.error(emsg);
// alertUser({ msg: emsg, title: 'export failed...' }); // spins off
// } finally {
// // Hide popover after selection
// ellipsisPopoverEl.hidePopover();
// }
// });
// });
this.elements = {
headerEl,
// headerTabsEl,
// addBtnEl,
// ellipsisBtnEl,
// ellipsisPopoverEl,
contentEl,
projectListEl,
projectListFilterEl,
footerEl,
}
} catch (error) {
console.error(`${lc} ${extractErrorMsg(error)}`);
throw error;
} finally {
if (logalot) { console.log(`${lc} complete.`); }
}
}
/**
* exports the current project. If more than one is open, exports the currently active project.
*
* Should this also include exporting the project's agent? As a flag
*/
private async exportProject({ compress }: { compress: boolean }): Promise {
const lc = `${this.lc}[${this.exportProject.name}]`;
try {
if (logalot) { console.log(`${lc} starting... (I: 7b8ac4cbe6382312b8b3416318fc0425)`); }
// if (!this.ibGib) { throw new Error(`(UNEXPECTED) this.ibGib falsy? (E: ec92b82f7708e9bbe89ff8e8ddfca825)`); }
if (!this.activeProjectTabInfo) { throw new Error(`(UNEXPECTED) this.activeProjectTabInfo falsy? (E: ebee7ed1a098abcb38cf0258270e7825)`); }
if (!this.activeProjectTabInfo.component) { throw new Error(`(UNEXPECTED) this.activeProjectTabInfo.component falsy? (E: 57a2d854006843f9781bef68b3e49d25)`); }
if (!this.activeProjectTabInfo.component.ibGib) { throw new Error(`(UNEXPECTED) this.activeProjectTabInfo.component.ibGib falsy? (E: 71b8d460d4c8e4b0fd62894f4c3a0825)`); }
await this.exportIbGib({
ibGib: this.activeProjectTabInfo.component.ibGib,
compress,
});
} catch (error) {
console.error(`${lc} ${extractErrorMsg(error)}`);
throw error;
} finally {
if (logalot) { console.log(`${lc} complete.`); }
}
}
private async importProject(): Promise {
const lc = `[${this.importProject.name}]`;
try {
if (logalot) { console.log(`${lc} starting... (I: 232821cbc758065f8d8be84a4728f225)`); }
throw new Error(`not implemented (yet!!) ...delayed because of thinking about import as basically being the same thing as a merge, which is non-trivial. work is mostly located in ibgib-dynamic-component-bases.mts importibGib, but need to pull out into a helper. that should actually just call the helper, passing in the components this.ibGib (can be overridden in descending concrete component classes, in case this.ibGib isn't what we want) (E: 6ae10a989a46095ad867a5a83368f325)`);
} catch (error) {
console.error(`${lc} ${extractErrorMsg(error)}`);
throw error;
} finally {
if (logalot) { console.log(`${lc} complete.`); }
}
}
/**
* Handler for when an option is selected from the add popover.
*/
private async handleEllipsisPopoverSelected(optionType: string): Promise {
const lc = `${this.lc}[${this.handleEllipsisPopoverSelected.name}]`;
try {
if (logalot) { console.log(`${lc} starting... optionType: ${optionType} (I: 4ee4d8f38618d34151b4d28853bdaa25)`); }
switch (optionType) {
case 'copy-active-project-address':
if (this.activeProjectTabInfo) {
copyToClipboard({ data: { text: this.activeProjectTabInfo.addr }, });
highlightElement({ el: this.activeProjectTabInfo.childBtnEl, magicHighlightTimingMs: 1_000 }); // spin off so options disappear
} else {
await alertUser({ title: 'No Project?', msg: `There was no project tab found? This seems like a bug so you should report it please. (E: 3c82e358dd89822a9f7b4bc89b372925)` });
}
break;
case 'export-project':
await this.exportProject({ compress: false });
break;
case 'export-project-gzip':
await this.exportProject({ compress: true });
break;
case 'import-project':
await this.importProject();
break;
default:
throw new Error(`(UNEXPECTED) invalid optionType (${optionType})? (E: 4c175e5f7ad48f9f9320c51b157c2825)`);
}
} catch (error) {
console.error(`${lc} ${extractErrorMsg(error)}`);
throw error;
} finally {
if (logalot) { console.log(`${lc} complete.`); }
}
}
protected override async initAgents(): Promise {
const lc = `${this.lc}[${this.initAgents.name}]`;
try {
if (logalot) { console.log(`${lc} starting... (I: ef8d186a78582cd22f17d7a8eb887a25)`); }
this.metaspace = await getGlobalMetaspace_waitIfNeeded();
let agent: AgentWitnessAny | undefined = undefined;
let agents = await getAgents({
metaspace: this.metaspace,
type: AGENT_SPECIAL_IBGIB_TYPE_PROJECTSAGENT,
spaceId: undefined, // explicitly use default local space just to show this option bc it's early in life
});
if (agents.length > 0) {
agent = agents.at(0)!;
} else {
const agentsSvc = getAgentsSvc();
agent = await agentsSvc.createNewAgent({
metaspace: this.metaspace,
superSpace: undefined, // uses default local user space as the super space
name: `ProjectsAgent-${this.instanceId}`,
api: 'gemini',
model: GEMINI_DEFAULT_MODEL_STR,
availableFunctions: [
...AGENT_AVAILABLE_FUNCTIONS_PROJECTSAGENT,
],
initialSystemText: AGENT_INITIAL_SYSTEM_TEXT_PROJECTSAGENT,
initialChatText: AGENT_INITIAL_CHAT_TEXT_PROJECTSAGENT,
fnGetAPIKey: getDefaultFnGetAPIKey(),
type: AGENT_SPECIAL_IBGIB_TYPE_PROJECTSAGENT,
addToAgentsTag: true,
});
}
this.agents = [agent];
if (!this.agent) { throw new Error(`(UNEXPECTED) agent falsy after createNewAgent? (E: 8e7ef1336958e674934288ac1078cb25)`); }
} catch (error) {
console.error(`${lc} ${extractErrorMsg(error)}`);
throw error;
} finally {
if (logalot) { console.log(`${lc} complete.`); }
}
}
}