Compare commits

..

28 Commits

Author SHA1 Message Date
bdbe8371dd 0.4.9 2016-09-14 22:50:29 -07:00
21a36eb9ee fixing broken image paths 2016-09-14 22:43:59 -07:00
376b20b035 0.4.8 2016-09-14 22:28:23 -07:00
7ce41b52aa fixing path in docs 2016-09-14 22:28:05 -07:00
46f7831e7c 0.4.7 2016-09-14 22:21:00 -07:00
dda29a5cb6 Bump pxt-core to 0.4.11 2016-09-14 22:20:58 -07:00
6e4f4595a2 updated usb images 2016-09-14 22:18:01 -07:00
cdbe1e513b 0.4.6 2016-09-14 20:38:49 -07:00
ae15c9a656 Bump pxt-core to 0.4.9 2016-09-14 20:38:47 -07:00
d993ff3a9d 0.4.5 2016-09-14 11:33:54 -07:00
13785a2438 OS X uploader (#252)
* Source for OS X uploader

* Readme for OS X uploader

* Export image

* .gitignore for Xcode project

* Remove redundant data

* Update readme instructions

* List formatting

* Remove personal copyright notice added by Xcode

* Added release build and updated readme

* point to doc cdn
2016-09-14 11:33:11 -07:00
4dfb77fcd7 0.4.4 2016-09-14 08:16:32 -07:00
70deffb665 Bump pxt-core to 0.4.8 2016-09-14 08:16:29 -07:00
41a148de28 Merge branch 'master' of https://github.com/Microsoft/pxt-microbit 2016-09-14 08:14:41 -07:00
886e071d7f updated about to link to docs 2016-09-14 08:14:04 -07:00
8a58d664c3 adding clear image in stopanimation 2016-09-14 07:54:35 -07:00
71d1155f21 Add file forgotten in 50473255a8 2016-09-14 12:31:52 +03:00
167c1d8fce loading board definition from pxtarget.json 2016-09-13 15:32:12 -07:00
e59ae37954 moving neopixel state to pxt 2016-09-13 13:09:02 -07:00
72a621ec8b mvoing edge connector to pxt 2016-09-13 12:48:07 -07:00
d6ff930333 fix capitalized file names 2016-09-13 10:36:25 -07:00
54a7ac81ea Recommended browser documentation (#251)
* Documentation page for unsupported browsers

* Update unsupported documentation with mac info

* Blur irrelevant information in version screenshots

* Rename to Peli's suggested path

* Browser recommendation for each platform
2016-09-13 10:33:06 -07:00
e5d985dbf1 moving boardhost to pxt 2016-09-13 09:59:34 -07:00
9db91d89d6 refactor part global lists 2016-09-13 09:44:58 -07:00
1fa9bf12d5 refactoring dalboard 2016-09-12 21:29:55 -07:00
61bab257eb 0.4.3 2016-09-12 11:45:22 -07:00
2e90b351da updated other download picture 2016-09-12 11:33:16 -07:00
801bd6c7a0 udpated download pictures 2016-09-12 11:30:12 -07:00
69 changed files with 2012 additions and 524 deletions

137
clients/macuploader/.gitignore vendored Normal file
View File

@ -0,0 +1,137 @@
# Created by https://www.gitignore.io/api/osx,xcode,objective-c,vim
### OSX ###
*.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### Xcode ###
# Xcode
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
## Build generated
build/
DerivedData/
## Various settings
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata/
## Other
*.moved-aside
*.xccheckout
*.xcscmblueprint
### Objective-C ###
# Xcode
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
## Build generated
build/
DerivedData/
## Various settings
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata/
## Other
*.moved-aside
*.xcuserstate
## Obj-C/Swift specific
*.hmap
*.ipa
*.dSYM.zip
*.dSYM
# CocoaPods
#
# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
#
# Pods/
# Carthage
#
# Add this line if you want to avoid checking in source code from Carthage dependencies.
# Carthage/Checkouts
Carthage/Build
# fastlane
#
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
# screenshots whenever they are needed.
# For more information about the recommended setup visit:
# https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots
fastlane/test_output
# Code Injection
#
# After new code Injection tools there's a generated folder /iOSInjectionProject
# https://github.com/johnno1962/injectionforxcode
iOSInjectionProject/
### Objective-C Patch ###
*.xcscmblueprint
### Vim ###
# swap
[._]*.s[a-w][a-z]
[._]s[a-w][a-z]
# session
Session.vim
# temporary
.netrwhist
*~
# auto-generated tag files
tags

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

View File

@ -0,0 +1,307 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
E93040071D895D1F00D931CA /* DirectoryWatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = E93040061D895D1F00D931CA /* DirectoryWatcher.m */; };
E930400A1D89620900D931CA /* Uploader.m in Sources */ = {isa = PBXBuildFile; fileRef = E93040091D89620900D931CA /* Uploader.m */; };
E9F4FEE21D8709980071D783 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = E9F4FEE11D8709980071D783 /* AppDelegate.m */; };
E9F4FEE51D8709980071D783 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = E9F4FEE41D8709980071D783 /* main.m */; };
E9F4FEE71D8709980071D783 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E9F4FEE61D8709980071D783 /* Assets.xcassets */; };
E9F4FEEA1D8709980071D783 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = E9F4FEE81D8709980071D783 /* MainMenu.xib */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
E93040051D895D1F00D931CA /* DirectoryWatcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DirectoryWatcher.h; sourceTree = "<group>"; };
E93040061D895D1F00D931CA /* DirectoryWatcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DirectoryWatcher.m; sourceTree = "<group>"; };
E93040081D89620900D931CA /* Uploader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Uploader.h; sourceTree = "<group>"; };
E93040091D89620900D931CA /* Uploader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Uploader.m; sourceTree = "<group>"; };
E9F4FEDD1D8709980071D783 /* Microbit Uploader.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Microbit Uploader.app"; sourceTree = BUILT_PRODUCTS_DIR; };
E9F4FEE01D8709980071D783 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
E9F4FEE11D8709980071D783 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
E9F4FEE41D8709980071D783 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
E9F4FEE61D8709980071D783 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
E9F4FEE91D8709980071D783 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
E9F4FEEB1D8709980071D783 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
E9F4FEDA1D8709980071D783 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
E9F4FED41D8709980071D783 = {
isa = PBXGroup;
children = (
E9F4FEDF1D8709980071D783 /* Microbit Uploader */,
E9F4FEDE1D8709980071D783 /* Products */,
);
sourceTree = "<group>";
};
E9F4FEDE1D8709980071D783 /* Products */ = {
isa = PBXGroup;
children = (
E9F4FEDD1D8709980071D783 /* Microbit Uploader.app */,
);
name = Products;
sourceTree = "<group>";
};
E9F4FEDF1D8709980071D783 /* Microbit Uploader */ = {
isa = PBXGroup;
children = (
E9F4FEE01D8709980071D783 /* AppDelegate.h */,
E9F4FEE11D8709980071D783 /* AppDelegate.m */,
E9F4FEE61D8709980071D783 /* Assets.xcassets */,
E9F4FEE81D8709980071D783 /* MainMenu.xib */,
E9F4FEEB1D8709980071D783 /* Info.plist */,
E9F4FEE31D8709980071D783 /* Supporting Files */,
E93040051D895D1F00D931CA /* DirectoryWatcher.h */,
E93040061D895D1F00D931CA /* DirectoryWatcher.m */,
E93040081D89620900D931CA /* Uploader.h */,
E93040091D89620900D931CA /* Uploader.m */,
);
path = "Microbit Uploader";
sourceTree = "<group>";
};
E9F4FEE31D8709980071D783 /* Supporting Files */ = {
isa = PBXGroup;
children = (
E9F4FEE41D8709980071D783 /* main.m */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
E9F4FEDC1D8709980071D783 /* Microbit Uploader */ = {
isa = PBXNativeTarget;
buildConfigurationList = E9F4FEEE1D8709980071D783 /* Build configuration list for PBXNativeTarget "Microbit Uploader" */;
buildPhases = (
E9F4FED91D8709980071D783 /* Sources */,
E9F4FEDA1D8709980071D783 /* Frameworks */,
E9F4FEDB1D8709980071D783 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = "Microbit Uploader";
productName = "Microbit Uploader";
productReference = E9F4FEDD1D8709980071D783 /* Microbit Uploader.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
E9F4FED51D8709980071D783 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0800;
ORGANIZATIONNAME = thomasdenney;
TargetAttributes = {
E9F4FEDC1D8709980071D783 = {
CreatedOnToolsVersion = 7.3.1;
};
};
};
buildConfigurationList = E9F4FED81D8709980071D783 /* Build configuration list for PBXProject "Microbit Uploader" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = E9F4FED41D8709980071D783;
productRefGroup = E9F4FEDE1D8709980071D783 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
E9F4FEDC1D8709980071D783 /* Microbit Uploader */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
E9F4FEDB1D8709980071D783 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
E9F4FEE71D8709980071D783 /* Assets.xcassets in Resources */,
E9F4FEEA1D8709980071D783 /* MainMenu.xib in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
E9F4FED91D8709980071D783 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
E9F4FEE51D8709980071D783 /* main.m in Sources */,
E930400A1D89620900D931CA /* Uploader.m in Sources */,
E9F4FEE21D8709980071D783 /* AppDelegate.m in Sources */,
E93040071D895D1F00D931CA /* DirectoryWatcher.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
E9F4FEE81D8709980071D783 /* MainMenu.xib */ = {
isa = PBXVariantGroup;
children = (
E9F4FEE91D8709980071D783 /* Base */,
);
name = MainMenu.xib;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
E9F4FEEC1D8709980071D783 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "-";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.11;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
};
name = Debug;
};
E9F4FEED1D8709980071D783 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "-";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.11;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx;
};
name = Release;
};
E9F4FEEF1D8709980071D783 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
COMBINE_HIDPI_IMAGES = YES;
INFOPLIST_FILE = "Microbit Uploader/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "org.thomasdenney.Microbit-Uploader";
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
};
E9F4FEF01D8709980071D783 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
COMBINE_HIDPI_IMAGES = YES;
INFOPLIST_FILE = "Microbit Uploader/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "org.thomasdenney.Microbit-Uploader";
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
E9F4FED81D8709980071D783 /* Build configuration list for PBXProject "Microbit Uploader" */ = {
isa = XCConfigurationList;
buildConfigurations = (
E9F4FEEC1D8709980071D783 /* Debug */,
E9F4FEED1D8709980071D783 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
E9F4FEEE1D8709980071D783 /* Build configuration list for PBXNativeTarget "Microbit Uploader" */ = {
isa = XCConfigurationList;
buildConfigurations = (
E9F4FEEF1D8709980071D783 /* Debug */,
E9F4FEF01D8709980071D783 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = E9F4FED51D8709980071D783 /* Project object */;
}

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:Microbit Uploader.xcodeproj">
</FileRef>
</Workspace>

View File

@ -0,0 +1,6 @@
#import <Cocoa/Cocoa.h>
@interface AppDelegate : NSObject <NSApplicationDelegate>
@end

View File

@ -0,0 +1,124 @@
#import "AppDelegate.h"
#import "DirectoryWatcher.h"
#import "Uploader.h"
@interface AppDelegate ()<DirectoryWatcherDelegate, UploaderDelegate, NSUserNotificationCenterDelegate>
@property (weak) IBOutlet NSWindow *window;
@property DirectoryWatcher * watcher;
@property Uploader * uploader;
@property NSStatusItem * menubarItem;
@end
@implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
self.watcher = [[DirectoryWatcher alloc] initWithPath:[self downloadsDirectory]];
self.watcher.delegate = self;
[self.watcher startWatching];
self.uploader = [[Uploader alloc] init];
self.uploader.delegate = self;
[NSUserNotificationCenter defaultUserNotificationCenter].delegate = self;
[self createMenuBarIcon];
[self configureVolumeMountNotifications];
[self showActiveMicroBits];
}
- (void)applicationWillTerminate:(NSNotification *)aNotification {
// Insert code here to tear down your application
[self.watcher stopWatching];
}
- (void)dealloc {
[[NSWorkspace sharedWorkspace].notificationCenter removeObserver:self];
}
#pragma mark - Directory
- (void)watcher:(DirectoryWatcher *)watcher observedNewFileAtPath:(NSString *)path {
NSString * fullPath = [watcher.path stringByAppendingPathComponent:path];
if ([self.uploader shouldUploadFileAtPath:fullPath]) {
[self.uploader uploadFile:fullPath];
}
}
- (NSString*)downloadsDirectory {
NSArray * paths = NSSearchPathForDirectoriesInDomains(NSDownloadsDirectory, NSUserDomainMask, YES);
return paths.firstObject;
}
#pragma mark - Uploader delegate
- (void)uploader:(Uploader *)uploader transferredFile:(NSString *)file toMicroBit:(NSString *)microbit {
[self showNotification:@"micro:bit upload" withDescription:[NSString stringWithFormat:@"%@ uploaded to %@", file.lastPathComponent, microbit]];
}
- (void)uploader:(Uploader *)uploader failedToTransferFile:(NSString *)file toMicroBit:(NSString *)microbit {
[self showNotification:@"micro:bit upload failed" withDescription:[NSString stringWithFormat:@"Couldn't transfer %@ to %@", file.lastPathComponent, microbit]];
}
- (void)showNotification:(NSString*)title withDescription:(NSString*)description {
NSUserNotification * notification = [NSUserNotification new];
notification.title = title;
notification.informativeText = description;
notification.soundName = NSUserNotificationDefaultSoundName;
[[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:notification];
}
#pragma mark - NSUserNotificationCenterDelegate
- (BOOL)userNotificationCenter:(NSUserNotificationCenter *)center shouldPresentNotification:(NSUserNotification *)notification {
return YES;
}
#pragma mark - Volume mount/unmount notification
- (void)configureVolumeMountNotifications {
[[NSWorkspace sharedWorkspace].notificationCenter addObserver:self selector:@selector(volumeMountNotification:) name:NSWorkspaceDidRenameVolumeNotification object:nil];
[[NSWorkspace sharedWorkspace].notificationCenter addObserver:self selector:@selector(volumeMountNotification:) name:NSWorkspaceDidMountNotification object:nil];
[[NSWorkspace sharedWorkspace].notificationCenter addObserver:self selector:@selector(volumeMountNotification:) name:NSWorkspaceDidUnmountNotification object:nil];
}
- (void)volumeMountNotification:(NSNotification*)sender {
//Delay upadting the menu to give the chance for the disk to fully mount or unmount
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self showActiveMicroBits];
});
}
#pragma mark - Menu bar app
- (void)createMenuBarIcon {
self.menubarItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength];
self.menubarItem.button.image = [NSImage imageNamed:@"menubar"];
}
- (void)showActiveMicroBits {
NSMenu * menu = [NSMenu new];
NSString * countString;
NSUInteger count = self.uploader.microBitPaths.count;
if (count == 0) {
countString = @"No connect micro:bits";
}
else if (count == 1) {
countString = @"1 connected micro:bit";
}
else {
countString = [NSString stringWithFormat:@"%lu connected micro:bits", count];
}
NSMenuItem * microBitCount = [[NSMenuItem alloc] initWithTitle:countString action:nil keyEquivalent:@""];
microBitCount.enabled = NO;
[menu addItem:microBitCount];
NSMenuItem * quitItem = [[NSMenuItem alloc] initWithTitle:@"Quit" action:@selector(terminate:) keyEquivalent:@"q"];
[menu addItem:quitItem];
self.menubarItem.menu = menu;
}
@end

