diff --git a/src/UI/UIDesktop.js b/src/UI/UIDesktop.js
index d73f918c..cab94d9d 100644
--- a/src/UI/UIDesktop.js
+++ b/src/UI/UIDesktop.js
@@ -35,6 +35,7 @@ import new_context_menu_item from "../helpers/new_context_menu_item.js"
import refresh_item_container from "../helpers/refresh_item_container.js"
import changeLanguage from "../i18n/i18nChangeLanguage.js"
import UIWindowSettings from "./Settings/UIWindowSettings.js"
+import UIWindowTaskManager from "./UIWindowTaskManager.js"
async function UIDesktop(options){
let h = '';
@@ -1190,7 +1191,7 @@ $(document).on('click', '.user-options-menu-btn', async function(e){
}
},
//--------------------------------------------------
- // Change Password
+ // Settings
//--------------------------------------------------
{
html: i18n('settings'),
@@ -1199,6 +1200,15 @@ $(document).on('click', '.user-options-menu-btn', async function(e){
}
},
//--------------------------------------------------
+ // Task Manager
+ //--------------------------------------------------
+ {
+ html: i18n('task_manager'),
+ onClick: async function(){
+ UIWindowTaskManager();
+ }
+ },
+ //--------------------------------------------------
// Contact Us
//--------------------------------------------------
{
diff --git a/src/UI/UIWindow.js b/src/UI/UIWindow.js
index 7274e243..812959f1 100644
--- a/src/UI/UIWindow.js
+++ b/src/UI/UIWindow.js
@@ -2773,6 +2773,7 @@ window.sidebar_item_droppable = (el_window)=>{
// closes a window
$.fn.close = async function(options) {
options = options || {};
+ console.log(options);
$(this).each(async function() {
const el_iframe = $(this).find('.window-app-iframe');
const app_uses_sdk = el_iframe.length > 0 && el_iframe.attr('data-appUsesSDK') === 'true';
diff --git a/src/UI/UIWindowTaskManager.js b/src/UI/UIWindowTaskManager.js
new file mode 100644
index 00000000..b01737a9
--- /dev/null
+++ b/src/UI/UIWindowTaskManager.js
@@ -0,0 +1,263 @@
+import { END_HARD, END_SOFT } from "../definitions.js";
+import UIAlert from "./UIAlert.js";
+import UIContextMenu from "./UIContextMenu.js";
+import UIWindow from "./UIWindow.js";
+
+const UIWindowTaskManager = async function UIWindowTaskManager () {
+ const svc_process = globalThis.services.get('process');
+
+ const w = await UIWindow({
+ title: i18n('task_manager'),
+ icon: globalThis.icons['cog.svg'],
+ uid: null,
+ is_dir: false,
+ message: 'message',
+ app: 'taskmgr',
+ // body_icon: options.body_icon,
+ // backdrop: options.backdrop ?? false,
+ is_resizable: true,
+ is_droppable: false,
+ has_head: true,
+ selectable_body: true,
+ draggable_body: false,
+ allow_context_menu: true,
+ // allow_native_ctxmenu: true,
+ show_in_taskbar: true,
+ dominant: true,
+ body_content: '',
+ width: 350,
+ // parent_uuid: options.parent_uuid,
+ // ...options.window_options,
+ window_css:{
+ height: 'initial',
+ },
+ body_css: {
+ width: 'initial',
+ padding: '20px',
+ // 'background-color': `hsla(
+ // var(--primary-hue),
+ // calc(max(var(--primary-saturation) - 15%, 0%)),
+ // calc(min(100%,var(--primary-lightness) + 20%)), .91)`,
+ 'background-color': `hsla(
+ var(--primary-hue),
+ var(--primary-saturation),
+ var(--primary-lightness),
+ var(--primary-alpha))`,
+ 'backdrop-filter': 'blur(3px)',
+
+ }
+ });
+ const w_body = w.querySelector('.window-body');
+ w_body.classList.add('taskmgr');
+
+ const Indent = ({ has_trunk, has_branch }) => {
+ const el = document.createElement('div');
+ el.classList.add('taskmgr-indentcell');
+ if ( has_trunk ) {
+ // Add new child element
+ const el_indentcell_child = document.createElement('div');
+ el_indentcell_child.classList.add('taskmgr-indentcell-trunk');
+ el.appendChild(el_indentcell_child);
+ }
+ if ( has_branch ) {
+ const el_indentcell_child = document.createElement('div');
+ el_indentcell_child.classList.add('taskmgr-indentcell-branch');
+ el.appendChild(el_indentcell_child);
+ }
+
+ return {
+ appendTo (parent) {
+ parent.appendChild(el);
+ return this;
+ }
+ };
+ };
+
+ const Task = ({ placement, name }) => {
+ const {
+ indent_level, last_item,
+ parent_last_item,
+ } = placement;
+
+ const el = document.createElement('div');
+ el.classList.add('taskmgr-task');
+
+ for ( let i=0; i < indent_level; i++ ) {
+ const last_cell = i === indent_level - 1;
+ Indent({
+ has_trunk: (last_cell && ( ! last_item )) ||
+ (!last_cell && !parent_last_item[i+1]),
+ has_branch: last_cell
+ }).appendTo(el);
+ }
+
+ const el_title = document.createElement('div');
+ el_title.classList.add('taskmgr-task-title');
+ el_title.innerText = name;
+ el.appendChild(el_title);
+
+ return {
+ el () { return el; },
+ appendTo (parent) {
+ parent.appendChild(el);
+ return this;
+ }
+ };
+ }
+
+ // https://codepen.io/fomkin/pen/gOgoBVy
+ const Table = ({ headings }) => {
+ const el_table = $(`
+
+
+
+ ${headings.map(heading =>
+ `${heading} | `).join('')}
+
+
+
+
+ `)[0];
+
+ const el_tbody = el_table.querySelector('tbody');
+
+ return {
+ el () { return el_table; },
+ add (el) {
+ if ( typeof el.el === 'function' ) el = el.el();
+ el_tbody.appendChild(el);
+ return this;
+ },
+ clear () {
+ el_tbody.innerHTML = '';
+ }
+ };
+ };
+
+ const Row = () => {
+ const el_tr = document.createElement('tr');
+ return {
+ attach (parent) {
+ parent.appendChild(el_tr);
+ return this;
+ },
+ el () { return el_tr; },
+ add (el) {
+ if ( typeof el.el === 'function' ) el = el.el();
+ const el_td = document.createElement('td');
+ el_td.appendChild(el);
+ el_tr.appendChild(el_td);
+ return this;
+ }
+ };
+ };
+
+ const el_taskarea = document.createElement('div');
+ el_taskarea.classList.add('taskmgr-taskarea');
+
+ const tasktable = Table({
+ headings: [
+ i18n('taskmgr_header_name'),
+ i18n('taskmgr_header_type'),
+ i18n('taskmgr_header_status'),
+ ]
+ });
+
+ el_taskarea.appendChild(tasktable.el());
+
+ const end_process_ = async (process, force) => {
+ let confirmation;
+
+ if ( process.is_init() ) {
+ if ( ! force ) {
+ confirmation = i18n('close_all_windows_confirm');
+ } else {
+ confirmation = i18n('restart_puter_confirm');
+ }
+ } else if ( force ) {
+ confirmation = i18n('end_process_force_confirm');
+ }
+
+ if ( confirmation ) {
+ const alert_resp = await UIAlert({
+ message: confirmation,
+ buttons:[
+ {
+ label: i18n('yes'),
+ value: true,
+ type: 'primary',
+ },
+ {
+ label: i18n('no'),
+ value: false,
+ },
+ ]
+ })
+ if ( ! alert_resp ) return;
+ }
+
+ process.signal(force ? END_HARD : END_SOFT);
+ }
+
+ const iter_tasks = (items, { indent_level, parent_last_item }) => {
+ for ( let i=0 ; i < items.length; i++ ) {
+ const row = Row();
+ const item = items[i];
+ const last_item = i === items.length - 1;
+ row.add(Task({
+ placement: {
+ parent_last_item,
+ indent_level,
+ last_item,
+ },
+ name: item.name
+ }));
+ row.add($(`${i18n('process_type_' + item.type)}`)[0])
+ row.add($(`${i18n('process_status_' + item.status.i18n_key)}`)[0])
+ tasktable.add(row);
+
+ $(row.el()).on('contextmenu', () => {
+ UIContextMenu({
+ parent_element: $(el_taskarea),
+ items: [
+ {
+ html: i18n('close'),
+ onClick: () => {
+ end_process_(item);
+ }
+ },
+ {
+ html: i18n('force_quit'),
+ onClick: () => {
+ end_process_(item, true);
+ }
+ }
+ ]
+ });
+ })
+
+ const children = svc_process.get_children_of(item.uuid);
+ if ( children ) {
+ iter_tasks(children, {
+ indent_level: indent_level + 1,
+ parent_last_item:
+ [...parent_last_item, last_item],
+ });
+ }
+ }
+ };
+
+ const interval = setInterval(() => {
+ tasktable.clear();
+ const processes = [svc_process.get_init()];
+ iter_tasks(processes, { indent_level: 0, parent_last_item: [] });
+ }, 500)
+
+ w.on_close = () => {
+ clearInterval(interval);
+ }
+
+ w_body.appendChild(el_taskarea);
+}
+
+export default UIWindowTaskManager;
diff --git a/src/UI/UIWindowThemeDialog.js b/src/UI/UIWindowThemeDialog.js
index 3932d183..58646a6f 100644
--- a/src/UI/UIWindowThemeDialog.js
+++ b/src/UI/UIWindowThemeDialog.js
@@ -1,4 +1,5 @@
import UIWindow from "./UIWindow.js";
+import UIWindowColorPicker from "./UIWindowColorPicker.js";
const UIWindowThemeDialog = async function UIWindowThemeDialog (options) {
options = options ?? {};
@@ -114,6 +115,12 @@ const UIWindowThemeDialog = async function UIWindowThemeDialog (options) {
svc_theme.reset();
})
;
+ Button({ label: i18n('reset_colors') })
+ .appendTo(w_body)
+ .onPress(() => {
+ UIWindowColorPicker();
+ })
+ ;
Slider({
label: i18n('hue'),
diff --git a/src/css/style.css b/src/css/style.css
index 7096bae2..cabcd58a 100644
--- a/src/css/style.css
+++ b/src/css/style.css
@@ -3781,4 +3781,114 @@ label {
flex-direction: row;
gap: 10px;
justify-content: flex-end;
+}
+
+.taskmgr {
+ box-sizing: border-box;
+ /* could have been avoided with box-sizing: border-box */
+ height: calc(100% - 30px);
+ display: flex;
+ flex-direction: column;
+
+ --scale: 2pt;
+ --line-color: #6e6e6ebd;
+}
+
+.taskmgr * {
+ box-sizing: border-box;
+}
+
+.taskmgr table {
+ border-collapse: collapse;
+}
+
+.taskmgr-taskarea {
+ flex-grow: 1;
+ display: flex;
+ flex-direction: column;
+ background-color: rgba(255,255,255,0.8);
+ border: 2px inset rgba(127, 127, 127, 0.3);
+ overflow: auto;
+}
+
+.taskmgr-taskarea table thead {
+}
+
+.taskmgr-taskarea table th {
+ -webkit-box-shadow: 0 1px 4px -2px rgba(0,0,0,0.2);
+ box-shadow: 0 1px 4px -2px rgba(0,0,0,0.2);
+ backdrop-filter: blur(2px);
+ position: sticky;
+ z-index: 100;
+ padding: 0;
+ top: 0;
+ background-color: hsla(0, 0%, 100%, 0.8);
+ text-align: left;
+}
+
+.taskmgr-taskarea table th > span {
+ display: inline-block;
+ width: 100%;
+ /* we set borders on this span because */
+ /* borders fly away from sticky headers */
+ border-bottom: 1px solid #e0e0e0;
+
+ /* padding order: top right bottom left */
+ padding:
+ calc(10 * var(--scale))
+ calc(2.5 * var(--scale))
+ calc(5 * var(--scale))
+ calc(2.5 * var(--scale));
+}
+
+.taskmgr-taskarea table th:not(:last-of-type) > span {
+ /* we set borders on this span because */
+ /* borders fly away from sticky headers */
+ border-right: 1px solid #e0e0e0;
+}
+
+.taskmgr-taskarea table td {
+ border-bottom: 1px solid #e0e0e0;
+}
+
+.taskmgr-taskarea table td > span {
+ padding: 0 calc(2.5 * var(--scale));
+}
+
+.taskmgr-indentcell {
+ position: relative;
+ align-items: right;
+ width: calc(10 * var(--scale));
+ height: calc(10 * var(--scale));
+}
+
+.taskmgr-indentcell-trunk {
+ position: absolute;
+ top: 0;
+ left: calc(5 * var(--scale));
+ width: calc(5 * var(--scale));
+ height: calc(10 * var(--scale));
+ border-left: 2px solid var(--line-color);
+}
+
+.taskmgr-indentcell-branch {
+ position: absolute;
+ top: 0;
+ left: calc(5 * var(--scale));
+ width: calc(5 * var(--scale));
+ height: calc(5 * var(--scale));
+ border-left: 2px solid var(--line-color);
+ border-bottom: 2px solid var(--line-color);
+ border-radius: 0 0 0 calc(2.5 * var(--scale));
+}
+
+.taskmgr-task {
+ display: flex;
+ height: calc(10 * var(--scale));
+ line-height: calc(10 * var(--scale));
+}
+
+.taskmgr-task-title {
+ flex-grow: 1;
+ padding-left: calc(2.5 * var(--scale));
}
\ No newline at end of file
diff --git a/src/definitions.js b/src/definitions.js
index 872e6315..026ace3c 100644
--- a/src/definitions.js
+++ b/src/definitions.js
@@ -19,3 +19,94 @@
export class Service {
//
};
+
+export const PROCESS_INITIALIZING = { i18n_key: 'initializing' };
+export const PROCESS_RUNNING = { i18n_key: 'running' };
+
+// Something is cloning these objects, so '===' checks don't work.
+// To work around this, the `i` property is used to compare them.
+export const END_SOFT = { i: 0, end: true, i18n_key: 'end_soft' };
+export const END_HARD = { i: 1, end: true, i18n_key: 'end_hard' };
+
+export class Process {
+ constructor ({ uuid, parent, name, meta }) {
+ this.uuid = uuid;
+ this.parent = parent;
+ this.name = name;
+ this.meta = meta;
+ this.references = {};
+
+ this.status = PROCESS_INITIALIZING;
+
+ this._construct();
+ }
+ _construct () {}
+
+ chstatus (status) {
+ this.status = status;
+ }
+
+ is_init () {}
+
+ signal (sig) {
+ this._signal(sig);
+ }
+
+ get type () {
+ const _to_type_name = (name) => {
+ return name.replace(/Process$/, '').toLowerCase();
+ };
+ return this.type_ || _to_type_name(this.constructor.name) ||
+ 'invalid'
+ }
+};
+
+export class InitProcess extends Process {
+ static created_ = false;
+
+ is_init () { return true; }
+
+ _construct () {
+ this.name = 'Puter';
+
+ if (InitProcess.created_) {
+ throw new Error('InitProccess already created');
+ }
+
+ InitProcess.created_ = true;
+ }
+
+ _signal (sig) {
+ const svc_process = globalThis.services.get('process');
+ for ( const process of svc_process.processes ) {
+ if ( process === this ) continue;
+ process.signal(sig);
+ }
+
+ if ( sig.i !== END_HARD.i ) return;
+
+ // Currently this is the only way to terminate `init`.
+ window.location.reload();
+ }
+}
+
+export class PortalProcess extends Process {
+ _construct () { this.type_ = 'app' }
+ _signal (sig) {
+ if ( sig.end ) {
+ $(this.references.el_win).close({
+ bypass_iframe_messaging: sig.i === END_HARD.i
+ });
+ }
+ }
+};
+export class PseudoProcess extends Process {
+ _construct () { this.type_ = 'ui' }
+ _signal (sig) {
+ if ( sig.end ) {
+ $(this.references.el_win).close({
+ bypass_iframe_messaging: sig.i === END_HARD.i
+ });
+ }
+ }
+};
diff --git a/src/helpers.js b/src/helpers.js
index aa23d499..f9504ca4 100644
--- a/src/helpers.js
+++ b/src/helpers.js
@@ -36,6 +36,7 @@ import update_username_in_gui from './helpers/update_username_in_gui.js';
import update_title_based_on_uploads from './helpers/update_title_based_on_uploads.js';
import content_type_to_icon from './helpers/content_type_to_icon.js';
import UIWindowDownloadDirProg from './UI/UIWindowDownloadDirProg.js';
+import { PROCESS_RUNNING, PortalProcess, PseudoProcess } from "./definitions.js";
window.is_auth = ()=>{
if(localStorage.getItem("auth_token") === null || auth_token === null)
@@ -1680,10 +1681,30 @@ window.launch_app = async (options)=>{
// add file_signature to options
file_signature = file_signature.items;
}
+
+ // -----------------------------------
+ // Create entry to track the "portal"
+ // (portals are processese in Puter's GUI)
+ // -----------------------------------
+
+ let el_win;
+ let process;
+
//------------------------------------
// Explorer
//------------------------------------
if(options.name === 'explorer'){
+ process = new PseudoProcess({
+ uuid,
+ name: 'explorer',
+ parent: options.parent_instance_id,
+ meta: {
+ launch_options: options,
+ app_info: app_info,
+ }
+ });
+ const svc_process = globalThis.services.get('process');
+ svc_process.register(process);
if(options.path === window.home_path){
title = 'Home';
icon = window.icons['folder-home.svg'];
@@ -1697,7 +1718,7 @@ window.launch_app = async (options)=>{
title = path.dirname(options.path);
// open window
- UIWindow({
+ el_win = UIWindow({
element_uuid: uuid,
icon: icon,
path: options.path ?? window.home_path,
@@ -1713,6 +1734,18 @@ window.launch_app = async (options)=>{
// All other apps
//------------------------------------
else{
+ process = new PortalProcess({
+ uuid,
+ name: app_info.name,
+ parent: options.parent_instance_id,
+ meta: {
+ launch_options: options,
+ app_info: app_info,
+ }
+ });
+ const svc_process = globalThis.services.get('process');
+ svc_process.register(process);
+
//-----------------------------------
// iframe_url
//-----------------------------------
@@ -1808,7 +1841,7 @@ window.launch_app = async (options)=>{
console.log('backgrounded??', app_info.background);
- const el_win = UIWindow({
+ el_win = UIWindow({
element_uuid: uuid,
title: title,
iframe_url: iframe_url.href,
@@ -1826,7 +1859,7 @@ window.launch_app = async (options)=>{
is_fullpage: options.is_fullpage,
...window_options,
show_in_taskbar: app_info.background ? false : window_options?.show_in_taskbar,
- });
+ });
if ( ! app_info.background ) {
$(el_win).show();
@@ -1859,6 +1892,18 @@ window.launch_app = async (options)=>{
})
}
}
+
+ (async () => {
+ const el = await el_win;
+ console.log('RESOV', el);
+ $(el).on('remove', () => {
+ const svc_process = globalThis.services.get('process');
+ svc_process.unregister(process.uuid);
+ });
+
+ process.references.el_win = el;
+ process.chstatus(PROCESS_RUNNING);
+ })();
}
window.open_item = async function(options){
diff --git a/src/i18n/translations/en.js b/src/i18n/translations/en.js
index f5b8eff4..44b82959 100644
--- a/src/i18n/translations/en.js
+++ b/src/i18n/translations/en.js
@@ -39,7 +39,9 @@ const en = {
change_password: "Change Password",
change_ui_colors: "Change UI Colors",
change_username: "Change Username",
+ close: 'Close',
close_all_windows: "Close All Windows",
+ close_all_windows_confirm: "Are you sure you want to close all windows?",
close_all_windows_and_log_out: 'Close Windows and Log Out',
change_always_open_with: "Do you want to always open this type of file with",
color: 'Color',
@@ -88,11 +90,15 @@ const en = {
empty_trash: 'Empty Trash',
empty_trash_confirmation: `Are you sure you want to permanently delete the items in Trash?`,
emptying_trash: 'Emptying Trash…',
+ end_hard: "End Hard",
+ end_process_force_confirm: "Are you sure you want to force-quit this process?",
+ end_soft: "End Soft",
enter_password_to_confirm_delete_user: "Enter your password to confirm account deletion",
feedback: "Feedback",
feedback_c2a: "Please use the form below to send us your feedback, comments, and bug reports.",
feedback_sent_confirmation: "Thank you for contacting us. If you have an email associated with your account, you will hear back from us as soon as possible.",
fit: "Fit",
+ force_quit: 'Force Quit',
forgot_pass_c2a: "Forgot password?",
from: "From",
general: "General",
@@ -154,6 +160,11 @@ const en = {
privacy: "Privacy",
proceed_to_login: 'Proceed to login',
proceed_with_account_deletion: "Proceed with Account Deletion",
+ process_status_initializing: "Initializing",
+ process_status_running: "Running",
+ process_type_app: 'App',
+ process_type_init: 'Init',
+ process_type_ui: 'UI',
properties: "Properties",
publish: "Publish",
publish_as_website: 'Publish as website',
@@ -172,6 +183,7 @@ const en = {
replace_all: 'Replace All',
resend_confirmation_code: "Re-send Confirmation Code",
reset_colors: "Reset Colors",
+ restart_puter_confirm: "Are you sure you want to restart Puter?",
restore: "Restore",
saturation: 'Saturation',
save_account: 'Save account',
@@ -203,6 +215,10 @@ const en = {
storage_usage: "Storage Usage",
storage_puter_used: 'used by Puter',
taking_longer_than_usual: 'Taking a little longer than usual. Please wait...',
+ task_manager: "Task Manager",
+ taskmgr_header_name: "Name",
+ taskmgr_header_status: "Status",
+ taskmgr_header_type: "Type",
terms: "Terms",
text_document: 'Text document',
tos_fineprint: `By clicking 'Create Free Account' you agree to Puter's {{link=terms}}Terms of Service{{/link}} and {{link=privacy}}Privacy Policy{{/link}}.`,
diff --git a/src/initgui.js b/src/initgui.js
index 9bf42784..3d16c69b 100644
--- a/src/initgui.js
+++ b/src/initgui.js
@@ -36,6 +36,9 @@ import PuterDialog from './UI/PuterDialog.js';
import determine_active_container_parent from './helpers/determine_active_container_parent.js';
import { ThemeService } from './services/ThemeService.js';
import { BroadcastService } from './services/BroadcastService.js';
+import UIWindowTaskManager from './UI/UIWindowTaskManager.js';
+import { ProcessService } from './services/ProcessService.js';
+import { PROCESS_RUNNING } from './definitions.js';
const launch_services = async function () {
const services_l_ = [];
@@ -51,10 +54,17 @@ const launch_services = async function () {
register('broadcast', new BroadcastService());
register('theme', new ThemeService());
+ register('process', new ProcessService())
for (const [_, instance] of services_l_) {
await instance._init();
}
+
+ // Set init process status
+ {
+ const svc_process = globalThis.services.get('process');
+ svc_process.get_init().chstatus(PROCESS_RUNNING);
+ }
};
window.initgui = async function(){
diff --git a/src/services/ProcessService.js b/src/services/ProcessService.js
new file mode 100644
index 00000000..8047083c
--- /dev/null
+++ b/src/services/ProcessService.js
@@ -0,0 +1,70 @@
+import { InitProcess, Service } from "../definitions.js";
+
+// The NULL UUID is also the UUID for the init process.
+const NULL_UUID = '00000000-0000-0000-0000-000000000000';
+
+export class ProcessService extends Service {
+ async _init () {
+ this.processes = [];
+ this.processes_map = new Map();
+ this.uuid_to_treelist = new Map();
+
+ const root = new InitProcess({
+ uuid: NULL_UUID,
+ });
+ this.register_(root);
+ }
+
+ get_init () {
+ return this.processes_map.get(NULL_UUID);
+ }
+
+ get_children_of (uuid) {
+ if ( ! uuid ) {
+ uuid = NULL_UUID;
+ }
+
+ return this.uuid_to_treelist.get(uuid);
+ }
+
+ register (process) {
+ this.register_(process);
+ this.attach_to_parent_(process);
+ }
+
+ register_ (process) {
+ this.processes.push(process);
+ this.processes_map.set(process.uuid, process);
+ this.uuid_to_treelist.set(process.uuid, []);
+ }
+
+ attach_to_parent_ (process) {
+ process.parent = process.parent ?? NULL_UUID;
+ const parent_list = this.uuid_to_treelist.get(process.parent);
+ parent_list.push(process);
+ }
+
+ unregister (uuid) {
+ const process = this.processes_map.get(uuid);
+ if ( ! process ) {
+ throw new Error(`Process with uuid ${uuid} not found`);
+ }
+
+ this.processes_map.delete(uuid);
+ this.processes.splice(this.processes.indexOf(process), 1);
+
+ const parent_list = this.uuid_to_treelist.get(process.parent);
+ parent_list.splice(parent_list.indexOf(process), 1);
+
+ const children = this.uuid_to_treelist.get(process.uuid);
+
+ delete this.uuid_to_treelist[process.uuid];
+ this.processes.splice(this.processes.indexOf(process), 1);
+
+ // Transfer children to init process
+ for ( const child of children ) {
+ child.parent = NULL_UUID;
+ this.attach_to_parent_(child);
+ }
+ }
+}