diff --git a/e2e-tests/fixtures/Plan.ts b/e2e-tests/fixtures/Plan.ts
index 084d3b011c..da69b548ac 100644
--- a/e2e-tests/fixtures/Plan.ts
+++ b/e2e-tests/fixtures/Plan.ts
@@ -249,7 +249,7 @@ export class Plan {
}
async fillActivityPresetName(presetName: string) {
- await this.panelActivityForm.getByRole('button', { name: 'Set Preset' }).click();
+ await this.panelActivityForm.getByRole('combobox', { name: 'None' }).click();
await this.panelActivityForm.locator('.dropdown-header').waitFor({ state: 'attached' });
await this.panelActivityForm.getByPlaceholder('Enter preset name').click();
await this.panelActivityForm.getByPlaceholder('Enter preset name').fill(presetName);
@@ -271,7 +271,7 @@ export class Plan {
}
async fillSimulationTemplateName(templateName: string) {
- await this.panelSimulation.getByRole('button', { name: 'Set Template' }).click();
+ await this.panelSimulation.locator('div[name="Set Template"]').click();
await this.panelSimulation.locator('.dropdown-header').waitFor({ state: 'attached' });
await this.panelSimulation.getByPlaceholder('Enter template name').click();
await this.panelSimulation.getByPlaceholder('Enter template name').fill(templateName);
@@ -374,22 +374,23 @@ export class Plan {
}
async selectActivityAnchorByIndex(index: number) {
- await this.panelActivityForm.getByRole('button', { name: 'Set Anchor' }).click();
+ const anchorCollapse = this.panelActivityForm.getByRole('group', { name: 'Anchor-collapse' });
+ await anchorCollapse.getByRole('combobox').click();
- await this.panelActivityForm.getByRole('menuitem').nth(index).waitFor({ state: 'attached' });
- const anchorMenuName = await this.panelActivityForm.getByRole('menuitem').nth(index)?.innerText();
- await this.panelActivityForm.getByRole('menuitem').nth(index).click();
- await this.panelActivityForm.getByRole('menuitem').nth(index).waitFor({ state: 'detached' });
+ await anchorCollapse.getByRole('menuitem').nth(index).waitFor({ state: 'attached' });
+ const anchorMenuName = await anchorCollapse.getByRole('menuitem').nth(index)?.innerText();
+ await anchorCollapse.getByRole('menuitem').nth(index).click();
+ await anchorCollapse.getByRole('menuitem').nth(index).waitFor({ state: 'detached' });
await this.page.waitForFunction(
anchorMenuName => document.querySelector('.anchor-form .selected-display-value')?.innerHTML === anchorMenuName,
anchorMenuName,
);
- await expect(this.panelActivityForm.getByRole('textbox', { name: anchorMenuName })).toBeVisible();
+ await expect(anchorCollapse.getByRole('combobox', { name: anchorMenuName })).toBeVisible();
}
async selectActivityPresetByName(presetName: string) {
- await this.panelActivityForm.getByRole('button', { name: 'Set Preset' }).click();
+ await this.panelActivityForm.locator('div[name="Set Preset"]').click();
await this.panelActivityForm.getByRole('menuitem', { name: presetName }).waitFor({ state: 'attached' });
await this.panelActivityForm.getByRole('menuitem', { name: presetName }).click();
@@ -415,11 +416,11 @@ export class Plan {
document.querySelector('.activity-preset-input-container .selected-display-value')?.innerHTML === presetName,
presetName,
);
- await expect(this.panelActivityForm.getByRole('textbox', { name: presetName })).toBeVisible();
+ await expect(this.panelActivityForm.getByRole('combobox', { name: presetName })).toBeVisible();
}
async selectSimulationTemplateByName(templateName: string) {
- await this.panelSimulation.getByRole('button', { name: 'Set Template' }).click();
+ await this.panelSimulation.locator('div[name="Set Template"]').click();
await this.panelSimulation.getByRole('menuitem', { name: templateName }).waitFor({ state: 'attached' });
await this.panelSimulation.getByRole('menuitem', { name: templateName }).click();
@@ -446,7 +447,7 @@ export class Plan {
templateName,
templateName,
);
- await expect(this.panelSimulation.getByRole('textbox', { name: templateName })).toBeVisible();
+ await expect(this.panelSimulation.getByRole('combobox', { name: templateName })).toBeVisible();
}
async showConstraintsLayout() {
diff --git a/e2e-tests/tests/plan-activities.test.ts b/e2e-tests/tests/plan-activities.test.ts
index 4ccc0d97dc..0822ee5487 100644
--- a/e2e-tests/tests/plan-activities.test.ts
+++ b/e2e-tests/tests/plan-activities.test.ts
@@ -69,7 +69,7 @@ test.describe.serial('Plan Activities', () => {
() => document.querySelector('.anchor-form .selected-display-value')?.innerHTML === 'To Plan',
);
- await expect(plan.panelActivityForm.getByRole('textbox', { name: 'To Plan' })).toBeVisible();
+ await expect(plan.panelActivityForm.getByRole('combobox', { name: 'To Plan' })).toBeVisible();
});
test('Deleting multiple activity directives but only 1 has a remaining anchored dependent should prompt for just the one with a remaining dependent', async () => {
diff --git a/e2e-tests/tests/plan-activity-presets.test.ts b/e2e-tests/tests/plan-activity-presets.test.ts
index 086966b05c..37f657938c 100644
--- a/e2e-tests/tests/plan-activity-presets.test.ts
+++ b/e2e-tests/tests/plan-activity-presets.test.ts
@@ -57,7 +57,7 @@ test.beforeAll(async ({ baseURL, browser }) => {
await plan.selectActivityPresetByName('None');
- await expect(plan.panelActivityForm.getByRole('textbox', { name: 'None' })).toBeVisible();
+ await expect(plan.panelActivityForm.getByRole('combobox', { name: 'None' })).toBeVisible();
});
test.afterAll(async () => {
@@ -72,18 +72,18 @@ test.afterAll(async () => {
test.describe.serial('Plan Activity Presets', () => {
test(`Setting a preset to a directive should update the parameter values`, async () => {
await plan.selectActivityPresetByName('Preset 1');
- await expect(page.getByRole('textbox', { name: 'Preset 1' })).toBeVisible();
+ await expect(page.getByRole('combobox', { name: 'Preset 1' })).toBeVisible();
});
test(`Removing an activity preset from a directive should reflect that it is no longer present`, async () => {
await plan.selectActivityPresetByName('None');
- await expect(page.getByRole('textbox', { name: 'None' })).toBeVisible();
+ await expect(page.getByRole('combobox', { name: 'None' })).toBeVisible();
});
test('Deleting an activity preset should remove it from the list of presets', async () => {
await plan.selectActivityPresetByName('Preset 1');
- await page.getByRole('button', { name: 'Set Preset' }).click();
+ await page.getByRole('combobox', { name: 'Preset 1' }).click();
await page.getByRole('button', { name: 'Delete preset' }).waitFor({ state: 'attached' });
await page.getByRole('button', { name: 'Delete preset' }).click();
@@ -96,6 +96,6 @@ test.describe.serial('Plan Activity Presets', () => {
() => document.querySelector('.activity-preset-input-container .selected-display-value')?.innerHTML === 'None',
);
- await expect(page.getByRole('textbox', { name: 'None' })).toBeVisible();
+ await expect(page.getByRole('combobox', { name: 'None' })).toBeVisible();
});
});
diff --git a/e2e-tests/tests/plan-simulation-templates.test.ts b/e2e-tests/tests/plan-simulation-templates.test.ts
index 10bd72bed0..9079dcd036 100644
--- a/e2e-tests/tests/plan-simulation-templates.test.ts
+++ b/e2e-tests/tests/plan-simulation-templates.test.ts
@@ -59,7 +59,7 @@ test.beforeAll(async ({ baseURL, browser }) => {
await plan.selectSimulationTemplateByName('None');
- expect(page.getByRole('textbox', { name: 'None' })).toBeVisible();
+ expect(page.getByRole('combobox', { name: 'None' })).toBeVisible();
});
test.afterAll(async () => {
@@ -75,19 +75,19 @@ test.describe.serial('Plan Simulation Templates', async () => {
test(`Setting a simulation template to a simulation should update the parameter values`, async () => {
await plan.selectSimulationTemplateByName('Template 1');
- expect(plan.panelSimulation.getByRole('textbox', { name: 'Template 1' })).toBeVisible();
+ expect(plan.panelSimulation.getByRole('combobox', { name: 'Template 1' })).toBeVisible();
});
test(`Removing an simulation template from a simulation should reflect that it is no longer present`, async () => {
await plan.selectSimulationTemplateByName('None');
- expect(page.getByRole('textbox', { name: 'None' })).toBeVisible();
+ expect(page.getByRole('combobox', { name: 'None' })).toBeVisible();
});
test('Deleting an simulation template should remove it from the list of templates', async () => {
await plan.selectSimulationTemplateByName('Template 1');
- await page.getByRole('button', { name: 'Set Template' }).click();
+ await page.getByRole('combobox', { name: 'Template 1' }).click();
await page.getByRole('button', { name: 'Delete Template' }).waitFor({ state: 'attached' });
await page.getByRole('button', { name: 'Delete Template' }).click();
@@ -98,6 +98,6 @@ test.describe.serial('Plan Simulation Templates', async () => {
await page.waitForFunction(() => document.querySelector('.selected-display-value')?.innerHTML === 'None');
- expect(page.getByRole('textbox', { name: 'None' })).toBeVisible();
+ expect(page.getByRole('combobox', { name: 'None' })).toBeVisible();
});
});
diff --git a/e2e-tests/tests/timeline-view-editing.test.ts b/e2e-tests/tests/timeline-view-editing.test.ts
index 5b9e39ea52..62d64a3655 100644
--- a/e2e-tests/tests/timeline-view-editing.test.ts
+++ b/e2e-tests/tests/timeline-view-editing.test.ts
@@ -50,6 +50,7 @@ test.describe.serial('Timeline View Editing', () => {
test('Add an activity to the parent plan', async () => {
await plan.showPanel(PanelNames.TIMELINE_ITEMS);
await plan.addActivity('PickBanana');
+ await plan.addActivity('PeelBanana');
});
test('Change the start time of the activity', async () => {
@@ -105,26 +106,121 @@ test.describe.serial('Timeline View Editing', () => {
// Look for back button indicating that the row editor is active
expect(page.locator('.section-back-button ').first()).toBeDefined();
- const existingLayerCount = await page.locator('.timeline-layer').count();
-
// Give the row a name
await page.locator('input[name="name"]').first().fill(rowName);
await page.locator('input[name="name"]').first().blur();
+ });
+
+ test('Add an activity layer', async () => {
+ const activityLayerEditor = page.getByLabel('Activity Layer-editor');
+ const existingLayerCount = await activityLayerEditor.locator('.timeline-layer-editor').count();
- // Add a layer
- await page.getByRole('button', { name: 'New Layer' }).click();
- const newLayerCount = await page.locator('.timeline-layer').count();
+ // Add an activity layer
+ await activityLayerEditor.getByRole('button', { name: 'New Activity Layer' }).click();
+ const newLayerCount = await activityLayerEditor.locator('.timeline-layer-editor').count();
expect(newLayerCount - existingLayerCount).toEqual(1);
- // Expect an activity layer to be created by default
- expect(await page.locator('select[name="chartType"]').last().inputValue()).toBe('activity');
+ // Expect the activity layer to include all activities
+ expect(await activityLayerEditor.locator('.timeline-layer-editor').first()).toHaveText('Activity Layer');
+ });
+
+ test('Edit an activity layer', async () => {
+ const activityLayerEditor = page.getByLabel('Activity Layer-editor');
+
+ // Open the activity filter builder
+ await activityLayerEditor
+ .locator('.timeline-layer-editor')
+ .first()
+ .getByLabel('Toggle activity filter builder modal')
+ .click();
+
+ // Expect that the modal is present
+ const modal = activityLayerEditor.getByRole('dialog');
+ expect(modal).toBeDefined();
+
+ // Expect that layer name is showing in the name input
+ expect(modal.locator('input[name="layer-name"]')).toHaveValue('Activity Layer');
+
+ // Expect that the resulting types list is not empty
+ const resultingTypesList = modal.locator('.resulting-types-list');
+ const allActivityTypesCount = await resultingTypesList.locator('.activity-type-result').count();
+ expect(allActivityTypesCount).toBeGreaterThan(0);
+
+ // Expect that manually selecting types cause the types to appear in the resulting types list
+ await modal.locator("input[name='manual-types-filter-input']").click();
+ expect(await modal.locator('.manual-types-menu').first()).toBeDefined();
+ await modal.getByRole('menuitem', { name: 'ChangeProducer' }).click();
+ await modal.getByRole('menuitem', { name: 'ControllableDurationActivity' }).click();
+ await page.keyboard.press('Escape');
+
+ expect(await resultingTypesList.getByText('ChangeProducer')).toBeDefined();
+ expect(await resultingTypesList.getByText('ControllableDurationActivity')).toBeDefined();
+
+ // Expect that dynamic types can be added
+ await modal.getByLabel('dynamic-types').getByRole('button', { name: 'Add Filter' }).click();
+ expect(await modal.getByLabel('dynamic-types').getByRole('listitem').count()).toBe(1);
+ await modal.getByLabel('dynamic-types').getByRole('listitem').locator("input[name='filter-value']").fill('banana');
+ expect(await resultingTypesList.locator('.activity-type-result').count()).toEqual(11);
+
+ // Expect that other filters can be added
+ await modal.getByLabel('other-filters').getByRole('button', { name: 'Add Filter' }).click();
+ expect(await modal.getByLabel('other-filters').getByRole('listitem').count()).toBe(1);
+ // Select parameter field
+ await modal.getByLabel('other-filters').locator("select[aria-label='field']").selectOption('Parameter');
+ // Select specific parameter
+ await modal.getByLabel('other-filters').getByText('Select Parameter').click();
+ await modal.getByLabel('other-filters').getByText('quantity (int)').click();
+ // Select operator
+ await modal.getByLabel('other-filters').locator("select[aria-label='operator']").selectOption('equals');
+ // Fill filter value input
+ await modal.getByLabel('other-filters').getByRole('listitem').locator("input[name='filter-value']").fill('10');
+ // Ensure that only one instance (PickBanana) is listed
+ expect(await modal.getByText('1 instance')).toBeDefined();
+
+ // Expect that type subfilters can be added
+ const activityResult = resultingTypesList.getByRole('listitem', { name: 'activity-type-result-PickBanana' });
+ await activityResult.getByRole('button', { name: 'Add Filter' }).click();
+ expect(await activityResult.getByRole('listitem').count()).toBe(1);
+ // Select name field
+ await activityResult.locator("select[aria-label='field']").selectOption('Name');
+ // Select operator
+ await activityResult.locator("select[aria-label='operator']").selectOption('includes');
+ // Fill filter value input
+ await activityResult.getByRole('listitem').locator("input[name='filter-value']").fill('foo');
+ // Ensure that only one instance (PickBanana) is listed
+ expect(await modal.getByText('0 instances')).toBeDefined();
+
+ // Expect that type subfilters can be removed
+ await activityResult.getByRole('button', { name: 'Remove filter' }).click();
+ expect(await modal.getByText('1 instance')).toBeDefined();
+
+ // Expect that other filters can be removed
+ await modal.getByLabel('other-filters').getByRole('button', { name: 'Remove filter' }).click();
+ expect(await modal.getByText('2 instances')).toBeDefined();
+
+ // Expect that dynamic types can be removed
+ await modal.getByLabel('dynamic-types').getByRole('button', { name: 'Remove filter' }).click();
+ expect(await resultingTypesList.locator('.activity-type-result').count()).toEqual(2);
+
+ // Expect that manual types can be cleared
+ await modal.locator("input[name='manual-types-filter-input']").click();
+ await modal.getByRole('menuitem', { name: 'ChangeProducer' }).click();
+ await page.keyboard.press('Escape');
+ await modal.getByRole('button', { name: 'Remove Types' }).click();
+ expect(await resultingTypesList.locator('.activity-type-result').count()).toEqual(allActivityTypesCount);
- // Expect the filter list to open
- await page.getByPlaceholder('Search').last().click();
- await expect(page.locator('.menu-slot > .header')).toBeDefined();
+ // Give the layer a new name
+ await modal.locator('input[name="layer-name"]').fill('Foo');
- // Add all activities
- await page.locator('button', { hasText: /Select [0-9]* activit/ }).click();
+ // Close the modal
+ await modal.getByRole('button', { name: 'close' }).click();
+
+ // Expect name to match given name
+ expect(await activityLayerEditor.locator('.timeline-layer-editor').first()).toHaveText('Foo');
+ });
+
+ test('Change activity layer settings', async () => {
+ const activityLayerEditor = await page.getByLabel('Activity Layer-editor');
// Expect to not see an activity tree group in this row
expect(await page.locator('.timeline-row-wrapper', { hasText: rowName }).locator('.activity-tree').count()).toBe(0);
@@ -141,9 +237,55 @@ test.describe.serial('Timeline View Editing', () => {
).toBe(1);
// Delete an activity layer
- await page.getByRole('button', { name: 'Layer Settings' }).last().click();
- await page.getByText('Delete Layer').click();
- const finalLayerCount = await page.locator('.timeline-layer').count();
- expect(finalLayerCount - newLayerCount).toEqual(-1);
+ await activityLayerEditor.locator('.timeline-layer-editor').first().getByRole('button', { name: 'Delete' }).click();
+ expect(await activityLayerEditor.locator('.timeline-layer-editor').count()).toBe(0);
+ });
+
+ test('Add a resource layer', async () => {
+ const resourceLayerEditor = await page.getByLabel('Resource Layer-editor');
+ const yAxisEditor = await page.getByLabel('Y Axis-editor');
+ const existingLayerCount = await resourceLayerEditor.locator('.timeline-layer-editor').count();
+ const existingYAxesCount = await yAxisEditor.locator('.timeline-y-axis').count();
+
+ // Expect no y-axis label to exist for the row in the timeline
+ expect(
+ await page.locator('.timeline-row-wrapper', { hasText: rowName }).locator('.row-header-y-axis-label').count(),
+ ).toBe(0);
+
+ // Add a resource layer
+ await resourceLayerEditor.getByRole('button', { name: 'New Resource Layer' }).click();
+ const newLayerCount = await resourceLayerEditor.locator('.timeline-layer-editor').count();
+ expect(newLayerCount - existingLayerCount).toEqual(1);
+
+ // Expect a y-axis to have been automatically created
+ const newYAxisCount = await yAxisEditor.locator('.timeline-y-axis').count();
+ expect(newYAxisCount - existingYAxesCount).toEqual(1);
+
+ // Select a resource
+ await resourceLayerEditor.getByRole('combobox').click();
+ await resourceLayerEditor.getByRole('menuitem', { name: '/peel' }).waitFor({ state: 'attached' });
+ await resourceLayerEditor.getByRole('menuitem', { name: '/peel' }).click();
+ await resourceLayerEditor.getByRole('menuitem', { name: '/peel' }).waitFor({ state: 'detached' });
+
+ // Run simulation
+ await plan.showPanel(PanelNames.SIMULATION, true);
+ await plan.runSimulation();
+
+ // Expect the resource to have a y-axis label in the timline
+ expect(
+ await page.locator('.timeline-row-wrapper', { hasText: rowName }).locator('.row-header-y-axis-label').count(),
+ ).toBe(1);
+
+ // Duplicate a resource layer
+ await resourceLayerEditor
+ .locator('.timeline-layer-editor')
+ .first()
+ .getByRole('button', { name: 'Duplicate' })
+ .click();
+ expect(await resourceLayerEditor.locator('.timeline-layer-editor').count()).toBe(2);
+
+ // Delete a resource layer
+ await resourceLayerEditor.locator('.timeline-layer-editor').first().getByRole('button', { name: 'Delete' }).click();
+ expect(await resourceLayerEditor.locator('.timeline-layer-editor').count()).toBe(1);
});
});
diff --git a/package-lock.json b/package-lock.json
index 06f8c5ece2..62be98fb51 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -56,9 +56,11 @@
"@lezer/generator": "^1.7.0",
"@lezer/highlight": "^1.2.0",
"@lezer/lr": "^1.4.0",
+ "@neodrag/svelte": "^2.0.6",
"@playwright/test": "^1.49.1",
"@poppanator/sveltekit-svg": "^4.2.1",
"@sveltejs/vite-plugin-svelte": "^3.0.0",
+ "@tanstack/svelte-virtual": "^3.11.2",
"@testing-library/svelte": "^4.0.2",
"@types/cookie": "^0.6.0",
"@types/d3-array": "^3.0.5",
@@ -1133,6 +1135,12 @@
"resolved": "https://registry.npmjs.org/@nasa-jpl/stellar/-/stellar-1.1.18.tgz",
"integrity": "sha512-e+26M01HFrGBZBQwsxxoJ8OSbRKn/zdUatwRryuPaEIm9RNJwDiejYqIoWCLn9s04hVeYr4PRe6IXa0nPR95vg=="
},
+ "node_modules/@neodrag/svelte": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/@neodrag/svelte/-/svelte-2.0.6.tgz",
+ "integrity": "sha512-jjmTjRTMJaer2IyEIoS5xbccmFmOpkeoTKpBORkMItCPjHWE19eW3kvH9SuTvZJAKnKERVNGdW3VBuDxZif9Dg==",
+ "dev": true
+ },
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -1595,6 +1603,32 @@
"vite": "^5.0.0"
}
},
+ "node_modules/@tanstack/svelte-virtual": {
+ "version": "3.11.2",
+ "resolved": "https://registry.npmjs.org/@tanstack/svelte-virtual/-/svelte-virtual-3.11.2.tgz",
+ "integrity": "sha512-o0VWDf8GlkZ8S5E2GjQq39qhZIB6U1Kej05/aTdxIQy18c022CxYgGWUydmrWJE3DV2ZA/q+Zm30oqnSRhQ4Lw==",
+ "dev": true,
+ "dependencies": {
+ "@tanstack/virtual-core": "3.11.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ },
+ "peerDependencies": {
+ "svelte": "^3.48.0 || ^4.0.0 || ^5.0.0"
+ }
+ },
+ "node_modules/@tanstack/virtual-core": {
+ "version": "3.11.2",
+ "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.11.2.tgz",
+ "integrity": "sha512-vTtpNt7mKCiZ1pwU9hfKPhpdVO2sVzFQsxoVBGtOSHxlrRRzYr8iQ2TlwbAcRYCcEiZ9ECAM8kBzH0v2+VzfKw==",
+ "dev": true,
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ }
+ },
"node_modules/@testing-library/dom": {
"version": "9.3.4",
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.4.tgz",
diff --git a/package.json b/package.json
index 32e5b016f9..e1c1666e39 100644
--- a/package.json
+++ b/package.json
@@ -86,8 +86,10 @@
"@lezer/highlight": "^1.2.0",
"@lezer/lr": "^1.4.0",
"@playwright/test": "^1.49.1",
+ "@neodrag/svelte": "^2.0.6",
"@poppanator/sveltekit-svg": "^4.2.1",
"@sveltejs/vite-plugin-svelte": "^3.0.0",
+ "@tanstack/svelte-virtual": "^3.11.2",
"@testing-library/svelte": "^4.0.2",
"@types/cookie": "^0.6.0",
"@types/d3-array": "^3.0.5",
diff --git a/src/assets/filter-with-plus.svg b/src/assets/filter-with-plus.svg
new file mode 100644
index 0000000000..5be45c60db
--- /dev/null
+++ b/src/assets/filter-with-plus.svg
@@ -0,0 +1,8 @@
+
diff --git a/src/assets/timeline-x-range-layer.svg b/src/assets/timeline-x-range-layer.svg
index c4bb6d1bc1..9213901872 100644
--- a/src/assets/timeline-x-range-layer.svg
+++ b/src/assets/timeline-x-range-layer.svg
@@ -1,5 +1,4 @@
diff --git a/src/components/ActivityList.svelte b/src/components/ActivityList.svelte
index 61cc1b0ed9..946f002ade 100644
--- a/src/components/ActivityList.svelte
+++ b/src/components/ActivityList.svelte
@@ -1,28 +1,13 @@