View File

@ -0,0 +1,68 @@
{
"images" : [
{
"size" : "16x16",
"idiom" : "mac",
"filename" : "icon_16x16.png",
"scale" : "1x"
},
{
"size" : "16x16",
"idiom" : "mac",
"filename" : "icon_16x16@2x.png",
"scale" : "2x"
},
{
"size" : "32x32",
"idiom" : "mac",
"filename" : "icon_32x32.png",
"scale" : "1x"
},
{
"size" : "32x32",
"idiom" : "mac",
"filename" : "icon_32x32@2x.png",
"scale" : "2x"
},
{
"size" : "128x128",
"idiom" : "mac",
"filename" : "icon_128x128.png",
"scale" : "1x"
},
{
"size" : "128x128",
"idiom" : "mac",
"filename" : "icon_128x128@2x.png",
"scale" : "2x"
},
{
"size" : "256x256",
"idiom" : "mac",
"filename" : "icon_256x256.png",
"scale" : "1x"
},
{
"size" : "256x256",
"idiom" : "mac",
"filename" : "icon_256x256@2x.png",
"scale" : "2x"
},
{
"size" : "512x512",
"idiom" : "mac",
"filename" : "icon_512x512.png",
"scale" : "1x"
},
{
"size" : "512x512",
"idiom" : "mac",
"filename" : "icon_512x512@2x.png",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 744 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 435 KiB

View File

@ -0,0 +1,6 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,15 @@
{
"images" : [
{
"idiom" : "mac",
"filename" : "menubar.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
},
"properties" : {
"template-rendering-intent" : "template"
}
}

View File

@ -0,0 +1,681 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="11201" systemVersion="15G1004" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="11201"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
<connections>
<outlet property="delegate" destination="Voe-Tx-rLC" id="GzC-gU-4Uq"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customObject id="Voe-Tx-rLC" customClass="AppDelegate">
<connections>
<outlet property="window" destination="QvC-M9-y7g" id="gIp-Ho-8D9"/>
</connections>
</customObject>
<customObject id="YLy-65-1bz" customClass="NSFontManager"/>
<menu title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
<items>
<menuItem title="Microbit Uploader" id="1Xt-HY-uBw">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Microbit Uploader" systemMenu="apple" id="uQy-DD-JDr">
<items>
<menuItem title="About Microbit Uploader" id="5kV-Vb-QxS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="orderFrontStandardAboutPanel:" target="-1" id="Exp-CZ-Vem"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
<menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW"/>
<menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/>
<menuItem title="Services" id="NMo-om-nkz">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Services" systemMenu="services" id="hz9-B4-Xy5"/>
</menuItem>
<menuItem isSeparatorItem="YES" id="4je-JR-u6R"/>
<menuItem title="Hide Microbit Uploader" keyEquivalent="h" id="Olw-nP-bQN">
<connections>
<action selector="hide:" target="-1" id="PnN-Uc-m68"/>
</connections>
</menuItem>
<menuItem title="Hide Others" keyEquivalent="h" id="Vdr-fp-XzO">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="hideOtherApplications:" target="-1" id="VT4-aY-XCT"/>
</connections>
</menuItem>
<menuItem title="Show All" id="Kd2-mp-pUS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="unhideAllApplications:" target="-1" id="Dhg-Le-xox"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/>
<menuItem title="Quit Microbit Uploader" keyEquivalent="q" id="4sb-4s-VLi">
<connections>
<action selector="terminate:" target="-1" id="Te7-pn-YzF"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="File" id="dMs-cI-mzQ">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="File" id="bib-Uj-vzu">
<items>
<menuItem title="New" keyEquivalent="n" id="Was-JA-tGl">
<connections>
<action selector="newDocument:" target="-1" id="4Si-XN-c54"/>
</connections>
</menuItem>
<menuItem title="Open…" keyEquivalent="o" id="IAo-SY-fd9">
<connections>
<action selector="openDocument:" target="-1" id="bVn-NM-KNZ"/>
</connections>
</menuItem>
<menuItem title="Open Recent" id="tXI-mr-wws">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Open Recent" systemMenu="recentDocuments" id="oas-Oc-fiZ">
<items>
<menuItem title="Clear Menu" id="vNY-rz-j42">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="clearRecentDocuments:" target="-1" id="Daa-9d-B3U"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem isSeparatorItem="YES" id="m54-Is-iLE"/>
<menuItem title="Close" keyEquivalent="w" id="DVo-aG-piG">
<connections>
<action selector="performClose:" target="-1" id="HmO-Ls-i7Q"/>
</connections>
</menuItem>
<menuItem title="Save…" keyEquivalent="s" id="pxx-59-PXV">
<connections>
<action selector="saveDocument:" target="-1" id="teZ-XB-qJY"/>
</connections>
</menuItem>
<menuItem title="Save As…" keyEquivalent="S" id="Bw7-FT-i3A">
<connections>
<action selector="saveDocumentAs:" target="-1" id="mDf-zr-I0C"/>
</connections>
</menuItem>
<menuItem title="Revert to Saved" id="KaW-ft-85H">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="revertDocumentToSaved:" target="-1" id="iJ3-Pv-kwq"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="aJh-i4-bef"/>
<menuItem title="Page Setup…" keyEquivalent="P" id="qIS-W8-SiK">
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
<connections>
<action selector="runPageLayout:" target="-1" id="Din-rz-gC5"/>
</connections>
</menuItem>
<menuItem title="Print…" keyEquivalent="p" id="aTl-1u-JFS">
<connections>
<action selector="print:" target="-1" id="qaZ-4w-aoO"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Edit" id="5QF-Oa-p0T">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Edit" id="W48-6f-4Dl">
<items>
<menuItem title="Undo" keyEquivalent="z" id="dRJ-4n-Yzg">
<connections>
<action selector="undo:" target="-1" id="M6e-cu-g7V"/>
</connections>
</menuItem>
<menuItem title="Redo" keyEquivalent="Z" id="6dh-zS-Vam">
<connections>
<action selector="redo:" target="-1" id="oIA-Rs-6OD"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="WRV-NI-Exz"/>
<menuItem title="Cut" keyEquivalent="x" id="uRl-iY-unG">
<connections>
<action selector="cut:" target="-1" id="YJe-68-I9s"/>
</connections>
</menuItem>
<menuItem title="Copy" keyEquivalent="c" id="x3v-GG-iWU">
<connections>
<action selector="copy:" target="-1" id="G1f-GL-Joy"/>
</connections>
</menuItem>
<menuItem title="Paste" keyEquivalent="v" id="gVA-U4-sdL">
<connections>
<action selector="paste:" target="-1" id="UvS-8e-Qdg"/>
</connections>
</menuItem>
<menuItem title="Paste and Match Style" keyEquivalent="V" id="WeT-3V-zwk">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="pasteAsPlainText:" target="-1" id="cEh-KX-wJQ"/>
</connections>
</menuItem>
<menuItem title="Delete" id="pa3-QI-u2k">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="delete:" target="-1" id="0Mk-Ml-PaM"/>
</connections>
</menuItem>
<menuItem title="Select All" keyEquivalent="a" id="Ruw-6m-B2m">
<connections>
<action selector="selectAll:" target="-1" id="VNm-Mi-diN"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="uyl-h8-XO2"/>
<menuItem title="Find" id="4EN-yA-p0u">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Find" id="1b7-l0-nxx">
<items>
<menuItem title="Find…" tag="1" keyEquivalent="f" id="Xz5-n4-O0W">
<connections>
<action selector="performFindPanelAction:" target="-1" id="cD7-Qs-BN4"/>
</connections>
</menuItem>
<menuItem title="Find and Replace…" tag="12" keyEquivalent="f" id="YEy-JH-Tfz">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="performFindPanelAction:" target="-1" id="WD3-Gg-5AJ"/>
</connections>
</menuItem>
<menuItem title="Find Next" tag="2" keyEquivalent="g" id="q09-fT-Sye">
<connections>
<action selector="performFindPanelAction:" target="-1" id="NDo-RZ-v9R"/>
</connections>
</menuItem>
<menuItem title="Find Previous" tag="3" keyEquivalent="G" id="OwM-mh-QMV">
<connections>
<action selector="performFindPanelAction:" target="-1" id="HOh-sY-3ay"/>
</connections>
</menuItem>
<menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="buJ-ug-pKt">
<connections>
<action selector="performFindPanelAction:" target="-1" id="U76-nv-p5D"/>
</connections>
</menuItem>
<menuItem title="Jump to Selection" keyEquivalent="j" id="S0p-oC-mLd">
<connections>
<action selector="centerSelectionInVisibleArea:" target="-1" id="IOG-6D-g5B"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Spelling and Grammar" id="Dv1-io-Yv7">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Spelling" id="3IN-sU-3Bg">
<items>
<menuItem title="Show Spelling and Grammar" keyEquivalent=":" id="HFo-cy-zxI">
<connections>
<action selector="showGuessPanel:" target="-1" id="vFj-Ks-hy3"/>
</connections>
</menuItem>
<menuItem title="Check Document Now" keyEquivalent=";" id="hz2-CU-CR7">
<connections>
<action selector="checkSpelling:" target="-1" id="fz7-VC-reM"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="bNw-od-mp5"/>
<menuItem title="Check Spelling While Typing" id="rbD-Rh-wIN">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleContinuousSpellChecking:" target="-1" id="7w6-Qz-0kB"/>
</connections>
</menuItem>
<menuItem title="Check Grammar With Spelling" id="mK6-2p-4JG">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleGrammarChecking:" target="-1" id="muD-Qn-j4w"/>
</connections>
</menuItem>
<menuItem title="Correct Spelling Automatically" id="78Y-hA-62v">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticSpellingCorrection:" target="-1" id="2lM-Qi-WAP"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Substitutions" id="9ic-FL-obx">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Substitutions" id="FeM-D8-WVr">
<items>
<menuItem title="Show Substitutions" id="z6F-FW-3nz">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="orderFrontSubstitutionsPanel:" target="-1" id="oku-mr-iSq"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="gPx-C9-uUO"/>
<menuItem title="Smart Copy/Paste" id="9yt-4B-nSM">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleSmartInsertDelete:" target="-1" id="3IJ-Se-DZD"/>
</connections>
</menuItem>
<menuItem title="Smart Quotes" id="hQb-2v-fYv">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticQuoteSubstitution:" target="-1" id="ptq-xd-QOA"/>
</connections>
</menuItem>
<menuItem title="Smart Dashes" id="rgM-f4-ycn">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticDashSubstitution:" target="-1" id="oCt-pO-9gS"/>
</connections>
</menuItem>
<menuItem title="Smart Links" id="cwL-P1-jid">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticLinkDetection:" target="-1" id="Gip-E3-Fov"/>
</connections>
</menuItem>
<menuItem title="Data Detectors" id="tRr-pd-1PS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticDataDetection:" target="-1" id="R1I-Nq-Kbl"/>
</connections>
</menuItem>
<menuItem title="Text Replacement" id="HFQ-gK-NFA">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticTextReplacement:" target="-1" id="DvP-Fe-Py6"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Transformations" id="2oI-Rn-ZJC">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Transformations" id="c8a-y6-VQd">
<items>
<menuItem title="Make Upper Case" id="vmV-6d-7jI">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="uppercaseWord:" target="-1" id="sPh-Tk-edu"/>
</connections>
</menuItem>
<menuItem title="Make Lower Case" id="d9M-CD-aMd">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="lowercaseWord:" target="-1" id="iUZ-b5-hil"/>
</connections>
</menuItem>
<menuItem title="Capitalize" id="UEZ-Bs-lqG">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="capitalizeWord:" target="-1" id="26H-TL-nsh"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Speech" id="xrE-MZ-jX0">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Speech" id="3rS-ZA-NoH">
<items>
<menuItem title="Start Speaking" id="Ynk-f8-cLZ">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="startSpeaking:" target="-1" id="654-Ng-kyl"/>
</connections>
</menuItem>
<menuItem title="Stop Speaking" id="Oyz-dy-DGm">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="stopSpeaking:" target="-1" id="dX8-6p-jy9"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Format" id="jxT-CU-nIS">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Format" id="GEO-Iw-cKr">
<items>
<menuItem title="Font" id="Gi5-1S-RQB">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Font" systemMenu="font" id="aXa-aM-Jaq">
<items>
<menuItem title="Show Fonts" keyEquivalent="t" id="Q5e-8K-NDq">
<connections>
<action selector="orderFrontFontPanel:" target="YLy-65-1bz" id="WHr-nq-2xA"/>
</connections>
</menuItem>
<menuItem title="Bold" tag="2" keyEquivalent="b" id="GB9-OM-e27">
<connections>
<action selector="addFontTrait:" target="YLy-65-1bz" id="hqk-hr-sYV"/>
</connections>
</menuItem>
<menuItem title="Italic" tag="1" keyEquivalent="i" id="Vjx-xi-njq">
<connections>
<action selector="addFontTrait:" target="YLy-65-1bz" id="IHV-OB-c03"/>
</connections>
</menuItem>
<menuItem title="Underline" keyEquivalent="u" id="WRG-CD-K1S">
<connections>
<action selector="underline:" target="-1" id="FYS-2b-JAY"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="5gT-KC-WSO"/>
<menuItem title="Bigger" tag="3" keyEquivalent="+" id="Ptp-SP-VEL">
<connections>
<action selector="modifyFont:" target="YLy-65-1bz" id="Uc7-di-UnL"/>
</connections>
</menuItem>
<menuItem title="Smaller" tag="4" keyEquivalent="-" id="i1d-Er-qST">
<connections>
<action selector="modifyFont:" target="YLy-65-1bz" id="HcX-Lf-eNd"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="kx3-Dk-x3B"/>
<menuItem title="Kern" id="jBQ-r6-VK2">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Kern" id="tlD-Oa-oAM">
<items>
<menuItem title="Use Default" id="GUa-eO-cwY">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="useStandardKerning:" target="-1" id="6dk-9l-Ckg"/>
</connections>
</menuItem>
<menuItem title="Use None" id="cDB-IK-hbR">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="turnOffKerning:" target="-1" id="U8a-gz-Maa"/>
</connections>
</menuItem>
<menuItem title="Tighten" id="46P-cB-AYj">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="tightenKerning:" target="-1" id="hr7-Nz-8ro"/>
</connections>
</menuItem>
<menuItem title="Loosen" id="ogc-rX-tC1">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="loosenKerning:" target="-1" id="8i4-f9-FKE"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Ligatures" id="o6e-r0-MWq">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Ligatures" id="w0m-vy-SC9">
<items>
<menuItem title="Use Default" id="agt-UL-0e3">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="useStandardLigatures:" target="-1" id="7uR-wd-Dx6"/>
</connections>
</menuItem>
<menuItem title="Use None" id="J7y-lM-qPV">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="turnOffLigatures:" target="-1" id="iX2-gA-Ilz"/>
</connections>
</menuItem>
<menuItem title="Use All" id="xQD-1f-W4t">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="useAllLigatures:" target="-1" id="KcB-kA-TuK"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Baseline" id="OaQ-X3-Vso">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Baseline" id="ijk-EB-dga">
<items>
<menuItem title="Use Default" id="3Om-Ey-2VK">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="unscript:" target="-1" id="0vZ-95-Ywn"/>
</connections>
</menuItem>
<menuItem title="Superscript" id="Rqc-34-cIF">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="superscript:" target="-1" id="3qV-fo-wpU"/>
</connections>
</menuItem>
<menuItem title="Subscript" id="I0S-gh-46l">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="subscript:" target="-1" id="Q6W-4W-IGz"/>
</connections>
</menuItem>
<menuItem title="Raise" id="2h7-ER-AoG">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="raiseBaseline:" target="-1" id="4sk-31-7Q9"/>
</connections>
</menuItem>
<menuItem title="Lower" id="1tx-W0-xDw">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="lowerBaseline:" target="-1" id="OF1-bc-KW4"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem isSeparatorItem="YES" id="Ndw-q3-faq"/>
<menuItem title="Show Colors" keyEquivalent="C" id="bgn-CT-cEk">
<connections>
<action selector="orderFrontColorPanel:" target="-1" id="mSX-Xz-DV3"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="iMs-zA-UFJ"/>
<menuItem title="Copy Style" keyEquivalent="c" id="5Vv-lz-BsD">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="copyFont:" target="-1" id="GJO-xA-L4q"/>
</connections>
</menuItem>
<menuItem title="Paste Style" keyEquivalent="v" id="vKC-jM-MkH">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="pasteFont:" target="-1" id="JfD-CL-leO"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Text" id="Fal-I4-PZk">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Text" id="d9c-me-L2H">
<items>
<menuItem title="Align Left" keyEquivalent="{" id="ZM1-6Q-yy1">
<connections>
<action selector="alignLeft:" target="-1" id="zUv-R1-uAa"/>
</connections>
</menuItem>
<menuItem title="Center" keyEquivalent="|" id="VIY-Ag-zcb">
<connections>
<action selector="alignCenter:" target="-1" id="spX-mk-kcS"/>
</connections>
</menuItem>
<menuItem title="Justify" id="J5U-5w-g23">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="alignJustified:" target="-1" id="ljL-7U-jND"/>
</connections>
</menuItem>
<menuItem title="Align Right" keyEquivalent="}" id="wb2-vD-lq4">
<connections>
<action selector="alignRight:" target="-1" id="r48-bG-YeY"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="4s2-GY-VfK"/>
<menuItem title="Writing Direction" id="H1b-Si-o9J">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Writing Direction" id="8mr-sm-Yjd">
<items>
<menuItem title="Paragraph" enabled="NO" id="ZvO-Gk-QUH">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem id="YGs-j5-SAR">
<string key="title"> Default</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeBaseWritingDirectionNatural:" target="-1" id="qtV-5e-UBP"/>
</connections>
</menuItem>
<menuItem id="Lbh-J2-qVU">
<string key="title"> Left to Right</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeBaseWritingDirectionLeftToRight:" target="-1" id="S0X-9S-QSf"/>
</connections>
</menuItem>
<menuItem id="jFq-tB-4Kx">
<string key="title"> Right to Left</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeBaseWritingDirectionRightToLeft:" target="-1" id="5fk-qB-AqJ"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="swp-gr-a21"/>
<menuItem title="Selection" enabled="NO" id="cqv-fj-IhA">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem id="Nop-cj-93Q">
<string key="title"> Default</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeTextWritingDirectionNatural:" target="-1" id="lPI-Se-ZHp"/>
</connections>
</menuItem>
<menuItem id="BgM-ve-c93">
<string key="title"> Left to Right</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeTextWritingDirectionLeftToRight:" target="-1" id="caW-Bv-w94"/>
</connections>
</menuItem>
<menuItem id="RB4-Sm-HuC">
<string key="title"> Right to Left</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeTextWritingDirectionRightToLeft:" target="-1" id="EXD-6r-ZUu"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem isSeparatorItem="YES" id="fKy-g9-1gm"/>
<menuItem title="Show Ruler" id="vLm-3I-IUL">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleRuler:" target="-1" id="FOx-HJ-KwY"/>
</connections>
</menuItem>
<menuItem title="Copy Ruler" keyEquivalent="c" id="MkV-Pr-PK5">
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
<connections>
<action selector="copyRuler:" target="-1" id="71i-fW-3W2"/>
</connections>
</menuItem>
<menuItem title="Paste Ruler" keyEquivalent="v" id="LVM-kO-fVI">
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
<connections>
<action selector="pasteRuler:" target="-1" id="cSh-wd-qM2"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="View" id="H8h-7b-M4v">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="View" id="HyV-fh-RgO">
<items>
<menuItem title="Show Toolbar" keyEquivalent="t" id="snW-S8-Cw5">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="toggleToolbarShown:" target="-1" id="BXY-wc-z0C"/>
</connections>
</menuItem>
<menuItem title="Customize Toolbar…" id="1UK-8n-QPP">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="runToolbarCustomizationPalette:" target="-1" id="pQI-g3-MTW"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Window" id="aUF-d1-5bR">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Window" systemMenu="window" id="Td7-aD-5lo">
<items>
<menuItem title="Minimize" keyEquivalent="m" id="OY7-WF-poV">
<connections>
<action selector="performMiniaturize:" target="-1" id="VwT-WD-YPe"/>
</connections>
</menuItem>
<menuItem title="Zoom" id="R4o-n2-Eq4">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="performZoom:" target="-1" id="DIl-cC-cCs"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="eu3-7i-yIM"/>
<menuItem title="Bring All to Front" id="LE2-aR-0XJ">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="arrangeInFront:" target="-1" id="DRN-fu-gQh"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Help" id="wpr-3q-Mcd">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Help" systemMenu="help" id="F2S-fz-NVQ">
<items>
<menuItem title="Microbit Uploader Help" keyEquivalent="?" id="FKE-Sm-Kum">
<connections>
<action selector="showHelp:" target="-1" id="y7X-2Q-9no"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
<window title="Microbit Uploader" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="QvC-M9-y7g">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="335" y="390" width="480" height="360"/>
<rect key="screenRect" x="0.0" y="0.0" width="1280" height="777"/>
<view key="contentView" id="EiT-Mj-1SZ">
<rect key="frame" x="0.0" y="0.0" width="480" height="360"/>
<autoresizingMask key="autoresizingMask"/>
</view>
</window>
</objects>
</document>

View File

@ -0,0 +1,24 @@
#import <Foundation/Foundation.h>
@class DirectoryWatcher;
@protocol DirectoryWatcherDelegate <NSObject>
- (void)watcher:(DirectoryWatcher*)watcher observedNewFileAtPath:(NSString*)path;
@end
@interface DirectoryWatcher : NSObject
- (instancetype)initWithPath:(NSString*)path;
@property (readonly) NSString * path;
@property id<DirectoryWatcherDelegate> delegate;
- (void)startWatching;
//Automatically called when deallocated
- (void)stopWatching;
@end

View File

@ -0,0 +1,73 @@
#import "DirectoryWatcher.h"
#import <CoreServices/CoreServices.h>
void callback(ConstFSEventStreamRef streamRef, void * info, size_t numEvents, void * eventPaths, const FSEventStreamEventFlags eventFlags[], const FSEventStreamEventId eventIds[]);
@interface DirectoryWatcher ()
@property NSString * path;
@property NSMutableSet<NSString*>* knownFiles;
@property FSEventStreamRef stream;
- (void)rescanPathWithEvents:(BOOL)sendEvents;
@end
@implementation DirectoryWatcher
- (instancetype)initWithPath:(NSString *)path {
if (!path) {
return nil;
}
self = [super init];
if (self) {
self.path = path;
}
return self;
}
- (void)dealloc {
[self stopWatching];
}
- (void)startWatching {
self.knownFiles = [NSMutableSet new];
[self rescanPathWithEvents:NO];
CFStringRef path = (__bridge CFStringRef)(self.path);
CFArrayRef pathsToWatch = CFArrayCreate(NULL, (const void**)&path, 1, NULL);
CFAbsoluteTime latency = 1;
FSEventStreamContext context = { 0, (__bridge void * _Nullable)(self), NULL, NULL, NULL };
self.stream = FSEventStreamCreate(NULL, &callback, &context, pathsToWatch, kFSEventStreamEventIdSinceNow, latency, kFSEventStreamCreateFlagNone);
FSEventStreamScheduleWithRunLoop(self.stream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
FSEventStreamStart(self.stream);
}
- (void)stopWatching {
if (self.stream) {
FSEventStreamStop(self.stream);
FSEventStreamInvalidate(self.stream);
FSEventStreamRelease(self.stream);
self.stream = nil;
}
}
- (void)rescanPathWithEvents:(BOOL)sendEvents {
NSArray<NSString*>* downloadFiles = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:self.path error:nil];
for (NSString * file in downloadFiles) {
if (![self.knownFiles containsObject:file]) {
if (sendEvents) {
[self.delegate watcher:self observedNewFileAtPath:file];
}
[self.knownFiles addObject:file];
}
}
}
@end
void callback(ConstFSEventStreamRef streamRef, void * info, size_t numEvents, void * eventPaths, const FSEventStreamEventFlags eventFlags[], const FSEventStreamEventId eventIds[]) {
DirectoryWatcher * watcher = (__bridge DirectoryWatcher*)info;
[watcher rescanPathWithEvents:YES];
}

View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>LSUIElement</key>
<true/>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSMinimumSystemVersion</key>
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2016 Thomas Denney. All rights reserved.</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
</dict>
</plist>

View File

@ -0,0 +1,21 @@
#import <Foundation/Foundation.h>
@class Uploader;
@protocol UploaderDelegate <NSObject>
- (void)uploader:(Uploader*)uploader transferredFile:(NSString*)file toMicroBit:(NSString*)microbit;
- (void)uploader:(Uploader*)uploader failedToTransferFile:(NSString*)file toMicroBit:(NSString*)microbit;
@end
@interface Uploader : NSObject
@property id<UploaderDelegate> delegate;
- (BOOL)shouldUploadFileAtPath:(NSString*)path;
- (NSArray<NSString*>*)microBitPaths;
- (void)uploadFile:(NSString*)file;
- (void)uploadFile:(NSString*)file toMicroBit:(NSString*)path;
@end

View File

@ -0,0 +1,74 @@
#import "Uploader.h"
@interface Uploader ()
@property NSOperationQueue * backgroundCopyQueue;
@end
@implementation Uploader
- (instancetype)init {
self = [super init];
if (self) {
self.backgroundCopyQueue = [NSOperationQueue new];
}
return self;
}
- (BOOL)shouldUploadFileAtPath:(NSString *)path {
//Whilst Safari is downloading the file it appends .download to the name
NSRegularExpression * ignoreDownload = [NSRegularExpression regularExpressionWithPattern:@".download$" options:NSRegularExpressionCaseInsensitive error:nil];
if ([ignoreDownload numberOfMatchesInString:path.lastPathComponent options:0 range:NSMakeRange(0, path.lastPathComponent.length)] > 0) {
return NO;
}
//Chrome and Firefox create .hex files
NSRegularExpression * hexFiles = [NSRegularExpression regularExpressionWithPattern:@".hex$" options:NSRegularExpressionCaseInsensitive error:nil];
if ([hexFiles numberOfMatchesInString:path.lastPathComponent options:0 range:NSMakeRange(0, path.lastPathComponent.length)] > 0) {
return YES;
}
//Safari tends to just name files 'Unknown X'
NSRegularExpression * unknownFiles = [NSRegularExpression regularExpressionWithPattern:@"^Unknown(( |-)[0-9]+)?" options:NSRegularExpressionCaseInsensitive error:nil];
if ([unknownFiles numberOfMatchesInString:path.lastPathComponent options:0 range:NSMakeRange(0, path.lastPathComponent.length)]) {
return YES;
}
return NO;
}
- (NSArray<NSString*>*)microBitPaths {
NSArray<NSURL*>* allVolumes = [[NSFileManager defaultManager] mountedVolumeURLsIncludingResourceValuesForKeys:nil options:NSVolumeEnumerationSkipHiddenVolumes];
NSMutableArray<NSString*>* microbitPaths = [NSMutableArray new];
NSRegularExpression * microbitRegex = [NSRegularExpression regularExpressionWithPattern:@"^MICROBIT" options:NSRegularExpressionCaseInsensitive error:nil];
for (NSURL * volume in allVolumes) {
NSString * lastPathComponent = volume.lastPathComponent;
if ([microbitRegex numberOfMatchesInString:lastPathComponent options:0 range:NSMakeRange(0, lastPathComponent.length)] > 0) {
[microbitPaths addObject:volume.path];
}
}
return microbitPaths;
}
- (void)uploadFile:(NSString *)file {
for (NSString * microbit in [self microBitPaths]) {
[self uploadFile:file toMicroBit:microbit];
}
}
- (void)uploadFile:(NSString *)file toMicroBit:(NSString *)path {
[self.backgroundCopyQueue addOperationWithBlock:^{
NSError * copyError;
NSString * destination = [path stringByAppendingPathComponent:file.lastPathComponent];
if (![[NSFileManager defaultManager] copyItemAtPath:file toPath:destination error:&copyError]) {
[self.delegate uploader:self failedToTransferFile:file toMicroBit:path.lastPathComponent];
}
else {
[self.delegate uploader:self transferredFile:file toMicroBit:path.lastPathComponent];
}
}];
}
@end

View File

@ -0,0 +1,5 @@
#import <Cocoa/Cocoa.h>
int main(int argc, const char * argv[]) {
return NSApplicationMain(argc, argv);
}

View File

@ -0,0 +1,40 @@
# micro:bit uploader for OS X
![](Microbit Uploader/Assets.xcassets/AppIcon.appiconset/icon_256x256.png)
This project is a clone of the [Windows
uploader](https://codethemicrobit.com/uploader), but for OS X. Once launched,
the app runs in your menu bar and will automatically deploy any HEX files to
your `micro:bit`. Like the Windows version, it is compatible with any browser
that can run [codethemicrobit.com](http://codethemicrobit.com).
## Install the built version
1. Download the latest `.zip` release from the `Release` directory
2. Unzip it
3. Drag `Microbit Uploader` to your Applications folder and launch it
## Building
To build the project you'll need a copy of OS X 10.11 or higher and Xcode 8 or
higher (you may be able to build on earlier OSes or versions of Xcode, but this
remains untested). Once you have a development environment set up, just build
and run `Microbit Uploader.xcodeproj`.
## Distributing
1. Open the Xcode project
2. Product > Archive
3. Export:
![Export](Graphics/export.png)
4. You will then have the option of either signing or not-signing the
application:
a) If you have an Apple developer account, select 'Export a Developer
ID-signed Application'
b) If you don't have a developer ID, select 'Export as a macOS App'
5. Zip the produced app and upload to CDN or equivalent

Binary file not shown.

5
docfiles/target.css Normal file
View File

@ -0,0 +1,5 @@
#root .avatar .avatar-image {
background-image: url(https://az851932.vo.msecnd.net/pub/jovrytni/microbit.simplified.svg);
background-size: contain;
background-repeat: no-repeat;
}

View File

@ -1,3 +1,50 @@
![](/static/mb/device/pano.jpg)
# About
### @description A Blocks / Javascript code editor for the micro:bit, a pocket-size computer with 5x5 display, sensors and Bluetooth.
The [BBC micro:bit](https://www.microbit.co.uk) is a [pocket-size computer](/device) with a 5x5 display of 25 LEDs, Bluetooth and sensors that can be programmed by anyone.
The BBC micro:bit was made possible by many [partners](https://www.microbit.co.uk/partners).
The micro:bit provides an easy and fun introduction to programming and making switch on, program it to do something fun wear it, customize it.
Just like Arduino, the micro:bit can be connected to and interact with sensors, displays, and other devices.
* [Read the docs](/docs)
## [Hardware: The Device](/device)
The BBC micro:bit is packaged with sensors, radio and other goodies. Learn about the [hardware components](/device) of the micro:bit to make the most of it!
## Programming: [Blocks](/blocks) or [JavaScript](/javascript)
You can program the micro:bit using [Blocks](/blocks) or [JavaScript](/javascript) in your web browser via the [micro:bit APIs](/reference):
```block
input.onButtonPressed(Button.A, () => {
basic.showString("Hi!");
})
```
```typescript
input.onButtonPressed(Button.A, () => {
basic.showString("Hi!");
})
```
The editor work in [most modern browsers](/browsers), work [offline](/offline) once loaded and do not require any installation.
## [Compile and Flash: Your Program!](/device/usb)
When you have your code ready, you connect your micro:bit to a computer via a USB cable, so it appears as a mounted drive (named MICROBIT).
Compilation to ARM thumb machine code from [Blocks](/blocks) or [JavaScript](/javascript) happens in the browser. You save the ARM binary
program to a file, which you then copy to the micro:bit drive, which flashes the micro:bit device with the new program.
## Simulator: Test Your Code
You can run your code using the micro:bit simulator, all within the confines of a web browser.
The simulator has support for the LED screen, buttons, as well as compass, accelerometer, and digital I/O pins.
```sim
basic.forever(() => {
basic.showString("Hi!");
@ -20,43 +67,8 @@ input.onButtonPressed(Button.B, () => {
. # . # .
. . # . .`);
});
```
# About
### @description A Blocks / Javascript code editor for the micro:bit, a pocket-size computer with 5x5 display, sensors and Bluetooth.
The [BBC micro:bit](https://www.microbit.co.uk) is a [pocket-size computer](/device) with a 5x5 display of 25 LEDs, Bluetooth and sensors that can be programmed by anyone.
The BBC micro:bit was made possible by many [partners](https://www.microbit.co.uk/partners).
The micro:bit provides an easy and fun introduction to programming and making switch on, program it to do something fun wear it, customize it.
Just like Arduino, the micro:bit can be connected to and interact with sensors, displays, and other devices.
## Hardware: The Device
Learn about the [hardware components](/device) of the micro:bit to make the most of it!
## Programming: Blocks or JavaScript
You can program the micro:bit using [Blocks](/blocks) or [JavaScript](/javascript), via the [micro:bit APIs](/reference):
```blocks
input.onButtonPressed(Button.A, () => {
basic.showString("Hi!");
})
```
## Compile and Flash: Your Program!
When you have your code ready, you connect your micro:bit to a computer via a USB cable, so it appears as a mounted drive (named MICROBIT).
Compilation to ARM thumb machine code from [Blocks](/blocks) or [JavaScript](/javascript) happens in the browser. You save the ARM binary
program to a file, which you then copy to the micro:bit drive, which flashes the micro:bit device with the new program.
## Simulator: Test Your Code
You can run your code using the micro:bit simulator, all within the confines of a web browser.
The simulator has support for the LED screen, buttons, as well as compass, accelerometer, and digital I/O pins.
## C++ Runtime
The [C++ micro:bit runtime](http://lancaster-university.github.io/microbit-docs/), created at [Lancaster University](http://www.lancaster.ac.uk/), provides access to the hardware functions of the micro:bit,
@ -65,6 +77,15 @@ as well as a set of helper functions (such as displaying a number/image/string o
The [micro:bit library](/reference) mirrors the functions of the C++ library.
When code is compiled to ARM machine code, the calls to JavaScript micro:bit functions are replaced with calls to the corresponding C++ functions.
## Open Source
## [Command Line Tools](/cli)
Looking to use codethemicrobit.com in your favorite editor? Install the [command line tools](/cli) and get rolling!
## [Packages](/packages)
Create, edit and distribute your own blocks and JavaScript using [packages](/packages). Packages are hosted on GitHub and may be written
using C++, JavaScript and/or ARM thumb.
## [Open Source](/open-source)
The code for the micro:bit is [open source](/open-source) on GitHub. Contributors are welcome!

101
docs/browsers.md Normal file
View File

@ -0,0 +1,101 @@
# Unsupported configuration
[codethemicrobit.com](https://codethemicrobit.com) doesn't currently support
your browser or operating system. The following configurations are supported:
## Windows
You need one of these browsers running on Windows 7, Windows 8, Windows 8.1, or
Windows 10:
* Internet Explorer 11
* Microsoft Edge
* Google Chrome
* Mozilla Firefox
## Mac
You need one of these browsers running on OS X 10.9 Mavericks, OS X 10.10
Yosemite, OS X 10.11 El Capitan, or macOS 10.12 Sierra:
* Safari
* Google Chrome
* Mozilla Firefox
## Linux
If you're using a Raspberry Pi, please see [the documentation
here](/raspberry-pi).
You need to be running a Linux distribution recent enough to run the most recent
version of one of the following:
* Google Chrome, or Chromium
* Mozilla Firefox, Iceweasel, or Seamonkey
## How to check your OS or browser
### Windows
* Click on the Start menu
* Type 'System'
* Click on the app called 'System'
* The version of Windows you are using will be displayed:
![](/static/configurations/windows-version.png)
### Mac
* Click on the Apple icon in the top left
* Click on 'About this Mac'
* This window will be displayed:
![](/static/configurations/osx-version.png)
### Internet Explorer
* Click on the Settings icon in the top right
* Click 'About Internet Explorer'
* This window will be displayed:
![](/static/configurations/ie-version.png)
### Edge
Edge automatically updates, so you should always be using the latest version
* Click on the menu icon in the top right (three dots)
* Scroll to the bottom
* Information similar to the following will be displayed:
![](/static/configurations/edge-version.png)
### Google Chrome
Google Chrome automatically updates, so you should always be using the latest version
* Click on the menu icon in the top right (three dots)
* Click Help, and About Google Chrome
* Information similar to the following will be displayed:
![](/static/configurations/chrome-version.png)
### Firefox
Firefox automatically updates, so you should always be using the latest version
* Click on the menu icon in the top right (three horizontal lines)
* Click the question mark icon (help button)
* Click 'About Firefox'
![](/static/configurations/firefox-version.png)
### Safari
Safari updates when your operating system updates, so if you are using the
latest version of OS X then you'll be using the latest version of Safari.
* Click on the Safari menu in the top left
* Click 'About Safari'
![](/static/configurations/safari-version.png)

6
docs/browsers/linux.md Normal file
View File

@ -0,0 +1,6 @@
# Unsupported configuration
As you are using Linux, it is recommended that you use Mozilla Firefox or Google
Chrome.
Please see [here](/browsers) for more information.

6
docs/browsers/mac.md Normal file
View File

@ -0,0 +1,6 @@
# Unsupported configuration
As you are using OS X, it is recommended that you use Safari. Alternatively,
Google Chrome and Mozilla Firefox are also supported.
Please see [here](/browsers) for more information.

8
docs/browsers/windows.md Normal file
View File

@ -0,0 +1,8 @@
# Unsupported configuration
As you are using Windows, it is recommended that you use Microsoft Edge. If you
are running a version of Windows prior to Windows 10, you can use Internet
Explorer 11. Alternatively, Google Chrome and Mozilla Firefox are also
supported.
Please see [here](/browsers) for more information.

View File

@ -69,7 +69,7 @@ Select **Save File** and then select **OK**.
The file will then appear in your downloads in the top right of your browser.
Click the **folder icon** next to the filename to open it in Windows Explorer.
![](/static/mb/device/usb-windows-firefox-2.png)
![](/static/mb/device/usb-windows-firefox-2.jpg)
Drag and drop the `.hex` file from the download folder onto the `MICROBIT` drive.
@ -115,7 +115,7 @@ your downloads in the top right of your browser. Right click on the file and
click on **Show in Finder** and the file will appear in your downloads folder.
Select the file and drag and drop it onto your `MICROBIT` drive.
![](/static/mb/device/usb-osx-firefox-1.png)
![](/static/mb/device/usb-osx-firefox-1.jpg)
![](/static/mb/device/usb-osx-firefox-2.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

BIN
docs/static/mb/device/pano.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 343 KiB

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 593 KiB

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 201 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.4 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 164 KiB

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 160 KiB

After

Width:  |  Height:  |  Size: 50 KiB

View File

@ -1,6 +1,6 @@
{
"name": "pxt-microbit",
"version": "0.4.2",
"version": "0.4.9",
"description": "micro:bit target for PXT",
"keywords": [
"JavaScript",
@ -29,6 +29,6 @@
"typescript": "^1.8.7"
},
"dependencies": {
"pxt-core": "0.4.3"
"pxt-core": "0.4.11"
}
}

View File

@ -87,6 +87,95 @@
"bluetooth": true,
"thermometer": true,
"compass": true
},
"boardDefinition": {
"visual": "microbit",
"gpioPinBlocks": [
[
"P0"
],
[
"P1"
],
[
"P2"
],
[
"P3"
],
[
"P4",
"P5",
"P6",
"P7"
],
[
"P8",
"P9",
"P10",
"P11",
"P12"
],
[
"P16"
]
],
"gpioPinMap": {
"P0": "P0",
"P1": "P1",
"P2": "P2",
"P3": "P3",
"P4": "P4",
"P5": "P5",
"P6": "P6",
"P7": "P7",
"P8": "P8",
"P9": "P9",
"P10": "P10",
"P11": "P11",
"P12": "P12",
"P13": "P13",
"P14": "P14",
"P15": "P15",
"P16": "P16",
"P19": "P19",
"P20": "P20"
},
"spiPins": {
"MOSI": "P15",
"MISO": "P14",
"SCK": "P13"
},
"i2cPins": {
"SDA": "P20",
"SCL": "P19"
},
"analogInPins": [
"P0",
"P1",
"P2",
"P3",
"P10"
],
"groundPins": [
"GND"
],
"threeVoltPins": [
"+3v3"
],
"attachPowerOnRight": true,
"onboardComponents": [
"buttonpair",
"ledmatrix",
"speaker"
],
"useCrocClips": true,
"marginWhenBreadboarding": [
0,
0,
80,
0
]
}
},
"compileService": {
@ -116,6 +205,33 @@
"privacyUrl": "https://go.microsoft.com/fwlink/?LinkId=521839",
"termsOfUseUrl": "https://go.microsoft.com/fwlink/?LinkID=206977",
"githubUrl": "https://github.com/Microsoft/pxt-microbit",
"browserSupport": [
{
"name": "unsupported",
"os": "*",
"path": "/browsers"
},
{
"name": "unsupported",
"os": "mac",
"path": "/browsers/mac"
},
{
"name": "unsupported",
"os": "linux",
"path": "browsers/linux"
},
{
"name": "unsupported",
"os": "rpi",
"path": "/raspberry-pi"
},
{
"name": "unsupported",
"os": "windows",
"path": "/browsers/windows"
}
],
"boardName": "BBC micro:bit",
"docMenu": [
{
@ -168,7 +284,7 @@
"name": "save",
"os": "mac",
"browser": "firefox",
"path": "/static/mb/device/usb-osx-firefox-1.png"
"path": "/static/mb/device/usb-osx-firefox-1.jpg"
},
{
"name": "save",

View File

@ -1,12 +1,7 @@
/// <reference path="../node_modules/pxt-core/built/pxtsim.d.ts"/>
namespace pxsim {
export class DalBoard extends BaseBoard {
id: string;
// the bus
bus: pxsim.EventBus;
export class DalBoard extends CoreBoard {
// state & update logic for component services
ledMatrixState: LedMatrixState;
edgeConnectorState: EdgeConnectorState;
@ -19,37 +14,58 @@ namespace pxsim {
radioState: RadioState;
neopixelState: NeoPixelState;
// updates
updateSubscribers: (() => void)[];
constructor() {
super()
this.id = "b" + Math_.random(2147483647);
this.bus = new pxsim.EventBus(runtime);
// components
this.ledMatrixState = new LedMatrixState(runtime);
this.buttonPairState = new ButtonPairState({
this.builtinParts["ledmatrix"] = this.ledMatrixState = new LedMatrixState(runtime);
this.builtinParts["buttonpair"] = this.buttonPairState = new ButtonPairState({
ID_BUTTON_A: DAL.MICROBIT_ID_BUTTON_A,
ID_BUTTON_B: DAL.MICROBIT_ID_BUTTON_B,
ID_BUTTON_AB: DAL.MICROBIT_ID_BUTTON_AB,
BUTTON_EVT_UP: DAL.MICROBIT_BUTTON_EVT_UP,
BUTTON_EVT_CLICK: DAL.MICROBIT_BUTTON_EVT_CLICK
});
this.edgeConnectorState = new EdgeConnectorState();
this.radioState = new RadioState(runtime);
this.accelerometerState = new AccelerometerState(runtime);
this.serialState = new SerialState();
this.thermometerState = new ThermometerState();
this.lightSensorState = new LightSensorState();
this.compassState = new CompassState();
this.neopixelState = new NeoPixelState();
this.builtinParts["edgeconnector"] = this.edgeConnectorState = new EdgeConnectorState({
pins: [
DAL.MICROBIT_ID_IO_P0,
DAL.MICROBIT_ID_IO_P1,
DAL.MICROBIT_ID_IO_P2,
DAL.MICROBIT_ID_IO_P3,
DAL.MICROBIT_ID_IO_P4,
DAL.MICROBIT_ID_IO_P5,
DAL.MICROBIT_ID_IO_P6,
DAL.MICROBIT_ID_IO_P7,
DAL.MICROBIT_ID_IO_P8,
DAL.MICROBIT_ID_IO_P9,
DAL.MICROBIT_ID_IO_P10,
DAL.MICROBIT_ID_IO_P11,
DAL.MICROBIT_ID_IO_P12,
DAL.MICROBIT_ID_IO_P13,
DAL.MICROBIT_ID_IO_P14,
DAL.MICROBIT_ID_IO_P15,
DAL.MICROBIT_ID_IO_P16,
0,
0,
DAL.MICROBIT_ID_IO_P19,
DAL.MICROBIT_ID_IO_P20
]
});
this.builtinParts["radio"] = this.radioState = new RadioState(runtime);
this.builtinParts["accelerometer"] = this.accelerometerState = new AccelerometerState(runtime);
this.builtinParts["serial"] = this.serialState = new SerialState();
this.builtinParts["thermometer"] = this.thermometerState = new ThermometerState();
this.builtinParts["lightsensor"] = this.lightSensorState = new LightSensorState();
this.builtinParts["compass"] = this.compassState = new CompassState();
this.builtinParts["neopixel"] = this.neopixelState = new NeoPixelState();
// updates
this.updateSubscribers = []
this.updateView = () => {
this.updateSubscribers.forEach(sub => sub());
}
this.builtinVisuals["buttonpair"] = () => new visuals.ButtonPairView();
this.builtinVisuals["ledmatrix"] = () => new visuals.LedMatrixView();
this.builtinVisuals["neopixel"] = () => new visuals.NeoPixelView();
this.builtinPartVisuals["buttonpair"] = (xy: visuals.Coord) => visuals.mkBtnSvg(xy);
this.builtinPartVisuals["ledmatrix"] = (xy: visuals.Coord) => visuals.mkLedMatrixSvg(xy, 8, 8);
this.builtinPartVisuals["neopixel"] = (xy: visuals.Coord) => visuals.mkNeoPixelPart(xy);
}
receiveMessage(msg: SimulatorMessage) {
@ -71,23 +87,17 @@ namespace pxsim {
}
}
kill() {
super.kill();
AudioContextManager.stop();
}
initAsync(msg: SimulatorRunMessage): Promise<void> {
super.initAsync(msg);
let options = (msg.options || {}) as RuntimeOptions;
const options = (msg.options || {}) as RuntimeOptions;
let boardDef = CURRENT_BOARD; //TODO: read from pxt.json/pxttarget.json
const boardDef = msg.boardDefinition;
const cmpsList = msg.parts;
const cmpDefs = msg.partDefinitions || {};
const fnArgs = msg.fnArgs;
let cmpsList = msg.parts;
let cmpDefs = msg.partDefinitions || {}; //TODO: read from pxt.json/pxttarget.json
let fnArgs = msg.fnArgs;
let viewHost = new visuals.BoardHost({
const opts : visuals.BoardHostOpts = {
state: this,
boardDef: boardDef,
partsList: cmpsList,
@ -95,7 +105,8 @@ namespace pxsim {
fnArgs: fnArgs,
maxWidth: "100%",
maxHeight: "100%",
});
};
const viewHost = new visuals.BoardHost(pxsim.visuals.mkBoardView(opts), opts);
document.body.innerHTML = ""; // clear children
document.body.appendChild(viewHost.getView());
@ -131,9 +142,9 @@ namespace pxsim {
if (!pxsim.initCurrentRuntime) {
pxsim.initCurrentRuntime = initRuntimeWithDalBoard;
}
}
export function board() {
return runtime.board as DalBoard;
}
}
}

View File

@ -1,81 +0,0 @@
/// <reference path="../node_modules/pxt-core/typings/bluebird/bluebird.d.ts"/>
/// <reference path="../node_modules/pxt-core/built/pxtparts.d.ts"/>
/// <reference path="../node_modules/pxt-core/built/pxtsim.d.ts"/>
/// <reference path="../libs/microbit/dal.d.ts"/>
/// <reference path="./visuals/neopixel.ts"/>
namespace pxsim {
export const MICROBIT_DEF: BoardDefinition = {
visual: "microbit",
gpioPinBlocks: [
["P0"], ["P1"], ["P2"],
["P3"],
["P4", "P5", "P6", "P7"],
["P8", "P9", "P10", "P11", "P12"],
["P16"],
],
gpioPinMap: {
"P0": "P0",
"P1": "P1",
"P2": "P2",
"P3": "P3",
"P4": "P4",
"P5": "P5",
"P6": "P6",
"P7": "P7",
"P8": "P8",
"P9": "P9",
"P10": "P10",
"P11": "P11",
"P12": "P12",
"P13": "P13",
"P14": "P14",
"P15": "P15",
"P16": "P16",
"P19": "P19",
"P20": "P20",
},
spiPins: {
MOSI: "P15",
MISO: "P14",
SCK: "P13",
},
i2cPins: {
SDA: "P20",
SCL: "P19",
},
analogInPins: ["P0", "P1", "P2", "P3", "P10"],
groundPins: ["GND"],
threeVoltPins: ["+3v3"],
attachPowerOnRight: true,
onboardComponents: ["buttonpair", "ledmatrix", "speaker"],
useCrocClips: true,
marginWhenBreadboarding: [0, 0, 80, 0],
}
export const builtinComponentSimVisual: Map<() => visuals.IBoardPart<any>> = {
"buttonpair": () => new visuals.ButtonPairView(),
"ledmatrix": () => new visuals.LedMatrixView(),
"neopixel": () => new visuals.NeoPixelView(),
};
export const builtinComponentSimState: Map<(d: DalBoard) => any> = {
"buttonpair": (d: DalBoard) => d.buttonPairState,
"ledmatrix": (d: DalBoard) => d.ledMatrixState,
"edgeconnector": (d: DalBoard) => d.edgeConnectorState,
"serial": (d: DalBoard) => d.serialState,
"radio": (d: DalBoard) => d.radioState,
"thermometer": (d: DalBoard) => d.thermometerState,
"accelerometer": (d: DalBoard) => d.accelerometerState,
"compass": (d: DalBoard) => d.compassState,
"lightsensor": (d: DalBoard) => d.lightSensorState,
"neopixel": (d: DalBoard) => d.neopixelState,
};
export const builtinComponentPartVisual: Map<(xy: visuals.Coord) => visuals.SVGElAndSize> = {
"buttonpair": (xy: visuals.Coord) => visuals.mkBtnSvg(xy),
"ledmatrix": (xy: visuals.Coord) => visuals.mkLedMatrixSvg(xy, 8, 8),
"neopixel": (xy: visuals.Coord) => visuals.mkNeoPixelPart(xy),
};
//TODO: add multiple board support
export const CURRENT_BOARD = MICROBIT_DEF;
}

View File

@ -282,13 +282,14 @@ namespace pxsim.instructions {
return div;
}
function mkCmpDiv(cmp: "wire" | PartVisualDefinition, opts: mkCmpDivOpts): HTMLElement {
let state = runtime.board as pxsim.CoreBoard;
let el: visuals.SVGElAndSize;
if (cmp == "wire") {
el = visuals.mkWirePart([0, 0], opts.wireClr || "red", opts.crocClips);
} else {
let partVis = <PartVisualDefinition>cmp;
if (typeof partVis.builtIn == "string") {
let cnstr = builtinComponentPartVisual[partVis.builtIn];
let cnstr = state.builtinPartVisuals[partVis.builtIn];
el = cnstr([0, 0]);
} else {
el = visuals.mkGenericPartSVG(partVis);
@ -353,8 +354,8 @@ namespace pxsim.instructions {
};
}
function mkBlankBoardAndBreadboard(boardDef: BoardDefinition, cmpDefs: Map<PartDefinition>, fnArgs: any, width: number, buildMode: boolean = false): visuals.BoardHost {
let state = runtime.board as pxsim.DalBoard;
let boardHost = new visuals.BoardHost({
const state = runtime.board as pxsim.CoreBoard;
const opts : visuals.BoardHostOpts = {
state: state,
boardDef: boardDef,
forceBreadboard: true,
@ -362,7 +363,8 @@ namespace pxsim.instructions {
maxWidth: `${width}px`,
fnArgs: fnArgs,
wireframe: buildMode,
});
};
let boardHost = new visuals.BoardHost(pxsim.visuals.mkBoardView(opts), opts);
let view = boardHost.getView();
svg.addClass(view, "board-svg");
@ -613,6 +615,9 @@ ${tsPackage}
});
}
// board def
const boardDef = JSON.parse(getQsVal("board")) as pxsim.BoardDefinition;
//parts list
let parts = (getQsVal("parts") || "").split(" ");
parts.sort();
@ -636,7 +641,6 @@ ${tsPackage}
style.textContent += STYLE;
const boardDef = CURRENT_BOARD;
const cmpDefs = partDefinitions;
//props

View File

@ -24,64 +24,6 @@ namespace pxsim {
export function getPin(id: number) {
return board().edgeConnectorState.getPin(id);
}
export enum PinFlags {
Unused = 0,
Digital = 0x0001,
Analog = 0x0002,
Input = 0x0004,
Output = 0x0008,
Touch = 0x0010
}
export class Pin {
constructor(public id: number) { }
touched = false;
value = 0;
period = 0;
mode = PinFlags.Unused;
pitch = false;
pull = 0; // PullDown
isTouched(): boolean {
this.mode = PinFlags.Touch;
return this.touched;
}
}
export class EdgeConnectorState {
pins: Pin[];
constructor() {
this.pins = [
new Pin(DAL.MICROBIT_ID_IO_P0),
new Pin(DAL.MICROBIT_ID_IO_P1),
new Pin(DAL.MICROBIT_ID_IO_P2),
new Pin(DAL.MICROBIT_ID_IO_P3),
new Pin(DAL.MICROBIT_ID_IO_P4),
new Pin(DAL.MICROBIT_ID_IO_P5),
new Pin(DAL.MICROBIT_ID_IO_P6),
new Pin(DAL.MICROBIT_ID_IO_P7),
new Pin(DAL.MICROBIT_ID_IO_P8),
new Pin(DAL.MICROBIT_ID_IO_P9),
new Pin(DAL.MICROBIT_ID_IO_P10),
new Pin(DAL.MICROBIT_ID_IO_P11),
new Pin(DAL.MICROBIT_ID_IO_P12),
new Pin(DAL.MICROBIT_ID_IO_P13),
new Pin(DAL.MICROBIT_ID_IO_P14),
new Pin(DAL.MICROBIT_ID_IO_P15),
new Pin(DAL.MICROBIT_ID_IO_P16),
null,
null,
new Pin(DAL.MICROBIT_ID_IO_P19),
new Pin(DAL.MICROBIT_ID_IO_P20)
];
}
public getPin(id: number) {
return this.pins.filter(p => p && p.id == id)[0] || null
}
}
}
namespace pxsim.pins {

View File

@ -271,6 +271,7 @@ namespace pxsim.led {
export function stopAnimation(): void {
board().ledMatrixState.animationQ.cancelAll();
board().ledMatrixState.image.clear();
}
export function setDisplayMode(mode: DisplayMode): void {

View File

@ -4,54 +4,10 @@ namespace pxsim {
if (b) {
let np = b.neopixelState;
if (np) {
np.updateBuffer(buffer, pin);
let buf = <Uint8Array[]>(<any>buffer).data;
np.updateBuffer(buf, pin);
runtime.queueDisplayUpdate();
}
}
}
}
namespace pxsim {
export enum NeoPixelMode {RGB, RGBW};
export type RGBW = [number, number, number, number];
function readNeoPixelBuffer(inBuffer: Uint8Array[], outColors: RGBW[], mode: NeoPixelMode) {
let buf = inBuffer;
let stride = mode === NeoPixelMode.RGBW ? 4 : 3;
let pixelCount = Math.floor(buf.length / stride);
for (let i = 0; i < pixelCount; i++) {
// NOTE: for whatever reason, NeoPixels pack GRB not RGB
let r = buf[i * stride + 1] as any as number
let g = buf[i * stride + 0] as any as number
let b = buf[i * stride + 2] as any as number
let w = 0;
if (stride === 4)
w = buf[i * stride + 3] as any as number
outColors[i] = [r, g, b, w]
}
}
export class NeoPixelState {
private buffers: {[pin: number]: Uint8Array[]} = {};
private colors: {[pin: number]: RGBW[]} = {};
private dirty: {[pin: number]: boolean} = {};
public updateBuffer(buffer: Buffer, pin: DigitalPin) {
//update buffers
let buf = <Uint8Array[]>(<any>buffer).data;
this.buffers[pin] = buf;
this.dirty[pin] = true;
}
public getColors(pin: number, mode: NeoPixelMode): RGBW[] {
let outColors = this.colors[pin] || (this.colors[pin] = []);
if (this.dirty[pin]) {
let buf = this.buffers[pin] || (this.buffers[pin] = []);
readNeoPixelBuffer(buf, outColors, mode);
this.dirty[pin] = false;
}
return outColors;
}
}
}

View File

@ -1,214 +0,0 @@
namespace pxsim.visuals {
export interface BoardHostOpts {
state: DalBoard,
boardDef: BoardDefinition,
partsList?: string[],
partDefs: Map<PartDefinition>,
fnArgs: any,
forceBreadboard?: boolean,
maxWidth?: string,
maxHeight?: string
wireframe?: boolean
}
export class BoardHost {
private parts: IBoardPart<any>[] = [];
private wireFactory: WireFactory;
private breadboard: Breadboard;
private fromBBCoord: (xy: Coord) => Coord;
private fromMBCoord: (xy: Coord) => Coord;
private boardView: BoardView;
private view: SVGSVGElement;
private partGroup: SVGGElement;
private partOverGroup: SVGGElement;
private style: SVGStyleElement;
private defs: SVGDefsElement;
private state: DalBoard;
private useCrocClips: boolean;
constructor(opts: BoardHostOpts) {
this.state = opts.state;
let onboardCmps = opts.boardDef.onboardComponents || [];
let activeComponents = (opts.partsList || []).filter(c => onboardCmps.indexOf(c) < 0);
activeComponents.sort();
this.useCrocClips = opts.boardDef.useCrocClips;
if (opts.boardDef.visual === "microbit") {
this.boardView = new visuals.MicrobitBoardSvg({
runtime: runtime,
theme: visuals.randomTheme(),
disableTilt: false,
wireframe: opts.wireframe,
});
} else {
let boardVis = opts.boardDef.visual as BoardImageDefinition;
this.boardView = new visuals.GenericBoardSvg({
visualDef: boardVis,
wireframe: opts.wireframe,
});
}
let useBreadboard = 0 < activeComponents.length || opts.forceBreadboard;
if (useBreadboard) {
this.breadboard = new Breadboard({
wireframe: opts.wireframe,
});
let bMarg = opts.boardDef.marginWhenBreadboarding || [0, 0, 40, 0];
let composition = composeSVG({
el1: this.boardView.getView(),
scaleUnit1: this.boardView.getPinDist(),
el2: this.breadboard.getSVGAndSize(),
scaleUnit2: this.breadboard.getPinDist(),
margin: [bMarg[0], bMarg[1], 20, bMarg[3]],
middleMargin: bMarg[2],
maxWidth: opts.maxWidth,
maxHeight: opts.maxHeight,
});
let under = composition.under;
let over = composition.over;
this.view = composition.host;
let edges = composition.edges;
this.fromMBCoord = composition.toHostCoord1;
this.fromBBCoord = composition.toHostCoord2;
let pinDist = composition.scaleUnit;
this.partGroup = over;
this.partOverGroup = <SVGGElement>svg.child(this.view, "g");
this.style = <SVGStyleElement>svg.child(this.view, "style", {});
this.defs = <SVGDefsElement>svg.child(this.view, "defs", {});
this.wireFactory = new WireFactory(under, over, edges, this.style, this.getLocCoord.bind(this));
let allocRes = allocateDefinitions({
boardDef: opts.boardDef,
partDefs: opts.partDefs,
fnArgs: opts.fnArgs,
getBBCoord: this.breadboard.getCoord.bind(this.breadboard),
partsList: activeComponents,
});
this.addAll(allocRes);
} else {
let el = this.boardView.getView().el;
this.view = el;
this.partGroup = <SVGGElement>svg.child(this.view, "g");
this.partOverGroup = <SVGGElement>svg.child(this.view, "g");
if (opts.maxWidth)
svg.hydrate(this.view, { width: opts.maxWidth });
if (opts.maxHeight)
svg.hydrate(this.view, { height: opts.maxHeight });
}
this.state.updateSubscribers.push(() => this.updateState());
}
public highlightBoardPin(pinNm: string) {
this.boardView.highlightPin(pinNm);
}
public highlightBreadboardPin(rowCol: BBLoc) {
this.breadboard.highlightLoc(rowCol);
}
public highlightWire(wire: Wire) {
//TODO: move to wiring.ts
//underboard wires
wire.wires.forEach(e => {
svg.addClass(e, "highlight");
(<any>e).style["visibility"] = "visible";
});
//un greyed out
svg.addClass(wire.endG, "highlight");
}
public getView(): SVGElement {
return this.view;
}
private updateState() {
this.parts.forEach(c => c.updateState());
}
private getBBCoord(rowCol: BBLoc) {
let bbCoord = this.breadboard.getCoord(rowCol);
return this.fromBBCoord(bbCoord);
}
private getPinCoord(pin: string) {
let boardCoord = this.boardView.getCoord(pin);
return this.fromMBCoord(boardCoord);
}
public getLocCoord(loc: Loc): Coord {
let coord: Coord;
if (loc.type === "breadboard") {
let rowCol = (<BBLoc>loc);
coord = this.getBBCoord(rowCol);
} else {
let pinNm = (<BoardLoc>loc).pin;
coord = this.getPinCoord(pinNm);
}
if (!coord) {
console.error("Unknown location: " + name)
return [0, 0];
}
return coord;
}
public addPart(partInst: PartInst): IBoardPart<any> {
let part: IBoardPart<any> = null;
let colOffset = 0;
if (partInst.simulationBehavior) {
//TODO: seperate simulation behavior from builtin visual
let builtinBehavior = partInst.simulationBehavior;
let cnstr = builtinComponentSimVisual[builtinBehavior];
let stateFn = builtinComponentSimState[builtinBehavior];
part = cnstr();
part.init(this.state.bus, stateFn(this.state), this.view, partInst.params);
} else {
let vis = partInst.visual as PartVisualDefinition;
part = new GenericPart(vis);
}
this.parts.push(part);
this.partGroup.appendChild(part.element);
if (part.overElement)
this.partOverGroup.appendChild(part.overElement);
if (part.defs)
part.defs.forEach(d => this.defs.appendChild(d));
this.style.textContent += part.style || "";
let colIdx = partInst.startColumnIdx;
let rowIdx = partInst.startRowIdx;
let row = getRowName(rowIdx);
let col = getColumnName(colIdx);
let xOffset = partInst.bbFit.xOffset / partInst.visual.pinDistance;
let yOffset = partInst.bbFit.yOffset / partInst.visual.pinDistance;
let rowCol = <BBLoc>{
type: "breadboard",
row: row,
col: col,
xOffset: xOffset,
yOffset: yOffset
};
let coord = this.getBBCoord(rowCol);
part.moveToCoord(coord);
let getCmpClass = (type: string) => `sim-${type}-cmp`;
let cls = getCmpClass(partInst.name);
svg.addClass(part.element, cls);
svg.addClass(part.element, "sim-cmp");
part.updateTheme();
part.updateState();
return part;
}
public addWire(inst: WireInst): Wire {
return this.wireFactory.addWire(inst.start, inst.end, inst.color, this.useCrocClips);
}
public addAll(allocRes: AllocatorResult) {
allocRes.partsAndWires.forEach(pAndWs => {
let part = pAndWs.part;
if (part)
this.addPart(part)
let wires = pAndWs.wires;
if (wires)
wires.forEach(w => this.addWire(w));
})
}
}
}

19
sim/visuals/boardview.ts Normal file
View File

@ -0,0 +1,19 @@
namespace pxsim.visuals {
export function mkBoardView(opts: BoardHostOpts): BoardView {
if (opts.boardDef.visual === "microbit") {
return new visuals.MicrobitBoardSvg({
runtime: runtime,
theme: visuals.randomTheme(),
disableTilt: false,
wireframe: opts.wireframe,
});
} else {
let boardVis = opts.boardDef.visual as BoardImageDefinition;
return new visuals.GenericBoardSvg({
visualDef: boardVis,
wireframe: opts.wireframe,
});
}
}
}

View File

@ -3,43 +3,6 @@
/// <reference path="../../libs/microbit/shims.d.ts"/>
/// <reference path="../../libs/microbit/enums.d.ts"/>
//TODO move to utils
namespace pxsim.visuals {
//expects rgb from 0,255, gives h in [0,360], s in [0, 100], l in [0, 100]
export function rgbToHsl(rgb: [number, number, number]): [number, number, number] {
let [r, g, b] = rgb;
let [r$, g$, b$] = [r / 255, g / 255, b / 255];
let cMin = Math.min(r$, g$, b$);
let cMax = Math.max(r$, g$, b$);
let cDelta = cMax - cMin;
let h: number, s: number, l: number;
let maxAndMin = cMax + cMin;
//lum
l = (maxAndMin / 2) * 100
if (cDelta === 0)
s = h = 0;
else {
//hue
if (cMax === r$)
h = 60 * (((g$ - b$) / cDelta) % 6);
else if (cMax === g$)
h = 60 * (((b$ - r$) / cDelta) + 2);
else if (cMax === b$)
h = 60 * (((r$ - g$) / cDelta) + 4);
//sat
if (l > 50)
s = 100 * (cDelta / (2 - maxAndMin));
else
s = 100 * (cDelta / maxAndMin);
}
return [Math.floor(h), Math.floor(s), Math.floor(l)];
}
}
namespace pxsim.visuals {
const PIXEL_SPACING = PIN_DIST * 3;
const PIXEL_RADIUS = PIN_DIST;
@ -115,7 +78,7 @@ namespace pxsim.visuals {
}
public setRgb(rgb: [number, number, number]) {
let hsl = rgbToHsl(rgb);
let hsl = visuals.rgbToHsl(rgb);
let [h, s, l] = hsl;
// at least 70% luminosity
l = Math.max(l, 60);