diff --git a/sim/public/parts/speaker.svg b/libs/microbit/parts/speaker.svg
similarity index 100%
rename from sim/public/parts/speaker.svg
rename to libs/microbit/parts/speaker.svg
diff --git a/libs/microbit/pxt.json b/libs/microbit/pxt.json
index 248288c6..d7b15adb 100644
--- a/libs/microbit/pxt.json
+++ b/libs/microbit/pxt.json
@@ -27,6 +27,8 @@
"serial.cpp",
"serial.ts",
"buffer.cpp",
+ "pxtparts.json",
+ "parts/speaker.svg",
"_locales/fr/microbit-jsdoc-strings.json"
],
"public": true,
diff --git a/libs/microbit/pxtparts.json b/libs/microbit/pxtparts.json
new file mode 100644
index 00000000..cf0f73f1
--- /dev/null
+++ b/libs/microbit/pxtparts.json
@@ -0,0 +1,78 @@
+{
+ "ledmatrix": {
+ "visual": "ledmatrix",
+ "breadboardColumnsNeeded": 8,
+ "breadboardStartRow": "h",
+ "pinAllocation": {
+ "type": "auto",
+ "gpioPinsNeeded": [5, 5]
+ },
+ "assemblyStep": 0,
+ "wires": [
+ {"start": ["breadboard", "j", 0], "end": ["GPIO", 5], "color": "purple", "assemblyStep": 1},
+ {"start": ["breadboard", "j", 1], "end": ["GPIO", 6], "color": "purple", "assemblyStep": 1},
+ {"start": ["breadboard", "j", 2], "end": ["GPIO", 7], "color": "purple", "assemblyStep": 1},
+ {"start": ["breadboard", "j", 3], "end": ["GPIO", 8], "color": "purple", "assemblyStep": 1},
+ {"start": ["breadboard", "a", 7], "end": ["GPIO", 9], "color": "purple", "assemblyStep": 1},
+ {"start": ["breadboard", "a", 0], "end": ["GPIO", 0], "color": "green", "assemblyStep": 2},
+ {"start": ["breadboard", "a", 1], "end": ["GPIO", 1], "color": "green", "assemblyStep": 2},
+ {"start": ["breadboard", "a", 2], "end": ["GPIO", 2], "color": "green", "assemblyStep": 2},
+ {"start": ["breadboard", "a", 3], "end": ["GPIO", 3], "color": "green", "assemblyStep": 2},
+ {"start": ["breadboard", "j", 4], "end": ["GPIO", 4], "color": "green", "assemblyStep": 2}
+ ]
+ },
+ "buttonpair": {
+ "visual": "buttonpair",
+ "breadboardColumnsNeeded": 6,
+ "breadboardStartRow": "f",
+ "pinAllocation": {
+ "type": "predefined",
+ "pins": ["P13", "P12"]
+ },
+ "assemblyStep": 0,
+ "wires": [
+ {"start": ["breadboard", "j", 0], "end": ["GPIO", 0], "color": "yellow", "assemblyStep": 1},
+ {"start": ["breadboard", "a", 2], "end": "ground", "color": "blue", "assemblyStep": 1},
+ {"start": ["breadboard", "j", 3], "end": ["GPIO", 1], "color": "orange", "assemblyStep": 2},
+ {"start": ["breadboard", "a", 5], "end": "ground", "color": "blue", "assemblyStep": 2}
+ ]
+ },
+ "neopixel": {
+ "visual": "neopixel",
+ "breadboardColumnsNeeded": 5,
+ "breadboardStartRow": "h",
+ "pinAllocation": {
+ "type": "factoryfunction",
+ "functionName": "neopixel.create",
+ "pinArgPositions": [0],
+ "otherArgPositions": [1]
+ },
+ "assemblyStep": 0,
+ "wires": [
+ {"start": ["breadboard", "j", 1], "end": "ground", "color": "blue", "assemblyStep": 1},
+ {"start": ["breadboard", "j", 2], "end": "threeVolt", "color": "red", "assemblyStep": 2},
+ {"start": ["breadboard", "j", 3], "end": ["GPIO", 0], "color": "green", "assemblyStep": 2}
+ ]
+ },
+ "speaker": {
+ "visual": {
+ "image": "/parts/speaker.svg",
+ "width": 500,
+ "height": 500,
+ "firstPin": [180, 135],
+ "pinDist": 70,
+ "extraColumnOffset": 1
+ },
+ "breadboardColumnsNeeded": 5,
+ "breadboardStartRow": "f",
+ "pinAllocation": {
+ "type": "auto",
+ "gpioPinsNeeded": 1
+ },
+ "assemblyStep": 0,
+ "wires": [
+ {"start": ["breadboard", "j", 1], "end": ["GPIO", 0], "color": "#ff80fa", "assemblyStep": 1},
+ {"start": ["breadboard", "j", 3], "end": "ground", "color": "blue", "assemblyStep": 1}
+ ]
+ }
+}
\ No newline at end of file
diff --git a/package.json b/package.json
index 2bd86e84..e5e2b1b0 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "pxt-microbit",
- "version": "0.3.67",
+ "version": "0.3.69",
"description": "micro:bit target for PXT",
"keywords": [
"JavaScript",
@@ -29,6 +29,6 @@
"typescript": "^1.8.7"
},
"dependencies": {
- "pxt-core": "0.3.76"
+ "pxt-core": "0.3.79"
}
}
diff --git a/sim/dalboard.ts b/sim/dalboard.ts
index 78fdf4dd..b95f354b 100644
--- a/sim/dalboard.ts
+++ b/sim/dalboard.ts
@@ -1,3 +1,5 @@
+///
+
namespace pxsim {
export class DalBoard extends BaseBoard {
id: string;
@@ -69,12 +71,14 @@ namespace pxsim {
}
initAsync(msg: SimulatorRunMessage): Promise {
+ super.initAsync(msg);
+
let options = (msg.options || {}) as RuntimeOptions;
let boardDef = CURRENT_BOARD; //TODO: read from pxt.json/pxttarget.json
let cmpsList = msg.parts;
- let cmpDefs = PART_DEFINITIONS; //TODO: read from pxt.json/pxttarget.json
+ let cmpDefs = msg.partDefinitions || {}; //TODO: read from pxt.json/pxttarget.json
let fnArgs = msg.fnArgs;
let viewHost = new visuals.BoardHost({
diff --git a/sim/definitions.ts b/sim/definitions.ts
index 66f013fd..154f4001 100644
--- a/sim/definitions.ts
+++ b/sim/definitions.ts
@@ -1,90 +1,10 @@
///
+///
///
///
///
namespace pxsim {
- export interface PinBlockDefinition {
- x: number,
- y: number,
- labelPosition: "above" | "below";
- labels: string[]
- }
- export interface BoardImageDefinition {
- image: string,
- outlineImage?: string,
- width: number,
- height: number,
- pinDist: number,
- pinBlocks: PinBlockDefinition[],
- };
- export interface BoardDefinition {
- visual: BoardImageDefinition | string,
- gpioPinBlocks?: string[][],
- gpioPinMap: {[pin: string]: string},
- groundPins: string[],
- threeVoltPins: string[],
- attachPowerOnRight?: boolean,
- onboardComponents?: string[]
- useCrocClips?: boolean,
- marginWhenBreadboarding?: [number, number, number, number],
- spiPins?: {
- MOSI: string,
- MISO: string,
- SCK: string,
- },
- i2cPins?: {
- SDA: string,
- SCL: string,
- },
- analogInPins?: string[], //TODO: implement allocation
- }
- export interface FactoryFunctionPinAlloc {
- type: "factoryfunction",
- functionName: string,
- pinArgPositions: number[],
- otherArgPositions?: number[],
- }
- export interface PredefinedPinAlloc {
- type: "predefined",
- pins: string[],
- }
- export interface AutoPinAlloc {
- type: "auto",
- gpioPinsNeeded: number | number[],
- }
- export interface PartVisualDefinition {
- image: string,
- width: number,
- height: number,
- pinDist: number,
- extraColumnOffset?: number,
- firstPin: [number, number],
- }
- export interface PartDefinition {
- visual: string | PartVisualDefinition,
- breadboardColumnsNeeded: number,
- breadboardStartRow: string,
- wires: WireDefinition[],
- assemblyStep: number,
- pinAllocation: FactoryFunctionPinAlloc | PredefinedPinAlloc | AutoPinAlloc,
- }
- export interface WireDefinition {
- start: WireLocationDefinition,
- end: WireLocationDefinition,
- color: string,
- assemblyStep: number
- };
- export type SPIPin = "MOSI" | "MISO" | "SCK";
- export type I2CPin = "SDA" | "SCL";
- export type WireLocationDefinition = (
- ["breadboard", string, number]
- | ["GPIO", number]
- | SPIPin
- | I2CPin
- | "ground"
- | "threeVolt");
-
export const MICROBIT_DEF: BoardDefinition = {
visual: "microbit",
gpioPinBlocks: [
@@ -133,110 +53,6 @@ namespace pxsim {
marginWhenBreadboarding: [0, 0, 80, 0],
}
- export const PART_DEFINITIONS: Map = {
- "ledmatrix": {
- visual: "ledmatrix",
- breadboardColumnsNeeded: 8,
- breadboardStartRow: "h",
- pinAllocation: {
- type: "auto",
- gpioPinsNeeded: [5, 5],
- },
- assemblyStep: 0,
- wires: [
- {start: ["breadboard", `j`, 0], end: ["GPIO", 5], color: "purple", assemblyStep: 1},
- {start: ["breadboard", `j`, 1], end: ["GPIO", 6], color: "purple", assemblyStep: 1},
- {start: ["breadboard", `j`, 2], end: ["GPIO", 7], color: "purple", assemblyStep: 1},
- {start: ["breadboard", `j`, 3], end: ["GPIO", 8], color: "purple", assemblyStep: 1},
- {start: ["breadboard", `a`, 7], end: ["GPIO", 9], color: "purple", assemblyStep: 1},
- {start: ["breadboard", `a`, 0], end: ["GPIO", 0], color: "green", assemblyStep: 2},
- {start: ["breadboard", `a`, 1], end: ["GPIO", 1], color: "green", assemblyStep: 2},
- {start: ["breadboard", `a`, 2], end: ["GPIO", 2], color: "green", assemblyStep: 2},
- {start: ["breadboard", `a`, 3], end: ["GPIO", 3], color: "green", assemblyStep: 2},
- {start: ["breadboard", `j`, 4], end: ["GPIO", 4], color: "green", assemblyStep: 2},
- ]
- },
- "buttonpair": {
- visual: "buttonpair",
- breadboardColumnsNeeded: 6,
- breadboardStartRow: "f",
- pinAllocation: {
- type: "predefined",
- pins: ["P13", "P12"],
- },
- assemblyStep: 0,
- wires: [
- {start: ["breadboard", "j", 0], end: ["GPIO", 0], color: "yellow", assemblyStep: 1},
- {start: ["breadboard", "a", 2], end: "ground", color: "blue", assemblyStep: 1},
- {start: ["breadboard", "j", 3], end: ["GPIO", 1], color: "orange", assemblyStep: 2},
- {start: ["breadboard", "a", 5], end: "ground", color: "blue", assemblyStep: 2},
- ],
- },
- "neopixel": {
- visual: "neopixel",
- breadboardColumnsNeeded: 5,
- breadboardStartRow: "h",
- pinAllocation: {
- type: "factoryfunction",
- functionName: "neopixel.create",
- pinArgPositions: [0],
- otherArgPositions: [1],
- },
- assemblyStep: 0,
- wires: [
- {start: ["breadboard", "j", 1], end: "ground", color: "blue", assemblyStep: 1},
- {start: ["breadboard", "j", 2], end: "threeVolt", color: "red", assemblyStep: 2},
- {start: ["breadboard", "j", 3], end: ["GPIO", 0], color: "green", assemblyStep: 2},
- ],
- },
- "speaker": {
- visual: {
- image: "/parts/speaker.svg",
- width: 500,
- height: 500,
- firstPin: [180, 135],
- pinDist: 70,
- extraColumnOffset: 1,
- },
- breadboardColumnsNeeded: 5,
- breadboardStartRow: "f",
- pinAllocation: {
- type: "auto",
- gpioPinsNeeded: 1,
- },
- assemblyStep: 0,
- wires: [
- {start: ["breadboard", "j", 1], end: ["GPIO", 0], color: "#ff80fa", assemblyStep: 1},
- {start: ["breadboard", "j", 3], end: "ground", color: "blue", assemblyStep: 1},
- ],
- },
- "max6675": {
- visual: {
- image: "/static/hardware/max6675.svg",
- width: 58,
- height: 64,
- firstPin: [11, 5],
- pinDist: 7.2,
- extraColumnOffset: 2,
- },
- breadboardColumnsNeeded: 10,
- breadboardStartRow: "h",
- pinAllocation: {
- type: "factoryfunction",
- functionName: "max6675.temperature",
- pinArgPositions: [0]
- },
- assemblyStep: 0,
- wires: [
- {start: ["breadboard", "j", 2], end: "SCK", color: "green", assemblyStep: 1},
- {start: ["breadboard", "j", 3], end: ["GPIO", 0], color: "blue", assemblyStep: 1},
- {start: ["breadboard", "j", 4], end: "MISO", color: "orange", assemblyStep: 1},
- {start: ["breadboard", "j", 5], end: "ground", color: "blue", assemblyStep: 0},
- {start: ["breadboard", "j", 6], end: "threeVolt", color: "red", assemblyStep: 1},
- ],
- },
- }
-
export const builtinComponentSimVisual: Map<() => visuals.IBoardComponent> = {
"buttonpair": () => new visuals.ButtonPairView(),
"ledmatrix": () => new visuals.LedMatrixView(),
diff --git a/sim/instructions/instructions.ts b/sim/instructions/instructions.ts
index 25cb44ec..431e8477 100644
--- a/sim/instructions/instructions.ts
+++ b/sim/instructions/instructions.ts
@@ -20,34 +20,37 @@ namespace pxsim.instructions {
const LBL_LEFT_PAD = 5;
const REQ_WIRE_HEIGHT = 45;
const REQ_CMP_HEIGHT = 55;
- const REQ_CMP_SCALE = 0.5;
+ const REQ_CMP_SCALE = 0.5 * 4;
type Orientation = "landscape" | "portrait";
const ORIENTATION: Orientation = "portrait";
const PPI = 96.0;
+ const PAGE_SCALAR = 0.95;
const [FULL_PAGE_WIDTH, FULL_PAGE_HEIGHT]
- = (ORIENTATION == "portrait" ? [PPI * 8.5, PPI * 11.0] : [PPI * 11.0, PPI * 8.5]);
+ = (ORIENTATION == "portrait" ? [PPI * 8.5 * PAGE_SCALAR, PPI * 11.0 * PAGE_SCALAR] : [PPI * 11.0 * PAGE_SCALAR, PPI * 8.5 * PAGE_SCALAR]);
const PAGE_MARGIN = PPI * 0.45;
const PAGE_WIDTH = FULL_PAGE_WIDTH - PAGE_MARGIN * 2;
const PAGE_HEIGHT = FULL_PAGE_HEIGHT - PAGE_MARGIN * 2;
const BORDER_COLOR = "gray";
- const BORDER_RADIUS = 5;
- const BORDER_WIDTH = 2;
- const [PANEL_ROWS, PANEL_COLS] = [2, 2];
+ const BORDER_RADIUS = 5 * 4;
+ const BORDER_WIDTH = 2 * 2;
+ const [PANEL_ROWS, PANEL_COLS] = [1, 1];
const PANEL_MARGIN = 20;
- const PANEL_PADDING = 8;
+ const PANEL_PADDING = 8 * 3;
const PANEL_WIDTH = PAGE_WIDTH / PANEL_COLS - (PANEL_MARGIN + PANEL_PADDING + BORDER_WIDTH) * PANEL_COLS;
const PANEL_HEIGHT = PAGE_HEIGHT / PANEL_ROWS - (PANEL_MARGIN + PANEL_PADDING + BORDER_WIDTH) * PANEL_ROWS;
- const BOARD_WIDTH = 240;
+ const BOARD_WIDTH = 465;
const BOARD_LEFT = (PANEL_WIDTH - BOARD_WIDTH) / 2.0 + PANEL_PADDING;
const BOARD_BOT = PANEL_PADDING;
- const NUM_BOX_SIZE = 60;
- const NUM_FONT = 40;
- const NUM_MARGIN = 5;
- const FRONT_PAGE_BOARD_WIDTH = 200;
+ const NUM_BOX_SIZE = 120;
+ const NUM_FONT = 80;
+ const NUM_MARGIN = 10;
+ const FRONT_PAGE_BOARD_WIDTH = 400;
+ const PART_SCALAR = 2.3
const PARTS_BOARD_SCALE = 0.17;
const PARTS_BB_SCALE = 0.25;
const PARTS_CMP_SCALE = 0.3;
const PARTS_WIRE_SCALE = 0.23;
+ const BACK_PAGE_BOARD_WIDTH = PANEL_WIDTH - PANEL_PADDING * 1.5;
const STYLE = `
.instr-panel {
margin: ${PANEL_MARGIN}px;
@@ -56,11 +59,12 @@ namespace pxsim.instructions {
border-color: ${BORDER_COLOR};
border-style: solid;
border-radius: ${BORDER_RADIUS}px;
- display: inline-block;
+ display: block;
width: ${PANEL_WIDTH}px;
height: ${PANEL_HEIGHT}px;
position: relative;
overflow: hidden;
+ page-break-inside: avoid;
}
.board-svg {
margin: 0 auto;
@@ -90,10 +94,11 @@ namespace pxsim.instructions {
}
.reqs-div {
margin-left: ${PANEL_PADDING + NUM_BOX_SIZE}px;
+ margin-top: 5px;
}
.partslist-wire,
.partslist-cmp {
- margin: 5px;
+ margin: 10px;
}
.partslist-wire {
display: inline-block;
@@ -269,8 +274,8 @@ namespace pxsim.instructions {
let svgAtts = {
"viewBox": `${dims.l} ${dims.t} ${dims.w} ${dims.h}`,
- "width": dims.w,
- "height": dims.h,
+ "width": dims.w * PART_SCALAR,
+ "height": dims.h * PART_SCALAR,
"preserveAspectRatio": "xMidYMid",
};
svg.hydrate(svgEl, svgAtts);
@@ -560,7 +565,6 @@ namespace pxsim.instructions {
return [panel, props];
}
function mkFinalPanel(props: BoardProps) {
- const BACK_PAGE_BOARD_WIDTH = PANEL_WIDTH - 20;
let panel = mkPanel();
addClass(panel, "back-panel");
@@ -620,6 +624,9 @@ ${tsPackage}
let parts = (getQsVal("parts") || "").split(" ");
parts.sort();
+ // parts definitions
+ let partDefinitions = JSON.parse(getQsVal("partdefs") || "{}") as pxsim.Map
+
//fn args
let fnArgs = JSON.parse((getQsVal("fnArgs") || "{}"));
@@ -637,7 +644,7 @@ ${tsPackage}
style.textContent += STYLE;
const boardDef = CURRENT_BOARD;
- const cmpDefs = PART_DEFINITIONS;
+ const cmpDefs = partDefinitions;
//props
let dummyBreadboard = new visuals.Breadboard({});
diff --git a/sim/public/siminstructions.html b/sim/public/siminstructions.html
index 723cf126..3d88ea16 100644
--- a/sim/public/siminstructions.html
+++ b/sim/public/siminstructions.html
@@ -96,9 +96,9 @@
.organization {
position: absolute;
- bottom: 1rem;
- right: 1rem;
- height: 2rem;
+ bottom: 2rem;
+ right: 2rem;
+ height: 4rem;
}
h1 {
@@ -112,23 +112,23 @@
#front-panel .board-svg {
position: absolute;
- left: 1rem;
- width: 140px;
- top: 8rem;
+ left: 2rem;
+ width: 300px;
+ top: 16rem;
}
#proj-title {
- top: 20px;
- position: absolute;
width: 100%;
+ font-size: 70px;
+ margin-top: 20px;
}
#proj-code {
- width: 140px;
- height: 200px;
+ width: 300px;
+ height: 400px;
position: absolute;
- right: 1rem;
- top: 8rem;
+ right: 2rem;
+ top: 16rem;
}
#proj-code-container {
width: 100%;
diff --git a/sim/visuals/neopixel.ts b/sim/visuals/neopixel.ts
index 2cdc73a8..9f65bfee 100644
--- a/sim/visuals/neopixel.ts
+++ b/sim/visuals/neopixel.ts
@@ -59,16 +59,48 @@ namespace pxsim.visuals {
const NP_PART_YOFF = -11;
const NP_PART_WIDTH = 87.5;
const NP_PART_HEIGHT = 190;
- const NEOPIXEL_PART_IMG = "neopixel.svg";
+ const NEOPIXEL_PART_IMG = ``;
let [x, y] = xy;
let l = x + NP_PART_XOFF;
let t = y + NP_PART_YOFF;
let w = NP_PART_WIDTH;
let h = NP_PART_HEIGHT;
let img = svg.elt("image");
- svg.hydrate(img, {class: "sim-neopixel-strip", x: l, y: t, width: w, height: h,
- href: `/parts/${NEOPIXEL_PART_IMG}`});
- return {el: img, x: l, y: t, w: w, h: h};
+ svg.hydrate(img, {
+ class: "sim-neopixel-strip", x: l, y: t, width: w, height: h,
+ href: svg.toDataUri(NEOPIXEL_PART_IMG)
+ });
+ return { el: img, x: l, y: t, w: w, h: h };
}
export class NeoPixel implements SVGAndSize {
public el: SVGCircleElement;
@@ -83,7 +115,7 @@ namespace pxsim.visuals {
let circle = svg.elt("circle");
let r = PIXEL_RADIUS;
let [cx, cy] = xy;
- svg.hydrate(circle, {cx: cx, cy: cy, r: r, class: "sim-neopixel"});
+ svg.hydrate(circle, { cx: cx, cy: cy, r: r, class: "sim-neopixel" });
this.el = circle;
this.w = r * 2;
this.h = r * 2;
@@ -121,14 +153,14 @@ namespace pxsim.visuals {
"height": `${CANVAS_HEIGHT}px`,
});
this.canvas = el;
- this.background = svg.child(el, "rect", { class: "sim-neopixel-background hidden"});
+ this.background = svg.child(el, "rect", { class: "sim-neopixel-background hidden" });
this.updateViewBox(-CANVAS_VIEW_WIDTH / 2, 0, CANVAS_VIEW_WIDTH, CANVAS_VIEW_HEIGHT);
}
private updateViewBox(x: number, y: number, w: number, h: number) {
this.viewBox = [x, y, w, h];
- svg.hydrate(this.canvas, {"viewBox": `${x} ${y} ${w} ${h}`});
- svg.hydrate(this.background, {"x": x, "y": y, "width": w, "height": h});
+ svg.hydrate(this.canvas, { "viewBox": `${x} ${y} ${w} ${h}` });
+ svg.hydrate(this.background, { "x": x, "y": y, "width": w, "height": h });
}
public update(colors: RGBW[]) {
@@ -144,7 +176,7 @@ namespace pxsim.visuals {
}
let color = colors[i];
pixel.setRgb(color);
- svg.hydrate(pixel.el, {title: `offset: ${i}`});
+ svg.hydrate(pixel.el, { title: `offset: ${i}` });
}
//show the canvas if it's hidden
@@ -164,7 +196,7 @@ namespace pxsim.visuals {
public setLoc(xy: Coord) {
let [x, y] = xy;
- svg.hydrate(this.canvas, {x: x, y: y});
+ svg.hydrate(this.canvas, { x: x, y: y });
}
};
@@ -232,7 +264,7 @@ namespace pxsim.visuals {
this.stripGroup.appendChild(part.el);
let canvas = new NeoPixelCanvas(this.pin);
this.canvas = canvas;
- let canvasG = svg.child(this.stripGroup, "g", {class: "sim-neopixel-canvas-parent"});
+ let canvasG = svg.child(this.stripGroup, "g", { class: "sim-neopixel-canvas-parent" });
canvasG.appendChild(canvas.canvas);
this.updateStripLoc();
}
@@ -245,12 +277,12 @@ namespace pxsim.visuals {
private updateStripLoc() {
let [x, y] = this.lastLocation;
this.canvas.setLoc([x + CANVAS_LEFT, y + CANVAS_TOP]);
- svg.hydrate(this.part.el, {transform: `translate(${x} ${y})`}); //TODO: update part's l,h, etc.
+ svg.hydrate(this.part.el, { transform: `translate(${x} ${y})` }); //TODO: update part's l,h, etc.
}
public updateState(): void {
let colors = this.state.getColors(this.pin, this.mode);
this.canvas.update(colors);
}
- public updateTheme (): void { }
+ public updateTheme(): void { }
}
}
\ No newline at end of file